├── .gitattributes ├── .eslintignore ├── generators ├── app │ ├── templates │ │ ├── static │ │ │ └── .gitkeep │ │ ├── src │ │ │ ├── assets │ │ │ │ ├── img │ │ │ │ │ ├── .gitkeep │ │ │ │ │ ├── alert-bg@2x.png │ │ │ │ │ └── alert-bg@3x.png │ │ │ │ ├── js │ │ │ │ │ └── .gitkeep │ │ │ │ ├── css │ │ │ │ │ ├── vars.less │ │ │ │ │ ├── reset.css │ │ │ │ │ ├── common.css │ │ │ │ │ ├── mixins.less │ │ │ │ │ ├── animation.css │ │ │ │ │ └── normalize.css │ │ │ │ └── logo.png │ │ │ ├── mixins │ │ │ │ ├── .gitkeep │ │ │ │ └── toggle.js │ │ │ ├── store │ │ │ │ ├── actions.js │ │ │ │ ├── getters.js │ │ │ │ ├── mutations.js │ │ │ │ ├── types.js │ │ │ │ ├── modules │ │ │ │ │ └── list.js │ │ │ │ └── index.js │ │ │ ├── filters │ │ │ │ ├── toHyphenate.js │ │ │ │ └── toCameCase.js │ │ │ ├── directions │ │ │ │ └── focus.js │ │ │ ├── skeleton.entry.js │ │ │ ├── modules │ │ │ │ └── index │ │ │ │ │ ├── index.vue │ │ │ │ │ ├── index.html │ │ │ │ │ └── _index.js │ │ │ ├── eventBus │ │ │ │ └── index.js │ │ │ ├── config │ │ │ │ ├── apis.js │ │ │ │ ├── env.js │ │ │ │ └── axiosConfig.js │ │ │ ├── router │ │ │ │ └── index.js │ │ │ ├── components │ │ │ │ ├── global.js │ │ │ │ ├── skeleton.vue │ │ │ │ ├── Toast.vue │ │ │ │ ├── textCarousel.vue │ │ │ │ ├── vwDemo.vue │ │ │ │ ├── Loading.vue │ │ │ │ ├── Alert.vue │ │ │ │ └── Confirm.vue │ │ │ ├── utils │ │ │ │ ├── types.js │ │ │ │ ├── storage.js │ │ │ │ └── utils.js │ │ │ ├── plugins │ │ │ │ ├── confirm │ │ │ │ │ └── index.js │ │ │ │ ├── loading │ │ │ │ │ └── index.js │ │ │ │ ├── alert │ │ │ │ │ └── index.js │ │ │ │ └── toast │ │ │ │ │ └── index.js │ │ │ ├── _main.js │ │ │ ├── pages │ │ │ │ └── _HelloWorld.vue │ │ │ └── App.vue │ │ ├── .eslintignore │ │ ├── config │ │ │ ├── test.env.js │ │ │ ├── prod.env.js │ │ │ ├── dll.libs.dependencies.js │ │ │ ├── dev.env.js │ │ │ └── _index.js │ │ ├── build │ │ │ ├── logo.png │ │ │ ├── ommit-css-webpack-plugin.js │ │ │ ├── vue-loader.conf.js │ │ │ ├── webpack.skeleton.conf.js │ │ │ ├── build.js │ │ │ ├── check-versions.js │ │ │ ├── webpack.dll.conf.js │ │ │ ├── _utils.js │ │ │ ├── _webpack.dev.conf.js │ │ │ ├── _webpack.test.conf.js │ │ │ ├── _webpack.base.conf.js │ │ │ └── _webpack.prod.conf.js │ │ ├── .editorconfig │ │ ├── _gitignore │ │ ├── .babelrc │ │ ├── skeleton.js │ │ ├── jsdoc.conf.json │ │ ├── _index.html │ │ ├── .eslintrc.js │ │ ├── _postcssrc.js │ │ ├── CHANGELOG.md │ │ ├── _package.json │ │ ├── README.md │ │ └── dist │ │ │ └── skeleton.json │ └── index.js ├── mpage │ ├── templates │ │ └── src │ │ │ └── modules │ │ │ └── index │ │ │ ├── _index.vue │ │ │ ├── _index.html │ │ │ └── _index.js │ └── index.js ├── page │ ├── templates │ │ └── src │ │ │ ├── pages │ │ │ └── demo.vue │ │ │ └── router │ │ │ └── index.js │ └── index.js ├── publicPath │ ├── index.js │ └── templates │ │ └── config │ │ └── index.js └── imagePublicPath │ ├── index.js │ └── templates │ └── config │ └── index.js ├── screenshots ├── a.gif ├── a2.gif ├── a3.gif ├── a4.gif └── a5.gif ├── .travis.yml ├── .editorconfig ├── .yo-rc.json ├── .gitignore ├── __tests__ └── app.js ├── LICENSE ├── package.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | **/templates 3 | -------------------------------------------------------------------------------- /generators/app/templates/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/img/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/js/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/mixins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/actions.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/getters.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/types.js: -------------------------------------------------------------------------------- 1 | export const SET_INFO = 'SET_INFO' -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/vars.less: -------------------------------------------------------------------------------- 1 | @white: #fff; 2 | @gray: #666; 3 | 4 | -------------------------------------------------------------------------------- /screenshots/a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/screenshots/a.gif -------------------------------------------------------------------------------- /generators/app/templates/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | node_modules/* 3 | /config/ 4 | /dist/ 5 | /*.js 6 | -------------------------------------------------------------------------------- /screenshots/a2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/screenshots/a2.gif -------------------------------------------------------------------------------- /screenshots/a3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/screenshots/a3.gif -------------------------------------------------------------------------------- /screenshots/a4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/screenshots/a4.gif -------------------------------------------------------------------------------- /screenshots/a5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/screenshots/a5.gif -------------------------------------------------------------------------------- /generators/app/templates/config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"test"' 4 | } 5 | -------------------------------------------------------------------------------- /generators/app/templates/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /generators/app/templates/src/filters/toHyphenate.js: -------------------------------------------------------------------------------- 1 | export const hyphenate = val => val.replace(/([A-Z])/g, "-$1").toLowerCase() 2 | -------------------------------------------------------------------------------- /generators/app/templates/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/generators/app/templates/build/logo.png -------------------------------------------------------------------------------- /generators/app/templates/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/generators/app/templates/src/assets/logo.png -------------------------------------------------------------------------------- /generators/app/templates/src/directions/focus.js: -------------------------------------------------------------------------------- 1 | export const focus = { 2 | inserted: function(el) { 3 | el.focus(); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v11 4 | - v10 5 | - v8 6 | - v6 7 | - v4 8 | after_script: cat ./coverage/lcov.info | coveralls 9 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/img/alert-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/generators/app/templates/src/assets/img/alert-bg@2x.png -------------------------------------------------------------------------------- /generators/app/templates/src/assets/img/alert-bg@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/501981732/generator-easy-vue/HEAD/generators/app/templates/src/assets/img/alert-bg@3x.png -------------------------------------------------------------------------------- /generators/app/templates/config/dll.libs.dependencies.js: -------------------------------------------------------------------------------- 1 | // 工程组件,用于提前编译 dll目录 2 | const lib = { 3 | dll_vendor: ['vue/dist/vue.esm.js', 'vue-router'] 4 | 5 | } 6 | 7 | module.exports = lib; 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-node": { 3 | "promptValues": { 4 | "authorName": "wangmeng", 5 | "authorEmail": "501981732@qq.com", 6 | "authorUrl": "https://github.com/501981732" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /generators/app/templates/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /generators/app/templates/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /generators/app/templates/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /generators/app/templates/src/mixins/toggle.js: -------------------------------------------------------------------------------- 1 | export const toggle = { 2 | data() { 3 | return { 4 | show: false 5 | } 6 | }, 7 | methods: { 8 | toggleShow() { 9 | this.show = !this.show 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/src/skeleton.entry.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import skeleton from './components/skeleton.vue' 3 | 4 | console.log('Skeleton...') 5 | 6 | export default new Vue({ 7 | components: { 8 | skeleton 9 | }, 10 | template: '' 11 | }) 12 | -------------------------------------------------------------------------------- /generators/app/templates/src/modules/index/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | -------------------------------------------------------------------------------- /generators/mpage/templates/src/modules/index/_index.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | -------------------------------------------------------------------------------- /generators/app/templates/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | }, 8 | "useBuiltIns": true, 9 | }], 10 | "stage-2" 11 | ], 12 | "plugins": ["transform-vue-jsx", "transform-runtime"] 13 | } 14 | -------------------------------------------------------------------------------- /generators/page/templates/src/pages/demo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /generators/app/templates/src/eventBus/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | const EventBus = new Vue(); 3 | /** 4 | * 事件总线 5 | * @example 6 | * this.$bus.$emit("someEvent", someData) 7 | * this.$bus.$on("someEvent", (v) => {console.log(v)}) 8 | * 9 | */ 10 | Object.defineProperties(Vue.prototype, { 11 | $bus: { 12 | get: function () { 13 | return EventBus 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /__tests__/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const assert = require('yeoman-assert'); 4 | const helpers = require('yeoman-test'); 5 | 6 | describe('generator-easy-vue:app', () => { 7 | beforeAll(() => { 8 | return helpers 9 | .run(path.join(__dirname, '../generators/app')) 10 | .withPrompts({ someAnswer: true }); 11 | }); 12 | 13 | it('creates files', () => { 14 | assert.file(['dummyfile.txt']); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/modules/list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 分模块处理vuex 3 | */ 4 | import * as types from '../types' 5 | 6 | const state = { 7 | info: {} 8 | } 9 | 10 | const actions = { 11 | 12 | } 13 | 14 | const getters = { 15 | info: state => state.info, 16 | } 17 | 18 | const mutations = { 19 | [types.SET_INFO](state, payload) { 20 | state.info = payload 21 | }, 22 | } 23 | 24 | export default { 25 | state, 26 | actions, 27 | getters, 28 | mutations 29 | } 30 | -------------------------------------------------------------------------------- /generators/app/templates/src/filters/toCameCase.js: -------------------------------------------------------------------------------- 1 | export const cameCase = val => { 2 | let reg = /-(\w)/g; 3 | return reg.replace(o, function(a, b) { 4 | // b为子项; 5 | return b.toUpperCase() 6 | }) 7 | } 8 | 9 | export const caseCase2 = val => { 10 | 11 | let arr = str.split("-"); 12 | for (let i = 1, len = arr.length; i < len; i++) { 13 | arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1) 14 | } 15 | return arr.join('') 16 | } 17 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import list from './modules/list' 4 | import createLogger from 'vuex/dist/logger' 5 | import * as actions from './actions.js' 6 | import * as mutations from './mutations.js' 7 | 8 | Vue.use(Vuex) 9 | 10 | const debug = process.env.NODE_ENV !== 'production' 11 | 12 | export default new Vuex.Store({ 13 | modules: { 14 | list, 15 | }, 16 | actions, 17 | mutations, 18 | strict: debug, 19 | plugins: debug ? [createLogger()] : [] 20 | }) -------------------------------------------------------------------------------- /generators/app/templates/build/ommit-css-webpack-plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = class OmmitCSSPlugin { 2 | constructor() {} 3 | 4 | apply(compiler) { 5 | compiler.plugin('compilation', (compilation) => { 6 | compilation.plugin( 7 | 'html-webpack-plugin-alter-asset-tags', 8 | (args, cb) => { 9 | args.head = args.head.filter((link) => link.attributes.rel !== 'stylesheet'); 10 | cb(null, args); 11 | } 12 | ); 13 | }); 14 | } 15 | } -------------------------------------------------------------------------------- /generators/app/templates/src/config/apis.js: -------------------------------------------------------------------------------- 1 | import ax from './axiosConfig.js' 2 | 3 | let url = {}, 4 | postUrls = {}; 5 | 6 | if (process.env.NODE_ENV === "development") { 7 | url = { 8 | filter: "", 9 | }; 10 | } else if (process.env.NODE_ENV === "test") { 11 | url = { 12 | filter: "", 13 | }; 14 | } else if (process.env.NODE_ENV === "production") { 15 | url = { 16 | filter: "", 17 | }; 18 | } 19 | 20 | const filter = (data) => ax.get(url.filter,{params:data}) 21 | 22 | export default { 23 | filter, 24 | } 25 | -------------------------------------------------------------------------------- /generators/mpage/templates/src/modules/index/_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= projectName %><% if(dns) {%> 8 | 9 | <% for(var i of dns.split(',')){ %><% }%><%}%> 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generators/app/templates/skeleton.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const htmlMinifier = require('html-minifier') 3 | const { resolve } = require('path') 4 | 5 | const createBundleRenderer = require('vue-server-renderer').createBundleRenderer 6 | 7 | const renderer = createBundleRenderer(resolve(__dirname, './dist/skeleton.json'), { 8 | template: fs.readFileSync(resolve(__dirname, './index.html'), 'utf-8') 9 | }) 10 | 11 | renderer.renderToString({}, (err, html) => { 12 | html = htmlMinifier.minify(html, { 13 | collapseWhitespace: true, 14 | minifyCSS: true 15 | }) 16 | fs.writeFileSync('index.html', html, 'utf-8') 17 | }) -------------------------------------------------------------------------------- /generators/page/templates/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import { routerBaseUrl } from '@/config/env.js' 4 | import HelloWorld from '@/pages/HelloWorld' 5 | // const HelloWorld = () => import('@/pages/HelloWorld') 6 | 7 | const <%= page%> = () => import('@/pages/<%= page%>') 8 | 9 | Vue.use(Router) 10 | 11 | export default new Router({ 12 | mode: "hash", 13 | base: routerBaseUrl, 14 | routes: [ 15 | { 16 | path: '/', 17 | name: 'HelloWorld', 18 | component: HelloWorld 19 | },{ 20 | path: '/<%= page%>', 21 | name: '<%= page%>', 22 | component: <%= page%> 23 | } 24 | ] 25 | }) 26 | -------------------------------------------------------------------------------- /generators/app/templates/src/config/env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 配置线上环境和线下环境切换 3 | * baseUrl 域名地址 4 | * routerBaseUrl 若开启history模式则线上补全路径 5 | * @example url='//zp.58.com/yy/2018/employerbrandselection/main' baseUrl='//zp.58.com' routerBaseUrl='/yy/2018/employerbrandselection/main' 6 | * 7 | */ 8 | 9 | let baseUrl = ""; 10 | let routerBaseUrl = ""; 11 | if (process.env.NODE_ENV == "development") { 12 | // baseUrl = '' 13 | } else if (process.env.NODE_ENV == "test") { 14 | // 测试环境->代理proxy 15 | // baseUrl = ""; 16 | // routerBaseUrl = ""; 17 | } else if (process.env.NODE_ENV == "production") { 18 | baseUrl = ""; 19 | routerBaseUrl = ""; 20 | } 21 | export { baseUrl, routerBaseUrl }; 22 | -------------------------------------------------------------------------------- /generators/app/templates/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | // extract: true 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: ['src', 'poster'], 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /generators/app/templates/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import { routerBaseUrl } from '@/config/env.js' 4 | import HelloWorld from '@/pages/HelloWorld' 5 | // chunkname 6 | // const index = () => import( /* webpackChunkName: "index" */ '@/pages/index') 7 | 8 | Vue.use(Router) 9 | 10 | export default new Router({ 11 | mode: "hash", 12 | base: routerBaseUrl, 13 | scrollBehavior(to, from, savedPosition) { 14 | if (savedPosition) { 15 | return savedPosition 16 | } else { 17 | return { x: 0, y: 0 } 18 | } 19 | }, 20 | routes: [{ 21 | path: '/', 22 | name: 'HelloWorld', 23 | component: HelloWorld, 24 | meta: { 25 | zIndex: 1 26 | } 27 | }] 28 | }) 29 | -------------------------------------------------------------------------------- /generators/mpage/templates/src/modules/index/_index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import <%=mpage%> from './<%=mpage%>.vue' 3 | import router from '@/router' 4 | import axios from 'axios' 5 | // import store from '@/store/' 6 | // import FastClick from 'fastclick' 7 | 8 | Vue.config.productionTip = false 9 | 10 | import 'babel-polyfill' // API垫片 11 | 12 | // import '@/assets/css/reset.css' 13 | // import '@/assets/css/normalize.css' 14 | 15 | // import 'amfe-flexible/index.js' 16 | // FastClick.attach(document.body); 17 | 18 | // vue插件 19 | import AlertPlugin from '@/plugins/alert/index.js' 20 | Vue.use(AlertPlugin) 21 | 22 | // 自动注入components 23 | import '@/components/global.js' 24 | 25 | window.axios = axios 26 | 27 | /* eslint-disable no-new */ 28 | new Vue({ 29 | el: '#app', 30 | components: { <%=mpage%> }, 31 | template: '<<%=mpage%>/>' 32 | }) 33 | 34 | -------------------------------------------------------------------------------- /generators/publicPath/index.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const yosay = require('yosay'); 3 | const Generator = require('yeoman-generator'); 4 | 5 | module.exports = class extends Generator { 6 | // The name `constructor` is important here 7 | constructor(args, opts) { 8 | // Calling the super constructor is important so our generator is correctly set up 9 | super(args, opts); 10 | } 11 | initializing() { 12 | this.argument('publicPath', { type: String, required: true }); 13 | } 14 | prompting() {} 15 | 16 | writing() { 17 | this.fs.copyTpl( 18 | this.templatePath('config/index.js'), 19 | this.destinationPath('config/index.js'), 20 | { publicPath: this.options.publicPath } 21 | ); 22 | } 23 | install() {} 24 | 25 | end() { 26 | this.log(yosay(`Now the ${chalk.green(this.options.publicPath)} is ready!`)) 27 | } 28 | }; -------------------------------------------------------------------------------- /generators/app/templates/src/components/global.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 自动注入组件 4 | * require.context() 5 | * useage: import './components/global.js' 6 | * 引入组件时不再需要 import xx from 'xx' components:{xx} 7 | */ 8 | 9 | import Vue from 'vue' 10 | 11 | function capitalizeFirstLetter(string) { 12 | return string.charAt(0).toUpperCase() + string.slice(1) 13 | } 14 | 15 | const requireComponent = require.context( 16 | '.', true, /.vue$/ 17 | //找到components文件夹下以.vue命名的文件 18 | 19 | ) 20 | 21 | requireComponent.keys().forEach(fileName => { 22 | const componentConfig = requireComponent(fileName) 23 | const componentName = capitalizeFirstLetter( 24 | fileName.replace( /(.*)(\/)/, '').replace(/.\w+$/, '') 25 | //因为得到的filename格式是: './xxx.vue' './xxx/xxx.vue', 所以这里我们去掉头和尾,只保留真正的文件名 26 | ) 27 | 28 | Vue.component(componentName, componentConfig.default || componentConfig) 29 | 30 | }) -------------------------------------------------------------------------------- /generators/imagePublicPath/index.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const yosay = require('yosay'); 3 | const Generator = require('yeoman-generator'); 4 | 5 | module.exports = class extends Generator { 6 | // The name `constructor` is important here 7 | constructor(args, opts) { 8 | // Calling the super constructor is important so our generator is correctly set up 9 | super(args, opts); 10 | } 11 | initializing() { 12 | this.argument('imagePublicPath', { type: String, required: true }); 13 | } 14 | prompting() {} 15 | 16 | writing() { 17 | this.fs.copyTpl( 18 | this.templatePath('config/index.js'), 19 | this.destinationPath('config/index.js'), 20 | { imagePublicPath: this.options.imagePublicPath } 21 | ); 22 | } 23 | install() {} 24 | 25 | end() { 26 | this.log(yosay(`Now the ${chalk.green(this.options.imagePublicPath)} is ready!`)) 27 | } 28 | }; -------------------------------------------------------------------------------- /generators/app/templates/jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "source": { 6 | "include": ["./src/"], 7 | "exclude": [], 8 | "includePattern": "\\.(vue|js)$", 9 | "excludePattern": "(^|\\/|\\\\)_" 10 | }, 11 | "plugins": [ 12 | "node_modules/jsdoc-vue" 13 | // "plugins/markdown", 14 | // "plugins/summarize" 15 | ], 16 | "markdown": { 17 | 18 | }, 19 | "templates": { 20 | "cleverLinks": false, 21 | "monospaceLinks": false, 22 | "default": { 23 | "outputSourceFiles": true 24 | } 25 | }, 26 | "opts": { 27 | "template": "templates/default", 28 | "encoding": "utf8", 29 | "destination": "./jsdoc/", 30 | "recurse": true, 31 | "package": "./package.json", 32 | "reademe": "./READEME.md" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /generators/app/templates/_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= projectName %><% if(dns) {%> 11 | 12 | <% for(var i of dns.split(',')){ %><% }%><%}%><% if(dns) {%> 13 | <% for(var i of dns.split(',')){ %><% }%><%}%> 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /generators/app/templates/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | // 'standard' // 如果想用官方推荐的standard 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /generators/app/templates/src/modules/index/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= projectName %><% if(dns) {%> 8 | 9 | <% for(var i of dns.split(',')){ %><% }%><%}%><% if(dns) {%> 10 | <% for(var i of dns.split(',')){ %><% }%><%}%> 11 | 12 | 13 | 14 |
15 | 16 |
17 | <% if(layout === 'vw'){ %> 18 | 19 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /generators/page/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const chalk = require('chalk'); 3 | const yosay = require('yosay'); 4 | const Generator = require('yeoman-generator'); 5 | 6 | module.exports = class extends Generator { 7 | // The name `constructor` is important here 8 | constructor(args, opts) { 9 | // Calling the super constructor is important so our generator is correctly set up 10 | super(args, opts); 11 | } 12 | initializing() { 13 | this.argument('page', { type: String, required: true }); 14 | } 15 | prompting() {} 16 | 17 | writing() { 18 | this.fs.copyTpl( 19 | this.templatePath('src/pages/demo.vue'), 20 | this.destinationPath('src/pages/' + this.options.page + '.vue'), 21 | { page: this.options.page } 22 | ); 23 | this.fs.copyTpl( 24 | this.templatePath('src/router/index.js'), 25 | this.destinationPath('src/router/index.js'), 26 | { page: this.options.page } 27 | ); 28 | } 29 | install() {} 30 | 31 | end() { 32 | this.log(yosay(`Now the page ${chalk.green(this.options.page)} is ready!`)) 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /generators/app/templates/src/modules/index/_index.js: -------------------------------------------------------------------------------- 1 | 2 | import Vue from 'vue' 3 | import Index from './index.vue' 4 | import axios from 'axios' 5 | <% if(vuex) {%> 6 | import store from '@/store/' 7 | <% }%><% if(layout === 'rem' || layout === 'vw') {%> 8 | import FastClick from 'fastclick' 9 | <% }%> 10 | 11 | Vue.config.productionTip = false 12 | 13 | import 'babel-polyfill' // API垫片 14 | 15 | <% if(reset === 'reset.css') {%> 16 | import '@/assets/css/reset.css' 17 | <% } else if (reset === 'normalize.css') {%> 18 | import '@/assets/css/normalize.css' 19 | <% }%> 20 | <% if(layout === 'rem') {%> 21 | // REM布局方案 OR vw布局 22 | import 'amfe-flexible/index.js' 23 | FastClick.attach(document.body); 24 | <% } else if (layout === 'vw') {%> 25 | FastClick.attach(document.body); 26 | <% }%> 27 | // vue插件 28 | import AlertPlugin from '@/plugins/alert/index.js' 29 | Vue.use(AlertPlugin) 30 | 31 | // 自动注入components 32 | import '@/components/global.js' 33 | 34 | window.axios = axios 35 | 36 | /* eslint-disable no-new */ 37 | new Vue({ 38 | el: '#app',<% if(vuex) {%> 39 | store,<% }%> 40 | components: { Index }, 41 | template: '' 42 | }) 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 wangmeng <501981732@qq.com> (https://github.com/501981732) 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /generators/app/templates/src/config/axiosConfig.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import vue from 'vue' 3 | import { baseUrl } from './env.js' 4 | import qs from 'qs' 5 | import Toast from '@/plugins/toast/index.js' 6 | import Loading from '@/plugins/loading/index.js' 7 | vue.use(Toast) 8 | vue.use(Loading) 9 | 10 | const ax = axios.create({ 11 | baseURL: baseUrl, 12 | timeout: 10000, 13 | withCredentials: true // 允许携带cookie 14 | }) 15 | 16 | // 添加请求拦截器 17 | ax.interceptors.request.use(function(config) { 18 | if (config.method === 'post') { 19 | config.data = qs.stringify(config.data); 20 | } 21 | return config; 22 | }, function(error) { 23 | return Promise.reject(error); 24 | }); 25 | 26 | // 添加响应拦截器 27 | ax.interceptors.response.use(function(response) { 28 | if (process.env.NODE_ENV == 'development') { 29 | console.log(response) 30 | } 31 | return response; 32 | }, function(error) { 33 | if (error.message.includes('timeout')) { // 判断请求异常信息中是否含有超时timeout字符串 34 | console.log("错误回调", error) 35 | vue.$x.loading.hide() 36 | vue.$x.toast('请求超时请稍后再试') 37 | return Promise.reject(error); // reject这个错误信息 38 | } 39 | }); 40 | export default ax 41 | -------------------------------------------------------------------------------- /generators/app/templates/src/utils/types.js: -------------------------------------------------------------------------------- 1 | export const isArray = val => !!val && Array.isArray(val); 2 | 3 | export const isBoolean = val => typeof val === "boolean"; 4 | 5 | export const isFunction = val => val && typeof val === "function"; 6 | 7 | export const isNumber = val => typeof val === "number"; 8 | 9 | export const isString = val => typeof val === "string"; 10 | 11 | export const isSymbol = val => typeof val === "symbol"; 12 | 13 | export const inBrowser = typeof window !== "undefined"; 14 | 15 | export const UA = inBrowser && window.navigator.userAgent.toLowerCase(); 16 | 17 | export const isIE = UA && /msie|trident/.test(UA); 18 | 19 | export const isIE9 = UA && UA.indexOf("msie 9.0") > 0; 20 | 21 | export const isEdge = UA && UA.indexOf("edge/") > 0; 22 | 23 | export const isAndroid = UA && UA.indexOf("android") > 0; 24 | 25 | export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); 26 | 27 | export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; 28 | 29 | export const isPhantomJS = UA && /phantomjs/.test(UA); 30 | 31 | export const isInAPP = UA && /wuba/.test(UA); 32 | 33 | export const isWeiXin = UA && /micromessenger/.test(UA); 34 | 35 | export const is360zhushou = UA && /360appstore/.test(UA); 36 | -------------------------------------------------------------------------------- /generators/app/templates/build/webpack.skeleton.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const nodeExternals = require('webpack-node-externals') 4 | const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') 5 | // const VueLoaderPlugin = require('vue-loader/lib/plugin') 6 | 7 | module.exports = { 8 | target: 'node', 9 | entry: { 10 | skeleton: './src/skeleton.entry.js' 11 | }, 12 | output: { 13 | path: path.resolve(__dirname, '../dist'), 14 | publicPath: '/dist/', 15 | filename: '[name].js', 16 | libraryTarget: 'commonjs2' 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.css$/, 22 | use: [ 23 | 'vue-style-loader', 24 | 'css-loader' 25 | ] 26 | }, 27 | { 28 | test: /\.vue$/, 29 | loader: 'vue-loader' 30 | } 31 | ] 32 | }, 33 | externals: nodeExternals({ 34 | whitelist: /\.css$/ 35 | }), 36 | resolve: { 37 | alias: { 38 | 'vue$': 'vue/dist/vue.esm.js' 39 | }, 40 | extensions: ['*', '.js', '.vue', '.json'] 41 | }, 42 | plugins: [ 43 | // new VueLoaderPlugin(), 44 | new VueSSRServerPlugin({ 45 | filename: 'skeleton.json' 46 | }) 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /generators/app/templates/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/skeleton.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 64 | -------------------------------------------------------------------------------- /generators/app/templates/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /generators/mpage/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const chalk = require('chalk'); 3 | const yosay = require('yosay'); 4 | const Generator = require('yeoman-generator'); 5 | 6 | module.exports = class extends Generator { 7 | // The name `constructor` is important here 8 | constructor(args, opts) { 9 | // Calling the super constructor is important so our generator is correctly set up 10 | super(args, opts); 11 | } 12 | initializing() { 13 | this.argument('mpage', { 14 | type: String, 15 | required: true 16 | }); 17 | } 18 | prompting() {} 19 | 20 | writing() { 21 | let list = [ 22 | ['src/modules/index/_index.js', `src/modules/${this.options.mpage}/${this.options.mpage}.js`], 23 | ['src/modules/index/_index.vue', `src/modules/${this.options.mpage}/${this.options.mpage}.vue`], 24 | ['src/modules/index/_index.html', `src/modules/${this.options.mpage}/${this.options.mpage}.html`], 25 | ] 26 | list.forEach(item => { 27 | let fromFile = item[0]; 28 | let toFile = item[1]; 29 | this.fs.copyTpl( 30 | this.templatePath(fromFile), 31 | this.destinationPath(toFile), 32 | // 将配置参数带过去 33 | { 34 | mpage: this.options.mpage 35 | } 36 | ); 37 | }) 38 | } 39 | install() {} 40 | 41 | end() { 42 | this.log(yosay(`Now the ${chalk.green('mutil page')} is ready!`)) 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /generators/app/templates/_postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | <%if(layout === 'rem') {%> 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | // css后处理器 9 | "autoprefixer": {} 10 | }, 11 | // rem自动转换插件 12 | // 若想排除第三方库可以用基于postcss-px2rem 的postcss-px2rem-exclude or px2rem-exclude 13 | "postcss-pxtorem": { 14 | rootValue: 75, 15 | unitPrecision: 5, 16 | propList: ['*'], 17 | selectorBlackList: [], 18 | replace: true, 19 | mediaQuery: false, 20 | minPixelValue: 12 21 | } 22 | } 23 | <% }%><%if(layout === 'vw') {%> 24 | module.exports = { 25 | "plugins": { 26 | "postcss-import": {}, 27 | "postcss-url": {}, 28 | "postcss-aspect-ratio-mini": {}, // 处理元素容器宽高比 29 | "postcss-write-svg": { 30 | utf8: false 31 | }, 32 | "postcss-preset-env": { 33 | stage: 2, 34 | }, 35 | "postcss-px-to-viewport": { 36 | viewportWidth: 750, //视窗的宽度,对应的是我们设计稿的宽度,一般是750 37 | viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 38 | unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) 39 | viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw 40 | selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 41 | minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 42 | mediaQuery: false, // 允许在媒体查询中转换`px` 43 | exclude:/node_modules/i, //排除第三方库 44 | // selectorBlackList: ['delivery_alert_box']//黑名单 45 | }, 46 | // "cssnano": { //压缩和清理CSS代码 cssnano和css-loader捆绑在一起,所以不需要自己加载它 47 | // preset: "advanced", 48 | // autoprefixer: false, 49 | // "postcss-zindex": false 50 | // }, 51 | } 52 | } 53 | <% }%> 54 | -------------------------------------------------------------------------------- /generators/app/templates/src/plugins/confirm/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Confirm 插件 3 | * @example 4 | * this.$x.confirm({ 5 | * title:'提示1', 6 | * body: '1', 7 | * cancleText: '取消', 8 | * confirmText: '确定', 9 | * btnReverse: false, //按钮是否反正 10 | * useHTMLString: 'false' //是否使用动态HTML插入 11 | * }).then(res => { 12 | * console.log(res) //根据res判断是取消还是确定 13 | * }) 14 | */ 15 | import Vue from 'vue' 16 | import Confirm from '@/components/Confirm.vue' 17 | 18 | const defaultProps = { 19 | 20 | } 21 | 22 | const plugin = { 23 | install(vue, props = {}) { 24 | const ConfirmPlugin = vue.extend(Confirm) 25 | let $vm = new ConfirmPlugin({ 26 | el: document.createElement('div') 27 | }) 28 | document.body.appendChild($vm.$el) 29 | 30 | function confirm(options) { 31 | if ($vm.show) return 32 | 33 | if (typeof options === 'string') { 34 | options = { 35 | body: arguments[0] 36 | } 37 | } 38 | 39 | $vm = Object.assign($vm, defaultProps, props, options) 40 | 41 | $vm.show = true 42 | 43 | return new Promise((resolve, reject) => { 44 | $vm.$on('confirm',_ => { 45 | resolve(true) 46 | }) 47 | 48 | $vm.$on('cancle', _ => { 49 | resolve(false) 50 | }) 51 | 52 | }) 53 | } 54 | 55 | if( !vue.$x) { 56 | vue.$x = { 57 | confirm 58 | } 59 | } else { 60 | vue.$x.confirm = confirm 61 | } 62 | vue.mixin({ 63 | created: _ => { 64 | this.$x = vue.$x 65 | } 66 | }) 67 | } 68 | } 69 | export default plugin 70 | -------------------------------------------------------------------------------- /generators/app/templates/src/_main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | <% if(vuex) {%> 7 | import store from '@/store/' 8 | <% }%><% if(layout === 'rem' || layout === 'vw') {%> 9 | import FastClick from 'fastclick' 10 | <% }%> 11 | 12 | import api from "@/config/apis.js"; 13 | 14 | Vue.config.productionTip = false 15 | 16 | import 'babel-polyfill' // API垫片 17 | 18 | <% if(reset === 'reset.css') {%> 19 | import './assets/css/reset.css' 20 | <% } else if (reset === 'normalize.css') {%> 21 | import './assets/css/normalize.css' 22 | <% }%> 23 | import "./assets/css/animation.css"; 24 | import "./assets/css/common.css"; 25 | 26 | <% if(layout === 'rem') {%> 27 | // REM布局方案 OR vw布局 28 | import 'amfe-flexible/index.js' 29 | FastClick.attach(document.body); 30 | <% } else if (layout === 'vw') {%> 31 | FastClick.attach(document.body); 32 | <% }%> 33 | // 时间总线 34 | import '@/eventBus/index.js' 35 | /* eslint-disable no-unused-vars */ 36 | // import vConsole from "vconsole"; 37 | // const insvConsole = new vConsole(); 38 | 39 | // 自动注入components 40 | import './components/global.js' 41 | 42 | // vue插件 43 | import AlertPlugin from "./plugins/alert/index.js"; 44 | import ToastPlugin from "./plugins/toast/index.js"; 45 | import ConformPlugin from "./plugins/confirm/index.js"; 46 | import LoadingPlugin from "./plugins/loading/index.js"; 47 | 48 | Vue.use(AlertPlugin); 49 | Vue.use(ToastPlugin); 50 | Vue.use(ConformPlugin); 51 | Vue.use(LoadingPlugin); 52 | // mount api 53 | Vue.prototype.$api = api; 54 | 55 | 56 | /* eslint-disable no-new */ 57 | new Vue({ 58 | el: '#app', 59 | router,<% if(vuex) {%> 60 | store,<% }%> 61 | components: { App }, 62 | template: '' 63 | }) 64 | 65 | -------------------------------------------------------------------------------- /generators/app/templates/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | #### todo 4 | 5 | - 非常棒的preload prefetch and prepack 相关插件不支持webpack3.X 6 | 7 | #### 0.4.0 8 | 9 | - 净化css,css tree shake, config/index配置purgecssPath 10 | - postcss插件替换 postcss-preset-env替代cssnext 默认stage:2 11 | 12 | #### 0.3.9 13 | 14 | - 路由动画 15 | 16 | #### 0.3.3 17 | 18 | - 增加测试环境:开发环境接口 -> **mock**,测试环境走 ->**proxy**,生产环境直接打包线上全路径 19 | - 增加eventBus事件总线,挂载vue 全局调用 20 | - 修改loading插件样式 21 | - 请求增加超时提示,超时时间为10s 22 | - vw布局插件升级postcss-px-to-viewport,支持排除node_momdules插件包,rem布局暂不支持可换插件postcss-px2rem-exclude or px2rem-exclude 23 | 24 | #### 0.3.0 25 | 26 | - 按需加载打包 chunkname 27 | - axios 优化挂载方式 28 | - less 变量 mixins全局引入,优化app.css体积 style-resources-loader 29 | - 增加常用css方案 渐变兼容IE9 一像素边框 二倍图三倍图 iphoneX适配 等 30 | - 增加常用组件库 插件库 toast comfirm alert loading等 31 | - 增加常用工具库 32 | - vw布局优化 去掉postcss-viewport-units 和viewport-units-buggyfill 给vw、vh、vmin和vmax做适配的操作。 33 | - 填补cssnano 自动计算z-index的坑 34 | - 优化css打包过大问题 优化css结构 全局引入less变量 mixins等 **style-resources-loader** 35 | ``` 36 | vars.less //style-resources-loader 可在build/utils.js修改路径 37 | mixins.less //style-resources-loader 38 | common.css //index中import引入 39 | animation.css //index中import引入 40 | reset.css //index中import引入 41 | ``` 42 | #### 0.2.4 43 | 44 | - 开启动态链接库dll并自动插入到模板中新增插件 45 | - DllPlugin,DllReferencePlugin,html-webpack-include-assets-plugin,cross-env 46 | - 动态链接库修改在config/dll.lib.dependencies.js 默认只用了vue,vue-router 47 | #### 0.2.2 48 | 49 | - 0.2.2 开启可配置dns预解析 50 | 51 | #### 0.2.1 52 | 53 | - 增加代码检测工作流 54 | - prettier统一代码格式 55 | - precommit自动美化格式以及修复代码 56 | - lint-staged 多人写作避免代码冲突,渐进式lint代码 57 | - 如果想要启用官方推荐的standard 可在.eslintrc.js中开启 58 | 59 | 60 | #### 0.2.0 61 | 62 | - 增加jsdoc配置 自动生成说明文档 63 | - 增加对.vue文件的文档解析 jsdoc jsdoc-vue 64 | 65 | #### 0.1.8 66 | 67 | - 增加一键生成多页面应用 68 | 69 | #### 0.1.4 70 | 71 | - 增加多页面配置 72 | 73 | 74 | -------------------------------------------------------------------------------- /generators/app/templates/build/webpack.dll.conf.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const libs = require("../config/dll.libs.dependencies.js"); 4 | // const config = require("../config"); 5 | // const AssetsPlugin = require("assets-webpack-plugin"); 6 | 7 | const webpackConf = { 8 | entry: { 9 | ...libs 10 | }, 11 | 12 | output: { 13 | // 输出的文件都放到 dist 目录下 14 | path: path.join(__dirname, "../static/libs"), 15 | // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,也就是 entry 中配置的 vue 等 16 | filename: "dll.[name].js", 17 | // 存放动态链接库的全局变量名称,例如对应 vue 来说就是 _dll_vue 18 | // 之所以在前面加上 _dll_ 是为了防止全局变量冲突 19 | // publicPath: process.env.NODE_ENV === "production" ? config.build.assetsPublicPath : config.dev.assetsPublicPath, 20 | /** 21 | * output.library 22 | * 将会定义为 window.${output.library} 23 | * 在这次的例子中,将会定义为`window._dll_vue_library` 24 | */ 25 | library: "_dll_[name]" 26 | }, 27 | plugins: [ 28 | new webpack.DllPlugin({ 29 | // 动态链接库的全局变量名称,需要和 output.library 中保持一致 30 | // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值 31 | // 例如 vue.manifest.json 中就有 "name": "_dll_vue" 32 | path: path.join(__dirname, "../static/libs", "[name].manifest.json"), 33 | name: "_dll_[name]" 34 | }), 35 | new webpack.DefinePlugin({ 36 | 'process.env': { 37 | NODE_ENV: JSON.stringify(process.env.NODE_ENV) 38 | } 39 | }), 40 | 41 | // 插入到html中 42 | // new AssetsPlugin({ 43 | // filename: "bundle-config.json", 44 | // path: path.join(__dirname, '..', 'config') 45 | // }) 46 | 47 | ] 48 | }; 49 | // 生产环境下压缩 50 | if (process.env.NODE_ENV === 'production') { 51 | webpackConf.plugins.unshift(new webpack.optimize.UglifyJsPlugin()); 52 | } 53 | module.exports = webpackConf 54 | -------------------------------------------------------------------------------- /generators/app/templates/src/plugins/loading/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file loading 插件 3 | * @example 4 | * this.$x.loading.show('1') 5 | * this.$x.loading.show() 6 | * this.$x.loading.hide() 7 | */ 8 | import Vue from "vue"; 9 | import LoadingComponent from "@/components/Loading.vue"; 10 | // const plagin = { 11 | // install(Vue, options) { 12 | // Vue.component(loading.name, loading) // 组件的name属性 13 | // // 类似通过 this.$xxx 方式调用插件的 其实只是挂载到原型上而已 14 | // // Vue.prototype.$xxx // 最终可以在任何地方通过 this.$xxx 调用 15 | // // 虽然没有明确规定用$开头 但是大家都默认遵守这个规定 16 | // } 17 | // } 18 | // export default plagin // 导出.. 19 | 20 | export default { 21 | install(Vue, props) { 22 | const LoadingPlugin = Vue.extend(LoadingComponent); 23 | 24 | let $vm = new LoadingPlugin({ 25 | el: document.createElement("div") 26 | }); 27 | 28 | document.body.appendChild($vm.$el); 29 | 30 | // $vm.show = false; 31 | let loading = { 32 | show(text) { 33 | $vm.value = true; 34 | 35 | text && ($vm.text = text); 36 | }, 37 | hide() { 38 | $vm.value = false; 39 | } 40 | }; 41 | // const loading = { 42 | // show() { 43 | // if ($vm.show) return; 44 | // $vm.value = true; 45 | // }, 46 | // hide() { 47 | // $vm.value = false; 48 | // } 49 | // }; 50 | if (!Vue.$x) { 51 | Vue.$x = { 52 | loading 53 | }; 54 | } else { 55 | Vue.$x.loading = loading; 56 | } 57 | // if (!Vue.$loading) { 58 | // Vue.$loading = loading; 59 | // } 60 | // Vue.prototype.$loading = Vue.$loading; 61 | Vue.mixin({ 62 | created() { 63 | this.$x = Vue.$x; 64 | } 65 | }); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /generators/app/templates/src/pages/_HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 60 | 61 | 62 | 79 | -------------------------------------------------------------------------------- /generators/app/templates/src/plugins/alert/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file alert插件 3 | * @example 4 | *this.$x.alert({ 5 | * title: '你好', 6 | * subtitle: '我是副标题', 7 | * body:'呵呵', 8 | * delayed: true, 9 | * duration: 3000, 10 | * boxClass: '' 11 | * }) 12 | * .then(val => { 13 | * console.log('change'); 14 | * }); 15 | */ 16 | 17 | import Vue from 'vue'; 18 | import Alert from '@/components/Alert.vue'; 19 | 20 | const defaultProps = { 21 | delayed: true, 22 | duration: 3000 23 | } 24 | 25 | const plugin = { 26 | install(vue, props = {}) { 27 | const AlertPlugin = Vue.extend(Alert); 28 | 29 | let $vm = new AlertPlugin({ 30 | el: document.createElement('div') 31 | }); 32 | document.body.appendChild($vm.$el); 33 | 34 | function alert(options = {}) { 35 | if ($vm.show) return; 36 | 37 | 38 | // 如果传参为字符串,则直接显示body 的文本 39 | if (typeof options === 'string') { 40 | options = { 41 | body: arguments[0] 42 | }; 43 | } 44 | //配置优选级: 组件默认配置 < 插件默认配置 < 全局配置 < 实例配置 45 | $vm = Object.assign($vm, defaultProps, props, options); 46 | $vm.show = true; 47 | // 支持延时消失 默认支持 48 | if ($vm.delayed) { 49 | setTimeout(_ => { 50 | $vm.show = false 51 | }, $vm.duration) 52 | } 53 | return new Promise((resolve, reject) => { 54 | $vm.$watch('show', val => { 55 | resolve(); 56 | }); 57 | }); 58 | } 59 | 60 | if (!vue.$x) { 61 | vue.$x = { 62 | alert 63 | }; 64 | } else { 65 | vue.$x.alert = alert; 66 | } 67 | vue.mixin({ 68 | created: function() { 69 | this.$x = vue.$x; 70 | } 71 | }); 72 | } 73 | }; 74 | 75 | export default plugin; 76 | -------------------------------------------------------------------------------- /generators/app/templates/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 27 | 87 | -------------------------------------------------------------------------------- /generators/app/templates/src/plugins/toast/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file toast插件 3 | * @example 4 | * useage: 5 | * this.$x.toast('你好') 6 | * this.$x.toast({ 7 | * message: '你好', 8 | * position: 'top', 9 | * duration: 2000, 10 | * toastClass: '' 11 | * }) 12 | */ 13 | 14 | import Vue from "vue"; 15 | import Toast from "@/components/Toast"; 16 | 17 | const defaultProps = { 18 | delayed: true, 19 | duration: 2000 20 | }; 21 | const plugin = { 22 | install(vue, props = {}) { 23 | const ToastPlugin = Vue.extend(Toast); 24 | // toast实例 $vm 25 | let $vm = new ToastPlugin({ 26 | el: document.createElement("div") 27 | }); 28 | document.body.appendChild($vm.$el); 29 | 30 | function toast(options = {}) { 31 | // 如果当前显示 则return 32 | if ($vm.show) return; 33 | 34 | // 如果传参为字符串,则直接显示body 的文本 35 | if (typeof options === "string") { 36 | options = { 37 | message: arguments[0] 38 | }; 39 | } 40 | //配置优选级: 默认配置 < 全局配置 < 实例配置 41 | //$vm为默认配置 42 | //install时传入的props为全局设置 43 | //调取toast时传入的options为实例配置 44 | $vm = Object.assign($vm, defaultProps, props, options); 45 | $vm.show = true; 46 | // 支持延时消失 默认支持 47 | if ($vm.delayed) { 48 | setTimeout(_ => { 49 | $vm.show = false; 50 | }, $vm.duration); 51 | } 52 | return new Promise((resolve, reject) => { 53 | $vm.$watch("show", val => { 54 | resolve(); 55 | }); 56 | }); 57 | } 58 | 59 | if (!vue.$x) { 60 | vue.$x = { 61 | toast 62 | }; 63 | } else { 64 | vue.$x.toast = toast; 65 | } 66 | 67 | vue.mixin({ 68 | created: function() { 69 | this.$x = vue.$x; 70 | } 71 | }); 72 | } 73 | }; 74 | 75 | export default plugin; 76 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Toast.vue: -------------------------------------------------------------------------------- 1 | 8 | 62 | 90 | -------------------------------------------------------------------------------- /generators/app/templates/src/utils/storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 本地存储实现,封装localStorage和sessionStorage 3 | */ 4 | let store = { 5 | storage: window.localStorage, 6 | session: { 7 | storage: window.sessionStorage 8 | } 9 | } 10 | 11 | const api = { 12 | set(key, val) { 13 | if (this.disabled) { 14 | return 15 | } 16 | if (val === undefined) { 17 | return this.remove(key) 18 | } 19 | this.storage.setItem(key, serialize(val)) 20 | return val 21 | }, 22 | 23 | get(key, def) { 24 | if (this.disabled) { 25 | return def 26 | } 27 | let val = deserialize(this.storage.getItem(key)) 28 | return (val === undefined ? def : val) 29 | }, 30 | 31 | has(key) { 32 | return this.get(key) !== undefined 33 | }, 34 | 35 | remove(key) { 36 | if (this.disabled) { 37 | return 38 | } 39 | this.storage.removeItem(key) 40 | }, 41 | 42 | clear() { 43 | if (this.disabled) { 44 | return 45 | } 46 | this.storage.clear() 47 | }, 48 | 49 | getAll() { 50 | if (this.disabled) { 51 | return null 52 | } 53 | let ret = {} 54 | this.forEach((key, val) => { 55 | ret[key] = val 56 | }) 57 | return ret 58 | }, 59 | 60 | forEach(callback) { 61 | if (this.disabled) { 62 | return 63 | } 64 | for (let i = 0; i < this.storage.length; i++) { 65 | let key = this.storage.key(i) 66 | callback(key, this.get(key)) 67 | } 68 | } 69 | } 70 | 71 | Object.assign(store, api) 72 | 73 | Object.assign(store.session, api) 74 | 75 | function serialize(val) { 76 | return JSON.stringify(val) 77 | } 78 | 79 | function deserialize(val) { 80 | if (typeof val !== 'string') { 81 | return undefined 82 | } 83 | try { 84 | return JSON.parse(val) 85 | } catch (e) { 86 | return val || undefined 87 | } 88 | } 89 | 90 | try { 91 | const testKey = '__caniuse_storage__' 92 | store.set(testKey, testKey) 93 | if (store.get(testKey) !== testKey) { 94 | store.disabled = true 95 | } 96 | store.remove(testKey) 97 | } catch (e) { 98 | store.disabled = true 99 | } 100 | 101 | export default store 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-easy-vue", 3 | "version": "0.4.1", 4 | "description": "a generator for a super vue project ", 5 | "homepage": "https://github.com/501981732/easy-vue", 6 | "author": { 7 | "name": "wangmeng", 8 | "email": "501981732@qq.com", 9 | "url": "https://github.com/501981732" 10 | }, 11 | "files": [ 12 | "generators" 13 | ], 14 | "main": "generators/index.js", 15 | "keywords": [ 16 | "generator", 17 | "vue", 18 | "skeleton", 19 | "vw", 20 | "rem", 21 | "MPA", 22 | "dll", 23 | "multi-page", 24 | "jsdoc", 25 | "yeoman-generator", 26 | "eslint", 27 | "prettier", 28 | "lint-staged", 29 | "husky" 30 | ], 31 | "devDependencies": { 32 | "yeoman-test": "^1.7.0", 33 | "yeoman-assert": "^3.1.0", 34 | "coveralls": "^3.0.0", 35 | "eslint": "^5.4.0", 36 | "prettier": "^1.11.1", 37 | "husky": "^0.14.3", 38 | "lint-staged": "^7.2.2", 39 | "eslint-config-prettier": "^3.0.1", 40 | "eslint-plugin-prettier": "^2.6.0", 41 | "eslint-config-xo": "^0.24.2", 42 | "jest": "^23.5.0" 43 | }, 44 | "engines": { 45 | "npm": ">= 4.0.0" 46 | }, 47 | "dependencies": { 48 | "yeoman-generator": "^2.0.1", 49 | "chalk": "^2.1.0", 50 | "yosay": "^2.0.1" 51 | }, 52 | "jest": { 53 | "testEnvironment": "node" 54 | }, 55 | "lint-staged": { 56 | "*.js": [ 57 | "eslint --fix", 58 | "git add" 59 | ], 60 | "*.json": [ 61 | "prettier --write", 62 | "git add" 63 | ] 64 | }, 65 | "eslintConfig": { 66 | "extends": [ 67 | "xo", 68 | "prettier" 69 | ], 70 | "env": { 71 | "jest": true, 72 | "node": true 73 | }, 74 | "rules": { 75 | "prettier/prettier": [ 76 | "error", 77 | { 78 | "singleQuote": true, 79 | "printWidth": 90 80 | } 81 | ] 82 | }, 83 | "plugins": [ 84 | "prettier" 85 | ] 86 | }, 87 | "scripts": { 88 | "pretest": "eslint .", 89 | "precommit": "lint-staged", 90 | "test": "jest" 91 | }, 92 | "repository": "501981732/generator-easy-vue", 93 | "license": "MIT" 94 | } 95 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, 7 | body, 8 | div, 9 | span, 10 | applet, 11 | object, 12 | iframe, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | blockquote, 21 | pre, 22 | a, 23 | abbr, 24 | acronym, 25 | address, 26 | big, 27 | cite, 28 | code, 29 | del, 30 | dfn, 31 | em, 32 | img, 33 | ins, 34 | kbd, 35 | q, 36 | s, 37 | samp, 38 | small, 39 | strike, 40 | strong, 41 | sub, 42 | sup, 43 | tt, 44 | var, 45 | b, 46 | u, 47 | i, 48 | center, 49 | dl, 50 | dt, 51 | dd, 52 | ol, 53 | ul, 54 | li, 55 | fieldset, 56 | form, 57 | label, 58 | legend, 59 | table, 60 | caption, 61 | tbody, 62 | tfoot, 63 | thead, 64 | tr, 65 | th, 66 | td, 67 | article, 68 | aside, 69 | canvas, 70 | details, 71 | embed, 72 | figure, 73 | figcaption, 74 | footer, 75 | header, 76 | hgroup, 77 | menu, 78 | nav, 79 | output, 80 | ruby, 81 | section, 82 | summary, 83 | time, 84 | mark, 85 | audio, 86 | video { 87 | margin: 0; 88 | padding: 0; 89 | border: 0; 90 | font-size: 100%; 91 | font: inherit; 92 | vertical-align: baseline; 93 | } 94 | 95 | /* HTML5 display-role reset for older browsers */ 96 | article, 97 | aside, 98 | details, 99 | figcaption, 100 | figure, 101 | footer, 102 | header, 103 | hgroup, 104 | menu, 105 | nav, 106 | section { 107 | display: block; 108 | } 109 | 110 | body { 111 | line-height: normal; 112 | -webkit-text-size-adjust: none; 113 | min-height: 100%; 114 | /*滚动不顺畅*/ 115 | -webkit-overflow-scrolling: touch; 116 | font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Hiragino Sans GB', "Microsoft YaHei", Helvetica, "\5B8B\4F53", sans-serif; 117 | /*避免动画撑开*/ 118 | overflow-x: hidden; 119 | scroll-behavior: smooth; 120 | } 121 | 122 | ol, 123 | ul { 124 | list-style: none; 125 | } 126 | 127 | blockquote, 128 | q { 129 | quotes: none; 130 | } 131 | 132 | blockquote:before, 133 | blockquote:after, 134 | q:before, 135 | q:after { 136 | content: ''; 137 | content: none; 138 | } 139 | 140 | table { 141 | border-collapse: collapse; 142 | border-spacing: 0; 143 | } 144 | /*补充*/ 145 | input, 146 | button, 147 | textarea { 148 | border: 0; 149 | border-radius: 0; 150 | background: transparent; 151 | -webkit-appearance: none; 152 | box-sizing: border-box; 153 | outline: none; 154 | } 155 | 156 | button { 157 | outline: none; 158 | } 159 | 160 | a { 161 | text-decoration: none 162 | } 163 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/common.css: -------------------------------------------------------------------------------- 1 | /*//公共样式文件,class或id命名必须以c-为前缀开头*/ 2 | 3 | .clearfix:after, 4 | .clear:before { 5 | display: block; 6 | height: 0; 7 | visibility: hidden; 8 | clear: both; 9 | content: "."; 10 | } 11 | 12 | .clearfix { 13 | /*// overflow: auto;*/ 14 | zoom: 1; 15 | } 16 | 17 | [clearfix]:after, 18 | [clear]:before { 19 | display: block; 20 | height: 0; 21 | visibility: hidden; 22 | clear: both; 23 | content: "."; 24 | } 25 | 26 | [clearfix] { 27 | /*// overflow: auto;*/ 28 | zoom: 1; 29 | } 30 | 31 | .fl { 32 | float: left; 33 | } 34 | 35 | .fr { 36 | float: right; 37 | } 38 | 39 | 40 | .w-10 { 41 | width: 10px; 42 | } 43 | 44 | 45 | .h-10 { 46 | height: 10px; 47 | } 48 | 49 | .h-20 { 50 | height: 20px; 51 | } 52 | 53 | 54 | .mt-10 { 55 | margin-top: 10px; 56 | } 57 | 58 | 59 | .ml-10 { 60 | margin-left: 10px; 61 | } 62 | 63 | .pt-10 { 64 | padding-top: 10px; 65 | } 66 | 67 | .pl-10 { 68 | padding-left: 10px; 69 | } 70 | 71 | .search.icon { 72 | color: #000; 73 | position: absolute; 74 | margin-top: 2px; 75 | margin-left: 3px; 76 | width: 12px; 77 | height: 12px; 78 | border: solid 1px currentColor; 79 | border-radius: 100%; 80 | transform: rotate(-45deg); 81 | } 82 | 83 | .search.icon:before { 84 | content: ""; 85 | position: absolute; 86 | top: 12px; 87 | left: 5px; 88 | height: 6px; 89 | width: 1px; 90 | background-color: currentColor; 91 | } 92 | 93 | /*// 超出部分...*/ 94 | .ellipsis { 95 | overflow: hidden; 96 | text-overflow: ellipsis; 97 | white-space: nowrap; 98 | } 99 | 100 | /*// 垂直居中*/ 101 | .center_table { 102 | display: table; 103 | } 104 | 105 | .center_cell { 106 | display: table-cell; 107 | vertical-align: middle; 108 | text-align: center; 109 | } 110 | 111 | .no-scrollbar { 112 | &::-webkit-scrollbar { 113 | display: none; 114 | } 115 | } 116 | /*// 蒙层*/ 117 | .c-mask { 118 | position: fixed; 119 | top: 0; 120 | left: 0; 121 | bottom: 0; 122 | right: 0; 123 | height: 100%; 124 | background: rgba(0, 0, 0, .7); 125 | z-index: 1000; 126 | } 127 | /*// 蒙层box居中*/ 128 | .c-center-box { 129 | position: fixed; 130 | top: 50%; 131 | left: 50%; 132 | transform: translate(-50%, -50%); 133 | width: 80%; 134 | z-index: 1001; 135 | text-align: center; 136 | } 137 | 138 | .gpu { 139 | transform: translateZ(0); 140 | } 141 | .iphoneX { 142 | /*meta viewport-fit=cover*/ 143 | box-sizing: content-box; 144 | padding-bottom: constant(safe-area-inset-bottom); 145 | padding-bottom: env(safe-area-inset-bottom); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/textCarousel.vue: -------------------------------------------------------------------------------- 1 | 11 | 77 | 96 | -------------------------------------------------------------------------------- /generators/publicPath/templates/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | // "/yy": { 15 | // target: "http://zp.58.com/", 16 | // secure: false, 17 | // changeOrigin: true, 18 | // } 19 | }, 20 | 21 | // Various Dev Server settings 22 | host: 'localhost', // can be overwritten by process.env.HOST 23 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 24 | autoOpenBrowser: true, 25 | errorOverlay: true, 26 | notifyOnErrors: true, 27 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 28 | // Use Eslint Loader? 29 | // If true, your code will be linted during bundling and 30 | // linting errors and warnings will be shown in the console. 31 | useEslint: true, 32 | // If true, eslint errors and warnings will also be shown in the error overlay 33 | // in the browser. 34 | showEslintErrorsInOverlay: false, 35 | 36 | /** 37 | * Source Maps 38 | */ 39 | 40 | // https://webpack.js.org/configuration/devtool/#development 41 | devtool: 'cheap-module-eval-source-map', 42 | 43 | // If you have problems debugging vue-files in devtools, 44 | // set this to false - it *may* help 45 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 46 | cacheBusting: true, 47 | 48 | cssSourceMap: true 49 | }, 50 | 51 | build: { 52 | // Template for index.html 53 | index: path.resolve(__dirname, '../dist/index.html'), 54 | 55 | // Paths 56 | assetsRoot: path.resolve(__dirname, '../dist'), 57 | assetsSubDirectory: 'static', 58 | assetsPublicPath: '<%= publicPath%>', //j1.58cdn.com.cn/crop/baseteam/yunying/xxx/ 59 | cssPublicPath: '', 60 | imagePublicPath: '', //img.58cdn.com.cn/crop/baseteam/yunying/xxx/img/ 61 | /** 62 | * Source Maps 63 | */ 64 | 65 | productionSourceMap: false,//map文件 66 | // https://webpack.js.org/configuration/devtool/#production 67 | devtool: '#source-map', 68 | 69 | // Gzip off by default as many popular static hosts such as 70 | // Surge or Netlify already gzip all static assets for you. 71 | // Before setting to `true`, make sure to: 72 | // npm install --save-dev compression-webpack-plugin 73 | productionGzip: false, 74 | productionGzipExtensions: ['js', 'css'], 75 | 76 | // Run the build command with an extra argument to 77 | // View the bundle analyzer report after build finishes: 78 | // `npm run build --report` 79 | // Set to `true` or `false` to always turn it on or off 80 | bundleAnalyzerReport: process.env.npm_config_report 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /generators/imagePublicPath/templates/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | // "/yy": { 15 | // target: "http://zp.58.com/", 16 | // secure: false, 17 | // changeOrigin: true, 18 | // } 19 | }, 20 | 21 | // Various Dev Server settings 22 | host: 'localhost', // can be overwritten by process.env.HOST 23 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 24 | autoOpenBrowser: true, 25 | errorOverlay: true, 26 | notifyOnErrors: true, 27 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 28 | // Use Eslint Loader? 29 | // If true, your code will be linted during bundling and 30 | // linting errors and warnings will be shown in the console. 31 | useEslint: true, 32 | // If true, eslint errors and warnings will also be shown in the error overlay 33 | // in the browser. 34 | showEslintErrorsInOverlay: false, 35 | 36 | /** 37 | * Source Maps 38 | */ 39 | 40 | // https://webpack.js.org/configuration/devtool/#development 41 | devtool: 'cheap-module-eval-source-map', 42 | 43 | // If you have problems debugging vue-files in devtools, 44 | // set this to false - it *may* help 45 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 46 | cacheBusting: true, 47 | 48 | cssSourceMap: true 49 | }, 50 | 51 | build: { 52 | // Template for index.html 53 | index: path.resolve(__dirname, '../dist/index.html'), 54 | 55 | // Paths 56 | assetsRoot: path.resolve(__dirname, '../dist'), 57 | assetsSubDirectory: 'static', 58 | assetsPublicPath: '', //j1.58cdn.com.cn/crop/baseteam/yunying/xxx/ 59 | cssPublicPath: '', 60 | imagePublicPath: '<%= imagePublicPath%>', //img.58cdn.com.cn/crop/baseteam/yunying/xxx/img/ 61 | /** 62 | * Source Maps 63 | */ 64 | 65 | productionSourceMap: false,//map文件 66 | // https://webpack.js.org/configuration/devtool/#production 67 | devtool: '#source-map', 68 | 69 | // Gzip off by default as many popular static hosts such as 70 | // Surge or Netlify already gzip all static assets for you. 71 | // Before setting to `true`, make sure to: 72 | // npm install --save-dev compression-webpack-plugin 73 | productionGzip: false, 74 | productionGzipExtensions: ['js', 'css'], 75 | 76 | // Run the build command with an extra argument to 77 | // View the bundle analyzer report after build finishes: 78 | // `npm run build --report` 79 | // Set to `true` or `false` to always turn it on or off 80 | bundleAnalyzerReport: process.env.npm_config_report 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /generators/app/templates/config/_index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | // "/yy": { 15 | // target: "http://zp.58.com/", 16 | // secure: false, 17 | // changeOrigin: true, 18 | // } 19 | }, 20 | 21 | // Various Dev Server settings 22 | host: 'localhost', // can be overwritten by process.env.HOST 23 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 24 | autoOpenBrowser: true, 25 | errorOverlay: true, 26 | notifyOnErrors: true, 27 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 28 | // Use Eslint Loader? 29 | // If true, your code will be linted during bundling and 30 | // linting errors and warnings will be shown in the console. 31 | useEslint: true, 32 | // If true, eslint errors and warnings will also be shown in the error overlay 33 | // in the browser. 34 | showEslintErrorsInOverlay: false, 35 | 36 | /** 37 | * Source Maps 38 | */ 39 | 40 | // https://webpack.js.org/configuration/devtool/#development 41 | devtool: 'cheap-module-eval-source-map', 42 | 43 | // If you have problems debugging vue-files in devtools, 44 | // set this to false - it *may* help 45 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 46 | cacheBusting: true, 47 | 48 | cssSourceMap: true 49 | }, 50 | 51 | build: { 52 | // Template for index.html 53 | index: path.resolve(__dirname, '../dist/index.html'), 54 | 55 | // Paths 56 | assetsRoot: path.resolve(__dirname, '../dist'), 57 | assetsSubDirectory: 'static', 58 | assetsPublicPath: '<%= publicPath%>', //j1.58cdn.com.cn/crop/baseteam/yunying/xxx/ 59 | cssPublicPath: '', 60 | imagePublicPath: '<%= imagePublicPath%>', //img.58cdn.com.cn/crop/baseteam/yunying/xxx/img/ 61 | /** 62 | * Source Maps 63 | */ 64 | 65 | productionSourceMap: false,//map文件 66 | // https://webpack.js.org/configuration/devtool/#production 67 | devtool: '#source-map', 68 | 69 | // Gzip off by default as many popular static hosts such as 70 | // Surge or Netlify already gzip all static assets for you. 71 | // Before setting to `true`, make sure to: 72 | // npm install --save-dev compression-webpack-plugin 73 | productionGzip: false, 74 | productionGzipExtensions: ['js', 'css'], 75 | 76 | // Run the build command with an extra argument to 77 | // View the bundle analyzer report after build finishes: 78 | // `npm run build --report` 79 | // Set to `true` or `false` to always turn it on or off 80 | bundleAnalyzerReport: process.env.npm_config_report, 81 | purgecssPath: [ 82 | path.join(__dirname, './../src/index.html'), 83 | path.join(__dirname, './../src/*.vue'), 84 | path.join(__dirname, './../src/**/*.css'), 85 | path.join(__dirname, './../src/**/*.less'), 86 | path.join(__dirname, './../src/**/*.vue'), 87 | path.join(__dirname, './../src/**/**/*.css'), 88 | path.join(__dirname, './../src/**/**/*.less'), 89 | path.join(__dirname, './../src/**/**/*.vue'), 90 | path.join(__dirname, './../src/**/**/**/*.vue'), 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /generators/app/templates/build/_utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function(_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' ? 9 | config.build.assetsSubDirectory : 10 | config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function(options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders(loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap, 41 | }) 42 | }) 43 | if (loader === 'less') { 44 | loaders.push({ 45 | loader: 'style-resources-loader', 46 | options: { 47 | patterns: [ 48 | path.resolve(__dirname, '../src/assets/css/vars.less'), 49 | path.resolve(__dirname, '../src/assets/css/mixins.less'), 50 | ], 51 | injector: 'append' 52 | } 53 | }) 54 | } 55 | } 56 | 57 | // Extract CSS when that option is specified 58 | // (which is the case during production build) 59 | if (options.extract) { 60 | return ExtractTextPlugin.extract({ 61 | use: loaders, 62 | fallback: 'vue-style-loader' 63 | }) 64 | } else { 65 | return ['vue-style-loader'].concat(loaders) 66 | } 67 | } 68 | 69 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 70 | return { 71 | css: generateLoaders(), 72 | postcss: generateLoaders(), 73 | less: generateLoaders('less'), 74 | sass: generateLoaders('sass', { indentedSyntax: true }), 75 | scss: generateLoaders('sass'), 76 | stylus: generateLoaders('stylus'), 77 | styl: generateLoaders('stylus') 78 | } 79 | } 80 | 81 | // Generate loaders for standalone style files (outside of .vue) 82 | exports.styleLoaders = function(options) { 83 | const output = [] 84 | const loaders = exports.cssLoaders(options) 85 | 86 | for (const extension in loaders) { 87 | const loader = loaders[extension] 88 | output.push({ 89 | test: new RegExp('\\.' + extension + '$'), 90 | use: loader 91 | }) 92 | } 93 | 94 | return output 95 | } 96 | 97 | exports.createNotifierCallback = () => { 98 | const notifier = require('node-notifier') 99 | 100 | return (severity, errors) => { 101 | if (severity !== 'error') return 102 | 103 | const error = errors[0] 104 | const filename = error.file && error.file.split('!').pop() 105 | 106 | notifier.notify({ 107 | title: packageConfig.name, 108 | message: severity + ': ' + error.name, 109 | subtitle: filename || '', 110 | icon: path.join(__dirname, 'logo.png') 111 | }) 112 | } 113 | } 114 | // 多页面entry 115 | <% if (projectType == 'MPA') {%> 116 | const glob = require('glob') 117 | exports.getEntry = (globPath, type = 'js') => { 118 | return glob.sync(globPath).reduce(function(prev, curr) { 119 | type == 'js' ? prev[curr.slice(6, -3)] = curr : prev[curr.slice(6, -5)] = curr 120 | return prev; 121 | }, {}); 122 | } 123 | <%}%> 124 | // { 'module/fairs/index': './src/module/fairs/index.js', 125 | // 'module/shop/index': './src/module/shop/index.js' } 126 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/vwDemo.vue: -------------------------------------------------------------------------------- 1 | 45 | 56 | 57 | 178 | -------------------------------------------------------------------------------- /generators/app/templates/build/_webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | // const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 12 | // const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin') 13 | const portfinder = require('portfinder') 14 | 15 | const HOST = process.env.HOST 16 | const PORT = process.env.PORT && Number(process.env.PORT) 17 | 18 | // 防止报错 filter module rules 19 | baseWebpackConfig.module.rules.forEach((item,index) => { 20 | if(item.use && Array.isArray(item.use)) { 21 | baseWebpackConfig.module.rules[index].use = item.use.filter(Boolean) 22 | } 23 | }) 24 | <% if(projectType =='MPA'){ %> 25 | // 多页面配置 26 | const glob = require('glob'); 27 | const htmls = glob.sync('./src/modules/**/*.html').map(function (item) { 28 | var names = item.split('/') 29 | return new HtmlWebpackPlugin({ 30 | // filename: './'+ names[2]+'/'+names[4], //相当于url 31 | filename: './' + names[4], //相当于url './main.html' 32 | template: item, //模板路径 './src/modules/**/*.html' 33 | inject: true, 34 | chunks:[item.slice(6, -5),'vendor','manifest'] 35 | }); 36 | });<% }%> 37 | 38 | const devWebpackConfig = merge(baseWebpackConfig, { 39 | module: { 40 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 41 | }, 42 | // cheap-module-eval-source-map is faster for development 43 | devtool: config.dev.devtool, 44 | 45 | // these devServer options should be customized in /config/index.js 46 | devServer: { 47 | clientLogLevel: 'warning', 48 | historyApiFallback: { 49 | rewrites: [ 50 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 51 | ], 52 | }, 53 | hot: true, 54 | contentBase: false, // since we use CopyWebpackPlugin. 55 | compress: true, 56 | host: HOST || config.dev.host, 57 | port: PORT || config.dev.port, 58 | open: config.dev.autoOpenBrowser, 59 | overlay: config.dev.errorOverlay 60 | ? { warnings: false, errors: true } 61 | : false, 62 | publicPath: config.dev.assetsPublicPath, 63 | proxy: config.dev.proxyTable, 64 | quiet: true, // necessary for FriendlyErrorsPlugin 65 | watchOptions: { 66 | poll: config.dev.poll, 67 | } 68 | }, 69 | plugins: [ 70 | new webpack.DefinePlugin({ 71 | 'process.env': require('../config/dev.env') 72 | }), 73 | new webpack.HotModuleReplacementPlugin(), 74 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 75 | new webpack.NoEmitOnErrorsPlugin(), 76 | // https://github.com/ampedandwired/html-webpack-plugin 77 | <% if(projectType =='SPA'){ %> 78 | new HtmlWebpackPlugin({ 79 | filename: 'index.html', 80 | template: 'index.html', 81 | inject: true 82 | }),<% }%> 83 | // copy custom static assets 84 | new CopyWebpackPlugin([ 85 | { 86 | from: path.resolve(__dirname, '../static'), 87 | to: config.dev.assetsSubDirectory, 88 | ignore: ['.*'] 89 | } 90 | ]), 91 | // new ExtractTextPlugin({ 92 | // filename: utils.assetsPath('css/[name].[contenthash].css'), 93 | // allChunks: true, 94 | // }), 95 | // new SkeletonWebpackPlugin({ 96 | // webpackConfig: require('./webpack.skeleton.conf'), 97 | // quiet: true 98 | // }), 99 | ]<% if(projectType =='MPA'){ %>.concat(htmls)<%}%> 100 | }) 101 | 102 | module.exports = new Promise((resolve, reject) => { 103 | portfinder.basePort = process.env.PORT || config.dev.port 104 | portfinder.getPort((err, port) => { 105 | if (err) { 106 | reject(err) 107 | } else { 108 | // publish the new Port, necessary for e2e tests 109 | process.env.PORT = port 110 | // add port to devServer config 111 | devWebpackConfig.devServer.port = port 112 | 113 | // Add FriendlyErrorsPlugin 114 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 115 | compilationSuccessInfo: { 116 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 117 | }, 118 | onErrors: config.dev.notifyOnErrors 119 | ? utils.createNotifierCallback() 120 | : undefined 121 | })) 122 | 123 | resolve(devWebpackConfig) 124 | } 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /generators/app/templates/build/_webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | // const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 12 | // const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin') 13 | const portfinder = require('portfinder') 14 | 15 | const HOST = process.env.HOST 16 | const PORT = process.env.PORT && Number(process.env.PORT) 17 | 18 | // 防止报错 filter module rules 19 | baseWebpackConfig.module.rules.forEach((item,index) => { 20 | if(item.use && Array.isArray(item.use)) { 21 | baseWebpackConfig.module.rules[index].use = item.use.filter(Boolean) 22 | } 23 | }) 24 | <% if(projectType =='MPA'){ %> 25 | // 多页面配置 26 | const glob = require('glob'); 27 | const htmls = glob.sync('./src/modules/**/*.html').map(function (item) { 28 | var names = item.split('/') 29 | return new HtmlWebpackPlugin({ 30 | // filename: './'+ names[2]+'/'+names[4], //相当于url 31 | filename: './' + names[4], //相当于url './main.html' 32 | template: item, //模板路径 './src/modules/**/*.html' 33 | inject: true, 34 | chunks:[item.slice(6, -5),'vendor','manifest'] 35 | }); 36 | });<% }%> 37 | 38 | const devWebpackConfig = merge(baseWebpackConfig, { 39 | module: { 40 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 41 | }, 42 | // cheap-module-eval-source-map is faster for development 43 | devtool: config.dev.devtool, 44 | 45 | // these devServer options should be customized in /config/index.js 46 | devServer: { 47 | clientLogLevel: 'warning', 48 | historyApiFallback: { 49 | rewrites: [ 50 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 51 | ], 52 | }, 53 | hot: true, 54 | contentBase: false, // since we use CopyWebpackPlugin. 55 | compress: true, 56 | host: HOST || config.dev.host, 57 | port: PORT || config.dev.port, 58 | open: config.dev.autoOpenBrowser, 59 | overlay: config.dev.errorOverlay 60 | ? { warnings: false, errors: true } 61 | : false, 62 | publicPath: config.dev.assetsPublicPath, 63 | proxy: config.dev.proxyTable, 64 | quiet: true, // necessary for FriendlyErrorsPlugin 65 | watchOptions: { 66 | poll: config.dev.poll, 67 | } 68 | }, 69 | plugins: [ 70 | new webpack.DefinePlugin({ 71 | 'process.env': require('../config/test.env') 72 | }), 73 | new webpack.HotModuleReplacementPlugin(), 74 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 75 | new webpack.NoEmitOnErrorsPlugin(), 76 | // https://github.com/ampedandwired/html-webpack-plugin 77 | <% if(projectType =='SPA'){ %> 78 | new HtmlWebpackPlugin({ 79 | filename: 'index.html', 80 | template: 'index.html', 81 | inject: true 82 | }),<% }%> 83 | // copy custom static assets 84 | new CopyWebpackPlugin([ 85 | { 86 | from: path.resolve(__dirname, '../static'), 87 | to: config.dev.assetsSubDirectory, 88 | ignore: ['.*'] 89 | } 90 | ]), 91 | // new ExtractTextPlugin({ 92 | // filename: utils.assetsPath('css/[name].[contenthash].css'), 93 | // allChunks: true, 94 | // }), 95 | // new SkeletonWebpackPlugin({ 96 | // webpackConfig: require('./webpack.skeleton.conf'), 97 | // quiet: true 98 | // }), 99 | ]<% if(projectType =='MPA'){ %>.concat(htmls)<%}%> 100 | }) 101 | 102 | module.exports = new Promise((resolve, reject) => { 103 | portfinder.basePort = process.env.PORT || config.dev.port 104 | portfinder.getPort((err, port) => { 105 | if (err) { 106 | reject(err) 107 | } else { 108 | // publish the new Port, necessary for e2e tests 109 | process.env.PORT = port 110 | // add port to devServer config 111 | devWebpackConfig.devServer.port = port 112 | 113 | // Add FriendlyErrorsPlugin 114 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 115 | compilationSuccessInfo: { 116 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 117 | }, 118 | onErrors: config.dev.notifyOnErrors 119 | ? utils.createNotifierCallback() 120 | : undefined 121 | })) 122 | 123 | resolve(devWebpackConfig) 124 | } 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Loading.vue: -------------------------------------------------------------------------------- 1 | 22 | 51 | 170 | -------------------------------------------------------------------------------- /generators/app/templates/build/_webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | const DllReferencePlugin = require("webpack/lib/DllReferencePlugin"); 7 | //将dll文件插入 8 | const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin'); 9 | 10 | function resolve(dir) { 11 | return path.join(__dirname, '..', dir) 12 | } 13 | <% if (eslint) {%> 14 | const createLintingRule = () => ({ 15 | test: /\.(js|vue)$/, 16 | loader: "eslint-loader", 17 | enforce: "pre", 18 | include: [resolve("src"), resolve("test")], 19 | options: { 20 | formatter: require("eslint-friendly-formatter"), 21 | emitWarning: !config.dev.showEslintErrorsInOverlay 22 | } 23 | }); 24 | <% }%> 25 | const ISPROD = process.env.NODE_ENV === 'production' 26 | 27 | const IMAGE_NAME = ISPROD ? '[name].[ext]' : utils.assetsPath('img/[name].[ext]') 28 | 29 | const IMAGE_LOADER_QUERY = ISPROD ? `&name=${IMAGE_NAME}&outputPath=/img&publicPath=${config.build.imagePublicPath}` : `&name=${IMAGE_NAME}` 30 | 31 | //上线前打包压缩图片 开发环境禁用 32 | const IMAGELOADER = ISPROD ? `image-webpack-loader?{mozjpeg: {progressive: true,quality: 65},pngquant:{quality: "55-90", speed: 4}}` : null 33 | 34 | module.exports = { 35 | context: path.resolve(__dirname, '../'), 36 | <% if (projectType == 'SPA') {%> 37 | entry: { 38 | app: './src/main.js' 39 | }, 40 | <% } else { %> 41 | entry: utils.getEntry('./src/modules/**/*.js'), // 获得入口js文件, 42 | <% }%> 43 | output: { 44 | path: config.build.assetsRoot, 45 | filename: '[name].js', 46 | publicPath: process.env.NODE_ENV === 'production' ? 47 | config.build.assetsPublicPath : config.dev.assetsPublicPath 48 | }, 49 | resolve: { 50 | extensions: ['.js', '.vue', '.json'], 51 | alias: { 52 | 'vue$': 'vue/dist/vue.esm.js', 53 | '@': resolve('src'), 54 | } 55 | }, 56 | module: { 57 | rules: [ 58 | <% if (eslint) {%>...(config.dev.useEslint ? [createLintingRule()] : []), <% }%> { 59 | test: /\.vue$/, 60 | loader: 'vue-loader', 61 | options: vueLoaderConfig 62 | }, { 63 | test: /\.js$/, 64 | loader: 'babel-loader', 65 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 66 | }, { 67 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 68 | // loader: 'url-loader', 69 | // options: { 70 | // limit: 10000, 71 | // name: utils.assetsPath('img/[name].[ext]') 72 | use: [ 73 | `url-loader?limit=10000${IMAGE_LOADER_QUERY}`, 74 | // IMAGELOADER, 75 | ] 76 | }, { 77 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 78 | loader: 'url-loader', 79 | options: { 80 | limit: 10000, 81 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 82 | } 83 | }, { 84 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 85 | loader: 'url-loader', 86 | options: { 87 | limit: 10000, 88 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 89 | } 90 | } 91 | ] 92 | }, 93 | node: { 94 | // prevent webpack from injecting useless setImmediate polyfill because Vue 95 | // source contains it (although only uses it if it's native). 96 | setImmediate: false, 97 | // prevent webpack from injecting mocks to Node native modules 98 | // that does not make sense for the client 99 | dgram: 'empty', 100 | fs: 'empty', 101 | net: 'empty', 102 | tls: 'empty', 103 | child_process: 'empty' 104 | }, 105 | // externals: { 106 | // 'vue': 'Vue', 107 | // 'vue-router': 'VueRouter', 108 | // 'axios': 'axios', 109 | // }, 110 | plugins: [ 111 | // 告诉 Webpack 使用了哪些动态链接库 112 | new DllReferencePlugin({ 113 | // 描述 vue 动态链接库的文件内容 114 | manifest: require('../static/libs/dll_vendor.manifest.json'), 115 | }), 116 | //将打包的动态链接库插入到html模板中 117 | new HtmlWebpackIncludeAssetsPlugin({ 118 | assets: ["static/libs/dll.dll_vendor.js"], 119 | append: false, 120 | hash: false, 121 | publicPath: process.env.NODE_ENV === "production" ? 122 | config.build.assetsPublicPath : config.dev.assetsPublicPath 123 | }) 124 | ] 125 | } 126 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Alert.vue: -------------------------------------------------------------------------------- 1 | 29 | 87 | 173 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Confirm.vue: -------------------------------------------------------------------------------- 1 | 36 | 82 | 169 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/mixins.less: -------------------------------------------------------------------------------- 1 | // 箭头 2 | 3 | .down-arrow(@color:#bec8ca) { 4 | position: relative; 5 | &:after { 6 | content: ""; 7 | border-left: 2px solid @color; 8 | border-top: 2px solid @color; 9 | transform: translateY(-50%) rotate(-135deg); 10 | //为了更准确的居中,添加translateY(-50%),即上移自身的50%。注意必须写在rotate前面,否则坐标系被旋转后再平移达不到想要的效果 11 | display: inline-block; 12 | height: 8px; 13 | width: 8px; 14 | position: absolute; 15 | top: 50%; 16 | right: 2px; 17 | } 18 | } 19 | .up-arrow(@color:#bec8ca) { 20 | position: relative; 21 | &:after { 22 | content: ""; 23 | border-left: 2px solid @color; 24 | border-top: 2px solid @color; 25 | transform: translateY(-50%) rotate(45deg); 26 | display: inline-block; 27 | height: 8px; 28 | width: 8px; 29 | position: absolute; 30 | top: 50%; 31 | right: 2px; 32 | } 33 | } 34 | .right-arrow(@color:#bec8ca) { 35 | position: relative; 36 | &:after { 37 | content: ""; 38 | border-left: 2px solid @color; 39 | border-top: 2px solid @color; 40 | transform: translateY(-50%) rotate(135deg); 41 | display: inline-block; 42 | height: 8px; 43 | width: 8px; 44 | position: absolute; 45 | top: 50%; 46 | right: 2px; 47 | } 48 | } 49 | // m 一像素边框 50 | .one-px-top(@color:#fff) { 51 | position: relative; 52 | 53 | &:before { 54 | position: absolute; 55 | content: ""; 56 | left: 0; 57 | top: 0; 58 | height: 1px; 59 | width: 100%; 60 | background-color: @color; 61 | transform: scaleY(0.5); 62 | } 63 | } 64 | 65 | .one-px-bottom(@color:#fff) { 66 | position: relative; 67 | 68 | &:after { 69 | position: absolute; 70 | content: ""; 71 | left: 0; 72 | bottom: 0; 73 | height: 1px; 74 | width: 100%; 75 | background-color: @color; 76 | transform: scaleY(0.5); 77 | } 78 | } 79 | 80 | .one-px-right(@color:#fff) { 81 | position: relative; 82 | 83 | &:after { 84 | position: absolute; 85 | content: ""; 86 | right: 0; 87 | top: 0; 88 | width: 1px; 89 | height: 100%; 90 | background-color: @color; 91 | transform: scaleX(0.5); 92 | } 93 | } 94 | 95 | .one-px-left(@color:#fff) { 96 | position: relative; 97 | 98 | &:before { 99 | position: absolute; 100 | content: ""; 101 | left: 0; 102 | top: 0; 103 | width: 1px; 104 | height: 100%; 105 | background-color: @color; 106 | transform: scaleX(0.5); 107 | } 108 | } 109 | 110 | .one-px-button(@color:#fff) { 111 | position: relative; 112 | 113 | &:after { 114 | content: " "; 115 | width: 200%; 116 | height: 200%; 117 | position: absolute; 118 | top: 0; 119 | left: 0; 120 | border: 1px solid @color; 121 | transform: scale(0.5); 122 | transform-origin: 0 0; 123 | box-sizing: border-box; 124 | border-radius: 10px; 125 | } 126 | } 127 | 128 | .one-px-box(@color:#fff) { 129 | position: relative; 130 | 131 | &:after { 132 | content: " "; 133 | width: 200%; 134 | height: 200%; 135 | position: absolute; 136 | top: 0; 137 | left: 0; 138 | border: 1px solid @color; 139 | transform: scale(0.5); 140 | transform-origin: 0 0; 141 | box-sizing: border-box; 142 | border-radius: 10px; 143 | // 事件击穿 144 | pointer-events: none; 145 | } 146 | } 147 | 148 | // 二倍图 三倍图 149 | .bg-image(@url) { 150 | background-image:~"url('@{url}@2x.png')"; 151 | @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio:3) { 152 | background-image:~"url('@{url}@3x.png')"; 153 | } 154 | } 155 | 156 | // 角度渐变 三色 157 | .linear-gradient(@deg: -180deg, @start: #4771ec,@startPos: 0%,@minddle: #4768ea, @middlePos: 20%, @end: #2731cd, @endPos: 100%) { 158 | filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0, startColorStr=@start, endColorStr=@end); 159 | background: linear-gradient( 160 | @deg, 161 | @start @startPos, 162 | @minddle @middlePos, 163 | @end @endPos 164 | ); 165 | } 166 | // 两色渐变 两色 167 | .linear-gradient2(@deg: to bottom, @start: #4771ec,@startPos: 0%, @end: #2731cd, @endPos: 100%) { 168 | filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0, startColorStr=@start, endColorStr=@end); 169 | background: linear-gradient( 170 | @deg, 171 | @start @startPos, 172 | @end @endPos 173 | ); 174 | } 175 | 176 | .c-zoom-time(@time: 4s,@type: ease-in-out, @delay:0s) { 177 | animation: zoomIn2 @time @type @delay infinite; 178 | } 179 | @keyframes zoomIn2 { 180 | from { 181 | transform: scale(.95); 182 | } 183 | 184 | 50% { 185 | transform: scale(1.05); 186 | } 187 | 188 | 100% { 189 | transform: scale(.95); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /generators/app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= projectName%>", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "<%= author%> <%= email%>", 6 | "private": true, 7 | "scripts": {<% if(skeleton){ %> 8 | "skeleton": "webpack --config build/webpack.skeleton.conf.js && node skeleton.js",<% }%> 9 | "dll:dev": "cross-env NODE_ENV=development webpack --config build/webpack.dll.conf.js", 10 | "dll:build": "cross-env NODE_ENV=production webpack --config build/webpack.dll.conf.js", 11 | "dev": "npm run dll:dev && webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 12 | "dev:test": "npm run dll:dev && webpack-dev-server --inline --progress --config build/webpack.test.conf.js", 13 | "start": "npm run dev",<% if (eslint) {%> 14 | "eslint": "eslint --ext .js,.vue src", 15 | "eslint:fix": "eslint --fix --ext .js,.vue src", 16 | "lint-staged": "lint-staged", 17 | "prettier": "prettier --tab-width 4 --write \"./src/**/*.{js,vue,jsx,css,less,sass,scss,md,json}\"", 18 | "precommit": "lint-staged", 19 | "prebuild": "npm run dll:build && npm run eslint",<% }%> 20 | "jsdoc": "jsdoc -c ./jsdoc.conf.json -R ./README.md", 21 | "build": "node build/build.js" 22 | },<% if(eslint){ %> 23 | "pre-push": [], 24 | "lint-staged": { 25 | "src/**/*.{js,vue,jsx,sass,scss,md,json}": [ 26 | "prettier --tab-width 4 --write", 27 | "eslint --fix", 28 | "git add" 29 | ] 30 | },<% }%> 31 | "dependencies": {<% if(layout === 'rem'){ %> 32 | "fastclick": "^1.0.6", 33 | "amfe-flexible": "^2.2.1",<% } else if (layout === 'vw') {%> 34 | "fastclick": "^1.0.6",<% }%> 35 | "axios": "^0.18.0",<% if(vuex){ %> 36 | "vuex": "^3.0.1",<% }%> 37 | "vue": "^2.5.2", 38 | "vue-router": "^3.0.1" 39 | }, 40 | "devDependencies": { 41 | "autoprefixer": "^7.1.2", 42 | "babel-core": "^6.22.1",<% if (eslint) {%> 43 | "babel-eslint": "^10.0.1", 44 | "eslint": "^5.9.0", 45 | "eslint-config-standard": "^12.0.0", 46 | "eslint-friendly-formatter": "^4.0.1", 47 | "eslint-loader": "^2.1.1", 48 | "eslint-plugin-import": "^2.14.0", 49 | "eslint-plugin-node": "^8.0.0", 50 | "eslint-plugin-promise": "^4.0.1", 51 | "eslint-plugin-standard": "^4.0.0", 52 | "eslint-plugin-vue": "^4.7.1", 53 | "lint-staged": "^8.1.0", 54 | "prettier": "^1.15.3", 55 | "purgecss-webpack-plugin": "^1.5.0", 56 | "husky": "^1.2.0",<% }%> 57 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 58 | "babel-loader": "^7.1.1", 59 | "babel-plugin-syntax-jsx": "^6.18.0", 60 | "babel-plugin-transform-runtime": "^6.22.0", 61 | "babel-plugin-transform-vue-jsx": "^3.5.0", 62 | "babel-polyfill": "^6.26.0", 63 | "babel-preset-env": "^1.3.2", 64 | "babel-preset-stage-2": "^6.22.0", 65 | "chalk": "^2.0.1", 66 | "copy-webpack-plugin": "^4.0.1", 67 | "cross-env": "^5.2.0", 68 | "css-loader": "^0.28.0",<% if(layout === 'vw'){ %> 69 | "cssnano": "^4.1.7", 70 | "cssnano-preset-advanced": "^4.0.5", 71 | "postcss-aspect-ratio-mini": "^0.0.2", 72 | "postcss-cssnext": "^3.1.0", 73 | "postcss-px-to-viewport": "^1.1.0", 74 | "postcss-write-svg": "^3.0.1",<% }%> 75 | "extract-text-webpack-plugin": "^3.0.0", 76 | "file-loader": "^1.1.4", 77 | "friendly-errors-webpack-plugin": "^1.6.1",<% if(projectType === 'MPA'){ %> 78 | "glob": "^7.1.3",<% }%> 79 | "glob-all": "^3.1.0", 80 | "html-webpack-include-assets-plugin": "^1.0.6", 81 | "html-webpack-plugin": "^2.30.1",<% if(cssPrepeocessor === 'less'){ %> 82 | "jsdoc": "^3.5.5", 83 | "jsdoc-vue": "^1.0.0", 84 | "less": "^3.8.1", 85 | "less-loader": "^4.1.0",<% }%> 86 | "node-notifier": "^5.1.2",<% if(cssPrepeocessor === 'scss'){ %> 87 | "node-sass": "^4.10.0", 88 | "sass-loader": "^7.1.0",<% }%> 89 | "optimize-css-assets-webpack-plugin": "^3.2.0", 90 | "ora": "^1.2.0", 91 | "portfinder": "^1.0.13", 92 | "postcss-import": "^11.0.0", 93 | "postcss-loader": "^2.0.8",<% if(layout === 'rem'){ %> 94 | "postcss-pxtorem": "^4.0.1",<% }%> 95 | "postcss-url": "^7.2.1", 96 | "rimraf": "^2.6.0", 97 | "semver": "^5.3.0", 98 | "shelljs": "^0.7.6", 99 | "style-resources-loader": "^1.2.1", 100 | "uglifyjs-webpack-plugin": "^1.1.1", 101 | "url-loader": "^0.5.8", 102 | "vue-loader": "^13.3.0",<% if(skeleton){%> 103 | "vue-server-renderer": "^2.5.17", 104 | "vue-skeleton-webpack-plugin": "^1.2.2",<%}%> 105 | "vue-style-loader": "^3.0.1", 106 | "vue-template-compiler": "^2.5.2", 107 | "webpack": "^3.6.0", 108 | "webpack-bundle-analyzer": "^2.9.0", 109 | "webpack-dev-server": "^2.9.1", 110 | "webpack-merge": "^4.1.0" 111 | }, 112 | "engines": { 113 | "node": ">= 6.0.0", 114 | "npm": ">= 3.0.0" 115 | }, 116 | "browserslist": [ 117 | "> 1%", 118 | "last 2 versions", 119 | "not ie <= 8", 120 | "iOS >= 7", 121 | "Firefox >= 20", 122 | "Android > 3.2" 123 | ] 124 | } 125 | -------------------------------------------------------------------------------- /generators/app/templates/README.md: -------------------------------------------------------------------------------- 1 | # easy-vue 2 | 3 | > 多功能vue搬砖器 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | ```base 9 | yo easy-vue 一键生成可配置项目 10 | # install dependencies 11 | npm install or yarn install 12 | 13 | # 生成骨架屏(仅需要运行一次!) 14 | npm run skeleton 15 | 16 | # 生成开发环境下dll 17 | npm run dll:dev 18 | 19 | # 生成生产环境下dll 20 | npm run dll:build 21 | 22 | # eslint 代码检测 23 | npm run eslint 24 | 25 | # eslint 代码修复 26 | npm run eslint:fix 27 | 28 | # 代码格式化 (多人协作不建议使用,git commit 自动美化 eslint:fix自己修改部分的代码) 29 | npm run prettier 30 | 31 | #生成代码文档(jsdoc格式注释代码) 32 | npm run jsdoc 33 | 34 | # serve with hot reload at localhost:8080(自动 dll开发环境代码,并insert到html模板) 35 | npm run dev or npm run start 36 | 37 | # 测试环境 38 | npm run dev:test 39 | 40 | # rebuild钩子生成盛传环境dll 并eslint 代码 41 | npm run prebuild 42 | 43 | # build for production with minification(自动 dll生成环境代码,并insert到html模板已配置好publicPath) 44 | npm run build 45 | 46 | # build for production and view the bundle analyzer report 47 | npm run build --report 48 | 49 | #配置 publicPath imagePublicPath 或修改 config/index.js 50 | yo easy-vue:imagePublicPath path... 51 | yo easy-vue:publicPath path... 52 | 53 | # 一键生成页面 54 | yo easy-vue:page pageName 一键生成page 55 | yo easy-vue:mpage pageName 一键生成mutil-page(if you choose MPA) 56 | ``` 57 | ## 功能 58 | 59 | 60 | ``` 61 | 1. 支持可选reset.css normalize.css 62 | 2. 支持可选 rem布局 or vw布局 63 | 3. 等比例宽高容器 一像素边框等移动端解决方案 64 | 4. 支持可选less sass css预处理器 65 | 5. 动态加载路由,以及自动注入组件 66 | 6. 预渲染骨架屏 67 | 7. 按需(按照指定的浏览器环境所需)引入polyfill 68 | 8. 提供vue 插件alert demo写法 69 | 9. axios init配置 70 | 10. vuex init配置 71 | 11. vue-router两种模式 history 上线配置base url 填坑 72 | 12. 上线build 自动压缩图片 73 | 13. 重写image publicPath 74 | 14. 跨域反向代理案例 75 | 15. 单元测试和e2e todo 76 | 16. npm/yarn/bower... 77 | ``` 78 | 79 | **0.1.4** 80 | 81 | 增加多页面应用 82 | 83 | **0.1.8** 84 | 85 | 增加一键生成多页面应用 86 | 87 | **0.2.0** 88 | 89 | 增加jsdoc配置 自动生成说明文档 增加对.vue文件的文档解析 jsdoc jsdoc-vue 90 | 91 | **0.2.1** 92 | 93 | 1. 增加代码检测工作流 prettier统一代码格式 precommit自动美化格式以及修复代码 lint-staged 94 | 2. 多人写作避免代码冲突,渐进式lint代码 95 | 3. 如果想要启用官方推荐的standard 可在.eslintrc.js中开启 96 | 97 | **0.2.2** 98 | 99 | 开启可配置dns预解析 100 | 101 | **0.2.3** 102 | 103 | 开启动态链接库dll并自动插入到模板中新增插件 DllPlugin,DllReferencePlugin,html-webpack-include-assets-plugin,cross-env 104 | 动态链接库修改在config/dll.lib.dependencies.js 默认只用了vue,vue-router 105 | 106 | **0.3.0** 107 | 108 | 实战之后的优化以及埋坑 109 | 110 | 1. css打包大小优化 111 | 2. 常用css方案(渐变兼容IE9 一像素边框 二倍图三倍图 iphoneX适配 等) 112 | 3. axios配置优化 113 | 4. 增加常见组件库 插件库 工具库 114 | ps: 非常棒的preload prefetch and prepack 相关插件不支持webpack3.X sad💔... 115 | 116 | **0.3.3** 117 | 118 | 1. 增加测试环境:开发环境接口 -> **mock**,测试环境走 ->**proxy**,生产环境直接打包线上全路径 119 | 2. 增加eventBus事件总线,挂载vue 全局调用 120 | 3. 修改loading插件样式 121 | 4. 请求增加超时提示,超时时间为10s 122 | 5. vw布局插件升级,支持排除node_momdules插件包,rem布局暂不支持可换插件postcss-px2rem-exclude or px2rem-exclude 123 | 124 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 125 | 126 | ## 目录结构 127 | 128 | ``` 129 | . 130 | ├── build // 启动文件 131 | │ ├── build 132 | │ ├── check-versions.js 133 | │ ├── logo.png 134 | │ ├── commit-css-webpack-plugin.js //骨架屏相关插件 135 | │ ├── utils.js 136 | │ ├── vue-loader.conf.js 137 | │ ├── webpack.base.conf.js 138 | │ ├── webpack.dev.conf.js 139 | │ ├── webpack.prod.conf.js 140 | │ ├── webpack.test.conf.js 141 | │ ├── webpack.dll.conf.js //dll动态连接库 142 | │ ├── webpack.skeleton.conf.js //生成骨架屏 143 | ├── config // 项目环境配置 144 | │ ├── index.js // 入口 145 | │ ├── dll.libs.dependencies.js // dll所需库配置 146 | │ ├── dev.env.js // 开发环境配置 147 | │ ├── prod.env.js // 生成环境配置 148 | │ ├── test.env.js // 测试环境配置 149 | ├── dist // 上线打包 150 | │ ├── modules // 多页面 151 | │ ├── static 152 | │ │ ├── css 153 | │ │ ├── js 154 | │ │ └── libs //动态链接库文件 155 | ├── jsdoc // jsdoc 注释自动生成文档 156 | ├── src // 源码目录 157 | │ ├── assets 158 | │ ├── components 159 | │ ├── config 160 | │ │ ├── apis.js //封装接口 161 | │ │ ├── axiosConfig.js //配置axios 162 | │ │ ├── env.js //根据环境配置 切换接口baseURL && history模式路由配置 163 | │ ├── mixins 164 | │ ├── modules //多页面 165 | │ ├── pages 166 | │ ├── plugin //手写vue插件 167 | │ ├── filters //过滤器 168 | │ ├── direction //directions 169 | │ ├── router 170 | │ ├── store // 初始化vuex 171 | │ ├── eventBus // 事件总线 172 | │ ├── utils 173 | │ ├── APP.vue 174 | │ ├── mian.js 175 | │ ├── skeleton.entry.js //骨架屏配置 176 | ├── static 177 | ├── .babelrc 178 | ├── .editorconfig 179 | ├── eslintrc.js 180 | ├── .eslintignore 181 | ├── .gitignore 182 | ├── postcssrc.js 183 | ├── index.html 184 | ├── jsdoc.cong.json //jsdoc配置文件 185 | ├── .index 186 | ├── package.json 187 | ├── package-lock.json 188 | └── README.md 189 | ``` 190 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/animation.css: -------------------------------------------------------------------------------- 1 | /*// fade*/ 2 | /*.c-mask { 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | height: 100%; 9 | background: rgba(0, 0, 0, .7); 10 | }*/ 11 | /*// vue钩子动画*/ 12 | .c-fade-enter-active, .c-fade-leave-active { 13 | transition: opacity .5s; 14 | } 15 | .c-fade-enter, .c-fade-leave-to { 16 | opacity: 0; 17 | } 18 | 19 | .c-fadedown-enter-active, .c-fadedown-leave-active { 20 | transform: translateY(0); 21 | transition: all .6s ease-out; 22 | } 23 | .c-fadedown-enter, .c-fadedown-leave-to { 24 | opacity: 0; 25 | transform: translateY(-100px); 26 | } 27 | .c-fademove-enter-active, .c-fademove-leave-active { 28 | transform: translate(-50%, -50%)!important; 29 | transition: all .6s ease-out; 30 | } 31 | .c-fademove-enter, .c-fademove-leave-to { 32 | opacity: 0; 33 | transform: translate(-50%, -45%)!important; 34 | } 35 | .c-zoom-enter-active { 36 | animation: zoomInUp .5s; 37 | } 38 | 39 | .c-zoom-leave-active { 40 | animation: zoomInDown .5s reverse; 41 | } 42 | 43 | .c-bounce-enter-active { 44 | animation: bounceInUp 1s ; 45 | } 46 | 47 | .c-bounce-leave-active { 48 | animation: bounceInUp .8s reverse; 49 | } 50 | 51 | 52 | .c-zoom { 53 | animation: zoomIn 4.15s infinite; 54 | } 55 | .c-zoom2 { 56 | animation: zoomIn3 1s linear infinite ; 57 | } 58 | .c-translateY1 { 59 | animation: translateY1 4.15s linear 1.2s infinite reverse ; 60 | } 61 | .c-translateY2 { 62 | animation: translateY2 4.15s linear 0.9s infinite reverse ; 63 | } 64 | .c-translateY3 { 65 | animation: translateY3 4.15s linear 0.3s infinite reverse ; 66 | } 67 | .c-translateY4 { 68 | animation: translateY3 4.15s linear 2s infinite reverse ; 69 | } 70 | 71 | 72 | .c-bounceinup { 73 | animation: bounceInUp .8s; 74 | } 75 | 76 | .c-flipInY { 77 | animation: flipInY .8s .1s; 78 | } 79 | .c-flipInY-1 { 80 | animation: flipInY .8s .2s; 81 | } 82 | .c-flipInY-2 { 83 | animation: flipInY-2 .8s .4s; 84 | } 85 | .c-flipInY-3 { 86 | animation: flipInY .8s .6s; 87 | } 88 | .c-flipInY-4 { 89 | animation: flipInY .8s .8s; 90 | } 91 | .c-flipInY-1 { 92 | animation: flipInY .8s .5s; 93 | } 94 | 95 | @keyframes zoomIn { 96 | from { 97 | transform: scale(.9); 98 | } 99 | 100 | 50% { 101 | transform: scale(1.1); 102 | } 103 | 104 | 100% { 105 | transform: scale(.9); 106 | } 107 | } 108 | @keyframes zoomIn3 { 109 | from { 110 | transform: scale(.85); 111 | } 112 | 113 | 50% { 114 | transform: scale(1); 115 | } 116 | 117 | 100% { 118 | transform: scale(.85); 119 | } 120 | } 121 | 122 | @keyframes translateY1 { 123 | from { 124 | transform: translateY(0); 125 | } 126 | 127 | 25% { 128 | transform: translateY(5px); 129 | } 130 | 50% { 131 | transform: translateY(0px); 132 | } 133 | 75% { 134 | transform: translateY(-5px); 135 | } 136 | 100% { 137 | transform: translateY(0); 138 | } 139 | } 140 | @keyframes translateY2 { 141 | from { 142 | transform: translateY(0); 143 | } 144 | 145 | 25% { 146 | transform: translateY(8px); 147 | } 148 | 50% { 149 | transform: translateY(0px); 150 | } 151 | 75% { 152 | transform: translateY(-8px); 153 | } 154 | 100% { 155 | transform: translateY(0); 156 | } 157 | } 158 | @keyframes translateY3 { 159 | from { 160 | transform: translateX(-50%) translateY(0); 161 | } 162 | 163 | 25% { 164 | transform: translateX(-50%) translateY(10px); 165 | } 166 | 50% { 167 | transform: translateX(-50%) translateY(0px); 168 | } 169 | 75% { 170 | transform: translateX(-50%) translateY(-10px); 171 | } 172 | 100% { 173 | transform: translateX(-50%) translateY(0); 174 | } 175 | } 176 | 177 | 178 | @keyframes zoomInUp { 179 | from { 180 | opacity: 0; 181 | transform: translate(-50%,-50%) scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0); 182 | animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); 183 | } 184 | 185 | 60% { 186 | opacity: 1; 187 | transform: translate(-50%,-50%) scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0); 188 | animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); 189 | } 190 | } 191 | 192 | @keyframes zoomInDown { 193 | from { 194 | opacity: 0; 195 | transform: translate(-50%,-50%) scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0); 196 | animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); 197 | } 198 | 199 | 60% { 200 | opacity: 1; 201 | transform: translate(-50%,-50%) scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0); 202 | animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1); 203 | } 204 | } 205 | 206 | @keyframes bounceInUp { 207 | 208 | from { 209 | opacity: 0; 210 | transform: translate3d(-50%, 3000px, 0); 211 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 212 | 213 | } 214 | 215 | 60% { 216 | opacity: 1; 217 | transform: translate3d(-50%, -20px, 0); 218 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 219 | 220 | } 221 | 222 | 75% { 223 | transform: translate3d(-50%, 10px, 0); 224 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 225 | 226 | } 227 | 228 | 90% { 229 | transform: translate3d(-50%, -5px, 0); 230 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 231 | 232 | } 233 | 234 | to { 235 | transform: translate3d(-50%, 0, 0); 236 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 237 | 238 | } 239 | } 240 | 241 | 242 | @keyframes flipInY-2 { 243 | from { 244 | transform: translateX(-50%) perspective(400px) rotate3d(0, 1, 0, 90deg); 245 | animation-timing-function: ease-in; 246 | opacity: 0; 247 | } 248 | 249 | 40% { 250 | transform: translateX(-50%) perspective(400px) rotate3d(0, 1, 0, -20deg); 251 | animation-timing-function: ease-in; 252 | } 253 | 254 | 60% { 255 | transform: translateX(-50%) perspective(400px) rotate3d(0, 1, 0, 10deg); 256 | opacity: 1; 257 | } 258 | 259 | 80% { 260 | transform: translateX(-50%) perspective(400px) rotate3d(0, 1, 0, -5deg); 261 | } 262 | 263 | to { 264 | transform: translateX(-50%) perspective(400px); 265 | } 266 | } 267 | 268 | @keyframes flipInY { 269 | from { 270 | transform: perspective(400px) rotate3d(0, 1, 0, 90deg); 271 | animation-timing-function: ease-in; 272 | opacity: 0; 273 | } 274 | 275 | 40% { 276 | transform: perspective(400px) rotate3d(0, 1, 0, -20deg); 277 | animation-timing-function: ease-in; 278 | } 279 | 280 | 60% { 281 | transform: perspective(400px) rotate3d(0, 1, 0, 10deg); 282 | opacity: 1; 283 | } 284 | 285 | 80% { 286 | transform: perspective(400px) rotate3d(0, 1, 0, -5deg); 287 | } 288 | 289 | to { 290 | transform: perspective(400px); 291 | } 292 | } 293 | 294 | -------------------------------------------------------------------------------- /generators/app/templates/build/_webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | const PurgecssPlugin = require('purgecss-webpack-plugin')//净化 14 | const glob = require('glob-all') 15 | // skeleton 16 | // const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin') 17 | // const OmmitCSSPlugin = require('./ommit-css-webpack-plugin') 18 | const env = require('../config/prod.env') 19 | <% if(projectType =='MPA'){ %> 20 | // 多页面配置 21 | var glob = require('glob'); 22 | var htmls = glob.sync('./src/modules/**/*.html').map(function (item) { 23 | return new HtmlWebpackPlugin({ 24 | filename: './' + item.slice(6), // './modules/xx/xx.html' 25 | template: item, // './src/modules/**/*.html' 模板位置 26 | inject: true, 27 | chunks:[item.slice(6, -5),'vendor','manifest'] // '对应entry' 28 | }); 29 | });<% }%> 30 | const webpackConfig = merge(baseWebpackConfig, { 31 | module: { 32 | rules: utils.styleLoaders({ 33 | sourceMap: config.build.productionSourceMap, 34 | extract: true, 35 | usePostCSS: true 36 | }) 37 | }, 38 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 39 | output: { 40 | path: config.build.assetsRoot, 41 | //版本号由rd来控制 故去掉chunkhash 42 | // filename: utils.assetsPath('js/[name].[chunkhash].js'), 43 | filename: utils.assetsPath('js/[name].js'), 44 | chunkFilename: utils.assetsPath('js/[name].[chunkhash:7].js') 45 | }, 46 | plugins: [ 47 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 48 | new webpack.DefinePlugin({ 49 | 'process.env': env 50 | }), 51 | new UglifyJsPlugin({ 52 | uglifyOptions: { 53 | compress: { 54 | warnings: false, 55 | drop_console: true,//删除所有的 `console` 语句,可以兼容ie浏览器 56 | } 57 | }, 58 | sourceMap: config.build.productionSourceMap, 59 | parallel: true 60 | }), 61 | // extract css into its own file 62 | new ExtractTextPlugin({ 63 | filename: utils.assetsPath('css/[name].css'), 64 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 65 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 66 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 67 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 68 | // publicPath: config.build.cssPublicPath, 69 | allChunks: true, 70 | }), 71 | 72 | // skeleton 73 | // new SkeletonWebpackPlugin({ 74 | // webpackConfig: require('./webpack.skeleton.conf'), 75 | // quiet: true 76 | // }), 77 | // new OmmitCSSPlugin(), 78 | 79 | // Compress extracted CSS. We are using this plugin so that possible 80 | // duplicated CSS from different components can be deduped. 81 | new OptimizeCSSPlugin({ 82 | cssProcessorOptions: config.build.productionSourceMap 83 | ? { safe: true, map: { inline: false } } 84 | : { safe: true } 85 | }), 86 | // generate dist index.html with correct asset hash for caching. 87 | // you can customize output by editing /index.html 88 | // see https://github.com/ampedandwired/html-webpack-plugin 89 | <% if(projectType =='SPA'){ %> 90 | new HtmlWebpackPlugin({ 91 | filename: config.build.index, 92 | template: 'index.html', 93 | inject: true, 94 | minify: { 95 | removeComments: true, 96 | collapseWhitespace: true, 97 | removeAttributeQuotes: true 98 | // more options: 99 | // https://github.com/kangax/html-minifier#options-quick-reference 100 | }, 101 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 102 | chunksSortMode: 'dependency' 103 | }),<%}%> 104 | // keep module.id stable when vendor modules does not change 105 | new webpack.HashedModuleIdsPlugin(), 106 | // enable scope hoisting 107 | new webpack.optimize.ModuleConcatenationPlugin(), 108 | // split vendor js into its own file 109 | new webpack.optimize.CommonsChunkPlugin({ 110 | name: 'vendor', 111 | minChunks (module) { 112 | // any required modules inside node_modules are extracted to vendor 113 | return ( 114 | module.resource && 115 | /\.js$/.test(module.resource) && 116 | module.resource.indexOf( 117 | path.join(__dirname, '../node_modules') 118 | ) === 0 119 | ) 120 | } 121 | }), 122 | // extract webpack runtime and module manifest to its own file in order to 123 | // prevent vendor hash from being updated whenever app bundle is updated 124 | new webpack.optimize.CommonsChunkPlugin({ 125 | name: 'manifest', 126 | minChunks: Infinity 127 | }), 128 | // This instance extracts shared chunks from code splitted chunks and bundles them 129 | // in a separate chunk, similar to the vendor chunk 130 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 131 | new webpack.optimize.CommonsChunkPlugin({ 132 | name: 'app', 133 | async: 'vendor-async', 134 | children: true, 135 | minChunks: 3 136 | }), 137 | // css tree shake 净化 138 | new PurgecssPlugin({ 139 | paths: glob.sync(config.build.purgecssPath) 140 | }), 141 | // copy custom static assets 142 | new CopyWebpackPlugin([ 143 | { 144 | from: path.resolve(__dirname, '../static'), 145 | to: config.build.assetsSubDirectory, 146 | ignore: ['.*'] 147 | } 148 | ]) 149 | ]<% if(projectType =='MPA'){ %>.concat(htmls)<%}%> 150 | }) 151 | 152 | if (config.build.productionGzip) { 153 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 154 | 155 | webpackConfig.plugins.push( 156 | new CompressionWebpackPlugin({ 157 | asset: '[path].gz[query]', 158 | algorithm: 'gzip', 159 | test: new RegExp( 160 | '\\.(' + 161 | config.build.productionGzipExtensions.join('|') + 162 | ')$' 163 | ), 164 | threshold: 10240, 165 | minRatio: 0.8 166 | }) 167 | ) 168 | } 169 | 170 | if (config.build.bundleAnalyzerReport) { 171 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 172 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 173 | } 174 | 175 | module.exports = webpackConfig 176 | -------------------------------------------------------------------------------- /generators/app/templates/src/assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generator-easy-vue 2 | > a generator for a super vue 3 | 4 | > 此项目为开箱即用的vue项目生成器,几乎项目中用到的所有东西都一配置好,让你写代码如丝滑般顺滑~ 5 | 6 | ## 功能 7 | 8 | ``` 9 | 1. 支持可选reset.css/normalize.css 10 | 2. 支持可选 rem布局/vw布局 11 | 3. 等比例宽高容器 一像素边框等移动端解决方案 12 | 4. 支持可选less sass css预处理器 13 | 5. 动态加载路由 14 | 6. 自动注入全局组件 15 | 7. 预渲染骨架屏 16 | 8. 按需(按照指定的浏览器环境所需)引入polyfill 17 | 9. axios 简易配置 拦截器配置 全局拦截 超时提示等 18 | 10. vuex 简易配置 19 | 11. vue-router简易配置 20 | 12. build 自动压缩图片 21 | 13. 重写image publicPath 22 | 14. 跨域反向代理案例 23 | 15. 简易路由动画配置 24 | 16. 常用组件库 插件库(alert confirm loading toast) 工具库 25 | 17. 多页面应用以及一键生成页面命令 26 | 18. 注释自动生成说明文档配置 27 | 19. 增加代码检测工作流 precommit自动美化格式以及修复代码,多人写作避免代码冲突,渐进式lint代码 28 | 20. dns预加载配置 29 | 21. 动态链接库dll配置 30 | 22. css打包大小优化配置 设置全局css等 31 | 23. postcss插件配置 32 | 24. css的tree shake配置 33 | ``` 34 | 35 | **0.1.4** 36 | 37 | 增加多页面应用 38 | 39 | **0.1.8** 40 | 41 | 增加一键生成多页面应用 42 | 43 | **0.2.0** 44 | 45 | 增加jsdoc配置 自动生成说明文档 增加对.vue文件的文档解析 jsdoc jsdoc-vue 46 | 47 | **0.2.1** 48 | 49 | 1. 增加代码检测工作流 prettier统一代码格式 precommit自动美化格式以及修复代码 lint-staged 50 | 2. 多人写作避免代码冲突,渐进式lint代码 51 | 3. 如果想要启用官方推荐的standard 可在.eslintrc.js中开启 52 | 53 | **0.2.2** 54 | 55 | 开启可配置dns预解析 56 | 57 | **0.2.3** 58 | 59 | 开启动态链接库dll并自动插入到模板中新增插件 DllPlugin,DllReferencePlugin,html-webpack-include-assets-plugin,cross-env 60 | 动态链接库修改在config/dll.lib.dependencies.js 默认只用了vue,vue-router 61 | 62 | **0.3.0** 63 | 64 | 实战之后的优化以及埋坑 65 | 66 | 1. css打包大小优化 67 | 2. 常用css方案(渐变兼容IE9 一像素边框 二倍图三倍图 iphoneX适配 等) 68 | 3. axios配置优化 69 | 4. 增加常见组件库 插件库 工具库 70 | ps: 非常棒的preload prefetch and JavaScript源代码优化工具prepack 相关插件不支持webpack3.X 💔 后期考虑升级webpack4+ or在vue-cli3基础上重新配置... 71 | 72 | **0.3.3** 73 | 74 | 1. 增加测试环境:开发环境接口 -> **mock**,测试环境走 ->**proxy**,生产环境直接打包线上全路径 75 | 2. 增加eventBus事件总线,挂载vue 全局调用 76 | 3. 修改loading插件样式 77 | 4. 请求增加超时提示,超时时间为10s 78 | 5. vw布局插件升级,支持排除node_momdules插件包,rem布局暂不支持。可换插件postcss-px2rem-exclude or px2rem-exclude 79 | 80 | **0.3.9** 81 | 82 | 路由动画 router zIndex控制层级 83 | 84 | **0.4.0** 85 | 86 | - css tree shake净化css, config/index配置purgecssPath 87 | - postcss插件替换 postcss-preset-env替代cssnext 默认stage:2 88 | 89 | > 初始化项目 90 | 91 | 92 | 93 | > 初始化骨架屏 94 | 95 | 96 | 97 | > 自动生成页面 98 | 99 | 100 | 101 | > 骨架屏效果 102 | 103 | 104 | 105 | ## Installation 106 | 107 | ```bash 108 | npm install -g yo 109 | npm install -g generator-easy-vue 110 | ``` 111 | 112 | Then generate your new project: 113 | 114 | ```base 115 | yo easy-vue 一键生成可配置项目 116 | # install dependencies 117 | npm install or yarn install 118 | 119 | # 生成骨架屏(仅需要运行一次!) 120 | npm run skeleton 121 | 122 | # 生成开发环境下dll 123 | npm run dll:dev 124 | 125 | # 生成生产环境下dll 126 | npm run dll:build 127 | 128 | # eslint 代码检测 129 | npm run eslint 130 | 131 | # eslint 代码修复 132 | npm run eslint:fix 133 | 134 | # 代码格式化 (多人协作不建议使用,git commit 自动美化 eslint:fix自己修改部分的代码) 135 | npm run prettier 136 | 137 | #生成代码文档(jsdoc格式注释代码) 138 | npm run jsdoc 139 | 140 | # serve with hot reload at localhost:8080(自动 dll开发环境代码,并insert到html模板) 141 | npm run dev or npm run start 142 | 143 | # rebuild钩子生成盛传环境dll 并eslint 代码 144 | npm run prebuild 145 | 146 | # build for production with minification(自动 dll生成环境代码,并insert到html模板已配置好publicPath) 147 | npm run build 148 | 149 | # build for production and view the bundle analyzer report 150 | npm run build --report 151 | 152 | #配置 publicPath imagePublicPath 或修改 config/index.js 153 | yo easy-vue:imagePublicPath path... 154 | yo easy-vue:publicPath path... 155 | 156 | # 一键生成页面 157 | yo easy-vue:page pageName 一键生成page 158 | yo easy-vue:mpage pageName 一键生成mutil-page(if you choose MPA) 159 | ``` 160 | 161 | 162 | ## 项目相关依赖 163 | 164 | > dependencies 165 | 166 | ``` 167 | fastclick 168 | amfe-flexible 169 | axios 170 | vuex 171 | vue 172 | vue-router 173 | ``` 174 | > devDependencies 175 | 176 | ``` 177 | less 178 | less-loader 179 | node-sass 180 | sass-loader 181 | postcss-pxtorem 182 | image-webpack-plugin 183 | vue-server-renderer 184 | vue-skeleton-webpack-plugin 185 | postcss-aspect-ratio-mini 186 | postcss-px-to-viewport 187 | postcss-write-svg 188 | postcss-cssnext 189 | style-resources-loader 190 | postcss-viewport-units 已弃用 191 | style 192 | cssnano 193 | cssnano-preset-advanced 194 | babel-eslint eslint eslint-config-standard eslint-friendly-formatter eslint-loader eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard eslint-plugin-vue 195 | lint-staged 196 | husky 197 | prettier 198 | jsdoc 199 | jsdoc-vue 200 | DllPlugin 201 | DllReferencePlugin 202 | html-webpack-include-assets-plugin 203 | cross-env 204 | .... 205 | ``` 206 | 207 | 208 | ## 目录结构 209 | 210 | ``` 211 | . 212 | ├── build // 启动文件 213 | │ ├── build 214 | │ ├── check-versions.js 215 | │ ├── logo.png 216 | │ ├── commit-css-webpack-plugin.js //骨架屏相关插件 217 | │ ├── utils.js 218 | │ ├── vue-loader.conf.js 219 | │ ├── webpack.base.conf.js 220 | │ ├── webpack.dev.conf.js 221 | │ ├── webpack.prod.conf.js 222 | │ ├── webpack.test.conf.js 223 | │ ├── webpack.dll.conf.js //dll动态连接库 224 | │ ├── webpack.skeleton.conf.js //生成骨架屏 225 | ├── config // 项目环境配置 226 | │ ├── index.js // 入口 227 | │ ├── dll.libs.dependencies.js // dll所需库配置 228 | │ ├── dev.env.js // 开发环境配置 229 | │ ├── prod.env.js // 生成环境配置 230 | │ ├── test.env.js // 测试环境配置 231 | ├── dist // 上线打包 232 | │ ├── modules // 多页面 233 | │ ├── static 234 | │ │ ├── css 235 | │ │ ├── js 236 | │ │ └── libs //动态链接库文件 237 | ├── jsdoc // jsdoc 注释自动生成文档 238 | ├── src // 源码目录 239 | │ ├── assets 240 | │ ├── components 241 | │ ├── config 242 | │ │ ├── apis.js //封装接口 243 | │ │ ├── axiosConfig.js //配置axios 244 | │ │ ├── env.js //根据环境配置 切换接口baseURL && history模式路由配置 245 | │ ├── mixins 246 | │ ├── modules //多页面 247 | │ ├── pages 248 | │ ├── plugin //手写vue插件 249 | │ ├── filters //过滤器 250 | │ ├── direction //directions 251 | │ ├── router 252 | │ ├── store // 初始化vuex 253 | │ ├── eventBus // 事件总线 254 | │ ├── utils 255 | │ ├── APP.vue 256 | │ ├── mian.js 257 | │ ├── skeleton.entry.js //骨架屏配置 258 | ├── static 259 | ├── .babelrc 260 | ├── .editorconfig 261 | ├── eslintrc.js 262 | ├── .eslintignore 263 | ├── .gitignore 264 | ├── postcssrc.js 265 | ├── index.html 266 | ├── jsdoc.cong.json //jsdoc配置文件 267 | ├── .index 268 | ├── package.json 269 | ├── package-lock.json 270 | └── README.md 271 | ``` 272 | 273 | 274 | ## vue项目生成器 275 | 276 | > 地址[项目生成器](https://github.com/501981732/generator-easy-vue) 277 | 278 | 279 | 280 | ## License 281 | 282 | MIT © [wangmeng](https://github.com/501981732) 283 | 284 | 285 | [npm-image]: https://badge.fury.io/js/generator-easy-vue.svg 286 | [npm-url]: https://npmjs.org/package/generator-easy-vue 287 | [travis-image]: https://travis-ci.org/501981732/generator-easy-vue.svg?branch=master 288 | [travis-url]: https://travis-ci.org/501981732/generator-easy-vue 289 | [daviddm-image]: https://david-dm.org/501981732/generator-easy-vue.svg?theme=shields.io 290 | [daviddm-url]: https://david-dm.org/501981732/generator-easy-vue 291 | [coveralls-image]: https://coveralls.io/repos/501981732/generator-easy-vue/badge.svg 292 | [coveralls-url]: https://coveralls.io/r/501981732/generator-easy-vue 293 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* eslint-disable */ 3 | const Generator = require('yeoman-generator'); 4 | const chalk = require('chalk'); 5 | const yosay = require('yosay'); 6 | const path = require('path'); 7 | const util = require('util'); 8 | 9 | /** 10 | * 生命周期 11 | *prompting - 接收用户输入阶段 12 | *configuring - 保存配置信息和文件 13 | *default - 自定义功能函数名称,如 method1 14 | *writing - 生成项目目录结构阶段 15 | *conflicts - 统一处理冲突,如要生成的文件已经存在是否覆盖等处理 16 | *install - 安装依赖阶段 17 | *end - 生成器结束阶段 18 | */ 19 | module.exports = class extends Generator { 20 | // constructor(args,opts) { 21 | // super(args,opts) 22 | // this.appname = 'xxx' 23 | // } 24 | prompting() { 25 | this.log( 26 | yosay(`Welcome to the ${chalk.red('easy-vue')} generator! ${chalk.red('多功能搬砖器')}`) 27 | ); 28 | 29 | const prompts = [{ 30 | name: 'projectName', // 用户输入项标识,在获取用户输入值的时候用到 31 | message: 'your project name?', //给用户提示的信息 32 | // default: this.appname // 默认值 33 | default: path.basename(process.cwd()) // 默认值 34 | }, { 35 | type: 'text', 36 | name: 'author', 37 | message: 'Your name', 38 | default: this.user.git.name() 39 | }, { 40 | type: 'input', 41 | name: 'email', 42 | message: 'Your email', 43 | default: this.user.git.email() 44 | }, { 45 | name: 'projectType', 46 | type: 'list', 47 | message: 'Which type application do you want to build ?', 48 | choices: [{ 49 | name: 'SPA', 50 | value: 'SPA', 51 | checked: true 52 | }, { 53 | name: 'MPA', 54 | value: 'MPA' 55 | }, ] 56 | }, { 57 | name: 'reset', 58 | type: 'list', 59 | message: 'Choose a way to reset your css', 60 | choices: [{ 61 | name: 'reset.css', 62 | value: 'reset.css', 63 | checked: true 64 | }, { 65 | name: 'normalize.css', 66 | value: 'normalize.css' 67 | }, { 68 | name: 'none', 69 | value: 'none' 70 | }] 71 | }, { 72 | name: 'cssPrepeocessor', 73 | type: 'list', 74 | message: 'Choose a CSS preprocessor', 75 | choices: [{ 76 | name: 'less', 77 | value: 'less', 78 | checked: true 79 | }, { 80 | name: 'sass', 81 | value: 'scss' 82 | }] 83 | }, { 84 | name: 'layout', 85 | type: 'list', 86 | message: 'Choose a layout scheme if you need a M project', 87 | choices: [{ 88 | name: 'rem', 89 | value: 'rem', 90 | checked: true 91 | }, { 92 | name: 'vw', 93 | value: 'vw' 94 | }, { 95 | name: 'none', 96 | value: 'none' 97 | }] 98 | }, { 99 | type: 'confirm', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 100 | name: 'skeleton', 101 | message: 'Do you need a initialized skeleton ?', 102 | default: true 103 | }, { 104 | type: 'confirm', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 105 | name: 'vuex', 106 | message: 'Do you need a initialized vuex ?', 107 | default: true, 108 | store: true 109 | }, { 110 | type: 'confirm', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 111 | name: 'eslint', 112 | message: 'Use eslint and prettier to lint your code?', 113 | default: true, 114 | store: true 115 | }, { 116 | type: 'text', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 117 | name: 'imagePublicPath', 118 | message: 'Do you need a imagePublicPath ?', 119 | store: true //本地保留配置 120 | }, { 121 | type: 'input', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 122 | name: 'publicPath', 123 | message: 'Do you need a assetsPublicPath ?', 124 | store: true 125 | }, { 126 | type: 'input', // 非必填 默认text ‘confirm’ 是选择输入‘YES/NO’ 127 | name: 'dns', 128 | message: 'If you need a dns prefetch,Please input the dns split by ","', 129 | store: true 130 | }, { 131 | name: 'installationMethod', 132 | type: 'list', 133 | message: 'Choose a way to install the package:', 134 | choices: [{ 135 | name: 'yarn', 136 | value: 'yarn', 137 | checked: true 138 | }, { 139 | name: 'npm', 140 | value: 'npm' 141 | }, { 142 | name: 'no I install packages by myself', 143 | value: 'no' 144 | }] 145 | }]; 146 | 147 | return this.prompt(prompts).then(props => { 148 | // To access props later use this.props.xxx; 149 | this.props = props; 150 | }); 151 | } 152 | 153 | writing() { 154 | // this.templatePath 返回template目录下文件的地址 155 | // this.destinationPath 指定加工完成后文件的存放地址 一般指项目目录 156 | // this.fs.copy 把文件从一个目录复制到另一个目录,一般是从template目录复制到你所指定的项目目录,用于固定文件和可选文件(根据用户选择) 157 | // this.fs.copyTpl:和上面的函数作用一样,不过会事先经过模板引擎的处理,一般用来根据用户输入处理加工文件 158 | let target = [ 159 | //需要加工的文件使用数组 160 | 'build/build.js', 161 | 'build/check-versions.js', 162 | 'build/logo.png', 163 | 'build/vue-loader.conf.js', 164 | 'config/dev.env.js', 165 | 'config/prod.env.js', 166 | 'src/assets/css/common.css', 167 | 'src/assets/css/animation.css', 168 | 'src/assets/js/.gitkeep', 169 | 'src/assets/img/', 170 | 'src/assets/logo.png', 171 | 'src/components/global.js', 172 | 'src/components/Alert.vue', 173 | 'src/components/Loading.vue', 174 | 'src/components/Toast.vue', 175 | 'src/components/Confirm.vue', 176 | 'src/config', 177 | 'src/directions/', 178 | 'src/filters/', 179 | 'src/mixins/', 180 | 'src/plugins/', 181 | 'src/router/', 182 | 'src/utils/', 183 | 'static/.gitkeep', 184 | '.babelrc', 185 | '.editorconfig', 186 | 'README.md', 187 | 'yarn.lock', 188 | 'jsdoc.conf.json', 189 | 'package-lock.json', 190 | 'build/webpack.dll.conf.js', 191 | 'CHANGELOG.md', 192 | 'config/dll.libs.dependencies.js', 193 | 'config/test.env.js',//增加测试环境 194 | 'src/eventBus',//增加事件总线 195 | ] 196 | 197 | // fix linux 添加隐藏文件 198 | target = [...target, ['_gitignore', '.gitignore'], 199 | ['_package.json', 'package.json'], 200 | ['_postcssrc.js', '.postcssrc.js'], 201 | ['config/_index.js', 'config/index.js'], 202 | ['_index.html', 'index.html'], 203 | // ['src/components/_x-alert.vue', 'src/components/x-alert.vue'], 204 | ['src/pages/_HelloWorld.vue', 'src/pages/HelloWorld.vue'], 205 | ['build/_webpack.base.conf.js', 'build/webpack.base.conf.js'], 206 | ['build/_webpack.dev.conf.js', 'build/webpack.dev.conf.js'], 207 | ['build/_webpack.prod.conf.js', 'build/webpack.prod.conf.js'], 208 | ['build/_webpack.test.conf.js', 'build/webpack.test.conf.js'],////增加测试环境 209 | ['build/_utils.js', 'build/utils.js', ], 210 | ] 211 | // 是否添加单元测试 212 | // this.props.test && target.push('test/index.spec.js') 213 | this.props.reset == 'reset.css' && target.push('src/assets/css/reset.css') 214 | this.props.reset == 'normalize.css' && target.push('src/assets/css/normalize.css') 215 | // less处理 全局引入 216 | this.props.cssPrepeocessor === 'less' && target.push('src/assets/css/vars.less','src/assets/css/mixins.less') 217 | this.props.layout == 'vw' && target.push('src/components/vwDemo.vue') 218 | this.props.vuex && target.push('src/store') 219 | this.props.skeleton && target.push('build/webpack.skeleton.conf.js', 220 | 'src/components/skeleton.vue', 221 | 'src/skeleton.entry.js', 222 | 'skeleton.js') 223 | // 配置多页面 224 | this.props.projectType == 'SPA' && target.push(['src/_main.js', 'src/main.js'], 'src/App.vue', ) 225 | this.props.projectType == 'MPA' && target.push('src/modules/index/index.html', 'src/modules/index/index.vue', ['src/modules/index/_index.js', 'src/modules/index/index.js']) 226 | // eslint 227 | this.props.eslint && target.push('.eslintignore', '.eslintrc.js') 228 | 229 | // dns 230 | // if (this.props.dns) { 231 | // this.props.dnsArray = this.props.split(',') 232 | // } else { 233 | // this.props.dnsArray = [] 234 | // } 235 | 236 | target.forEach(item => { 237 | let toFile, fromFile; 238 | if (Array.isArray(item)) { 239 | fromFile = item[0]; 240 | toFile = item[1]; 241 | this.fs.copyTpl( 242 | this.templatePath(fromFile), 243 | this.destinationPath(toFile), 244 | // 将配置参数带过去 245 | this.props 246 | ); 247 | } else { 248 | fromFile = item; 249 | toFile = item; 250 | this.fs.copy( 251 | this.templatePath(fromFile), 252 | this.destinationPath(toFile), 253 | this.props 254 | ); 255 | } 256 | }) 257 | } 258 | install() { 259 | switch (this.props.installationMethod) { 260 | case 'npm': 261 | this.npmInstall(); 262 | break; 263 | case 'yarn': 264 | this.yarnInstall(); 265 | break; 266 | case 'bower': 267 | this.bowerInstall(); 268 | break; 269 | default: 270 | break; 271 | } 272 | 273 | } 274 | end() { 275 | this.log(yosay(`${chalk.green('Now do something ')}${chalk.blue(' whatever you want!!')} `)) 276 | } 277 | }; 278 | -------------------------------------------------------------------------------- /generators/app/templates/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 平滑滚动顶部 3 | * @author wm 4 | * @DateTime 2018-09-29 5 | */ 6 | 7 | const scrollToTop = () => { 8 | const c = document.documentElement.scrollTop || document.body.scrollTop; 9 | if (c > 0) { 10 | window.requestAnimationFrame(scrollToTop); 11 | window.scrollTo(0, c - c / 8); 12 | } 13 | }; 14 | /** 15 | * 滚动 16 | * @Author wangmeng 17 | * @DateTime 2019-02-22 18 | * @param {object} element dom 19 | * @param {number} speed speed 20 | */ 21 | const animateScroll = (element, speed) => { 22 | let rect = element.getBoundingClientRect(); 23 | //获取元素相对窗口的top值,此处应加上窗口本身的偏移 24 | let top = window.pageYOffset + rect.top; 25 | let currentTop = 0; 26 | let requestId; 27 | //采用requestAnimationFrame,平滑动画 28 | function step(timestamp) { 29 | currentTop += speed; 30 | if (currentTop <= top) { 31 | window.scrollTo(0, currentTop); 32 | requestId = window.requestAnimationFrame(step); 33 | } else { 34 | window.cancelAnimationFrame(requestId); 35 | } 36 | } 37 | window.requestAnimationFrame(step); 38 | }; 39 | /** 40 | * 验证邮箱 41 | * @author wm 42 | * @DateTime 2018-09-05 43 | * @param {string} str 验证的邮箱 44 | * @returns {boolean} 是否是邮箱 45 | */ 46 | const validateEmail = str => 47 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( 48 | str 49 | ); 50 | 51 | /** 52 | * 验证数字 53 | * @author wm 54 | * @DateTime 2018-09-05 55 | * @param {string} n 验证的数字 56 | * @returns {boolean} 是否是数据 57 | */ 58 | const validateNumber = n => 59 | !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; 60 | 61 | /** 62 | * 验证电话 63 | * @author wm 64 | * @DateTime 2018-09-05 65 | * @param {string} n 验证的电话 66 | * @returns {boolean} 是否是电话号码 67 | */ 68 | 69 | const validataPhone = n => /^[1][3,4,5,7,8,9][0-9]{9}$/.test(n); 70 | 71 | /** 72 | * 验证固定电话 73 | * @author wm 74 | * @DateTime 2018-09-05 75 | * @param {string} n 验证的固定电话 76 | * @returns {boolean} 是否是固定电话 77 | */ 78 | 79 | const validataFixedLineTelephone = n => /\d{2,5}-\d{7,8}/.test(n); 80 | 81 | /** 82 | * 判断平台 pc 83 | * @author wm 84 | * @returns {string} 平台类型 85 | */ 86 | 87 | const validataOS = () => { 88 | if (navigator.userAgent.indexOf("Window") > 0) { 89 | return "Windows"; 90 | } else if (navigator.userAgent.indexOf("Mac OS X") > 0) { 91 | return "Mac"; 92 | } else if (navigator.userAgent.indexOf("Linux") > 0) { 93 | return "Linux"; 94 | } else { 95 | return "NUll"; 96 | } 97 | }; 98 | const isWeiXin = () => { 99 | var ua = window.navigator.userAgent.toLowerCase(); 100 | if ( 101 | ua.match(/MicroMessenger/i) && 102 | ua.match(/MicroMessenger/i)[0] === "micromessenger" 103 | ) { 104 | return true; 105 | } else { 106 | return false; 107 | } 108 | }; 109 | let inBrowser = typeof window !== "undefined"; 110 | let UA = inBrowser && window.navigator.userAgent.toLowerCase(); 111 | 112 | /** 113 | *是否是android平台 114 | */ 115 | const isAndroid = UA && UA.indexOf("android") > 0; 116 | 117 | /** 118 | * 是否是ios平台 119 | */ 120 | const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); 121 | 122 | /** 123 | * 获取滚动条位置 124 | * @return {Number} 滚动条位置 125 | */ 126 | const getScrollTop = _ => { 127 | let scrollTop = 0; 128 | if (document.documentElement && document.documentElement.scrollTop) { 129 | scrollTop = document.documentElement.scrollTop; 130 | } else if (document.body) { 131 | scrollTop = document.body.scrollTop; 132 | } 133 | return scrollTop; 134 | }; 135 | 136 | /** 137 | * 获取可视范围高度 138 | * @return {Number} 可视范围高度 139 | */ 140 | const getClientHeight = _ => { 141 | let clientHeight = 0; 142 | if (document.body.clientHeight && document.documentElement.clientHeight) { 143 | clientHeight = Math.min( 144 | document.body.clientHeight, 145 | document.documentElement.clientHeight 146 | ); 147 | } else { 148 | clientHeight = Math.max( 149 | document.body.clientHeight, 150 | document.documentElement.clientHeight 151 | ); 152 | } 153 | return clientHeight; 154 | }; 155 | /** 156 | * 获取文档完整的高度 157 | * @return {Number} 文档高度 158 | */ 159 | const getScrollHeight = _ => { 160 | return Math.max( 161 | document.body.scrollHeight, 162 | document.documentElement.scrollHeight 163 | ); 164 | }; 165 | /** 166 | * 距离第二天的倒计时 167 | * @return {String} 距离第二天的时间 168 | */ 169 | const timeToNextDay = _ => { 170 | // 现在距离 1970-1-1 毫秒数 171 | let d = new Date(); 172 | let now = d.getTime(); 173 | // 今天凌晨距离 1970-1-1 毫秒数 174 | let today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); 175 | let tomorrow = today + 24 * 60 * 60 * 1000; 176 | let dis = (tomorrow - now) / 1000; 177 | let h = parseInt(dis / (60 * 60)); 178 | let m = parseInt((dis / 60) % 60); 179 | let s = parseInt(dis % 60); 180 | return `${h}时${m}分${formatTime(s)}秒`; 181 | }; 182 | 183 | const formatTime = s => { 184 | return Number(s) >= 10 ? s : "0" + s; 185 | }; 186 | 187 | /** 188 | * 获取某个cookie 189 | * @param {string} name cookie的key 190 | * @return {string} cookie的value 191 | */ 192 | const getCookie = name => { 193 | let arr; 194 | let reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); 195 | if ((arr = document.cookie.match(reg))) { 196 | return unescape(arr[2]).replace(/\"/g, ""); //替换引号 197 | } else { 198 | return null; 199 | } 200 | }; 201 | // const getCookie =(cname) => { 202 | // var name = cname + "="; 203 | // var ca = document.cookie.split(';'); 204 | // for (var i = 0; i < ca.length; i++) { 205 | // var c = ca[i].trim(); 206 | // if (c.indexOf(name) == 0) return c.substring(name.length, c.length); 207 | // } 208 | // return ""; 209 | // } 210 | 211 | /** 212 | * 删除cookie 213 | * @param name cookie的名称 214 | */ 215 | const delCookie = name => { 216 | setCookie(name, " ", -1); 217 | }; 218 | 219 | /** 220 | * 判断页面登录 221 | * @Author wangmeng 222 | * @DateTime 2019-01-08 223 | */ 224 | const getPPU = _ => { 225 | return getCookie("PPU"); 226 | }; 227 | 228 | /** 229 | * M端登录 230 | * @Author wangmeng 231 | * @DateTime 2019-01-08 232 | * @param url 登录成功后回跳页面 233 | */ 234 | const H5Login = url => { 235 | let href = window.location.href; 236 | location.href = `//m.m.58.com/login/?path=${url || href}`; 237 | }; 238 | 239 | /** 240 | * 点击复制到剪切板中 使用execCommand API 241 | * https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript/30810322#30810322 242 | * @param {string} text 要复制的内容 243 | * @return {promise} new promise 244 | */ 245 | 246 | const fallbackCopyTextToClipboard = text => { 247 | let textArea = document.createElement("textarea"); 248 | let root = document.getElementById("app"); 249 | textArea.value = text; 250 | // document.body.appendChild(textArea); 251 | document.body.insertBefore(textArea, root); 252 | textArea.focus(); 253 | textArea.select(); 254 | textArea.setSelectionRange(0, text.length); //不设置的话ios 会有问题 255 | return new Promise((reslove, reject) => { 256 | try { 257 | let success = document.execCommand("copy"); 258 | let msg = success ? "success" : "unsuccess"; 259 | reslove(msg); 260 | } catch (err) { 261 | reject(err); 262 | } 263 | document.body.removeChild(textArea); 264 | }); 265 | }; 266 | 267 | /** 268 | * 点击复制到剪切板中 使用 navigator.clipboard.writeText(text) 269 | * @param {string} text 要复制的内容 270 | * @return {promise} new promise 271 | */ 272 | 273 | const copyTextToClipboard = text => { 274 | if (!navigator.clipboard) { 275 | return fallbackCopyTextToClipboard(text); 276 | } 277 | return new Promise((reslove, reject) => { 278 | navigator.clipboard.writeText(text).then( 279 | function() { 280 | reslove("success"); 281 | }, 282 | function(err) { 283 | reject(err); 284 | } 285 | ); 286 | }); 287 | }; 288 | 289 | /** 290 | * 函数防抖 291 | * @param {function} func 需要防抖的函数 292 | * @param {string} wait 时间 293 | * @return {function} 防抖函数 294 | */ 295 | const simpleDebounce = (func, wait) => { 296 | let timer; 297 | return _ => { 298 | timer && clearTimeout(timer); 299 | timer = setTimeout(func, wait); 300 | }; 301 | }; 302 | /** 303 | * 函数防抖 304 | * @param {function} func 需要防抖的函数 305 | * @param {string} wait 时间 306 | * @param {bollean} immediate 是否立即执行 307 | * @return {function} 防抖函数 308 | */ 309 | const debounce = (func, wait, immediate) => { 310 | var timeout, result; 311 | 312 | return function() { 313 | var context = this; 314 | var args = arguments; 315 | 316 | if (timeout) clearTimeout(timeout); 317 | if (immediate) { 318 | // 如果已经执行过,不再执行 319 | var callNow = !timeout; 320 | timeout = setTimeout(function() { 321 | timeout = null; 322 | }, wait); 323 | if (callNow) result = func.apply(context, args); 324 | } else { 325 | timeout = setTimeout(function() { 326 | result = func.apply(context, args); 327 | }, wait); 328 | } 329 | 330 | return result; 331 | }; 332 | }; 333 | 334 | /** 335 | * 函数节流 336 | * @param {function} func 需要节流的函数 337 | * @param {strimg} wait 节流函数 338 | * @return {function} 节流的函数 339 | */ 340 | const throttle = (func, wait) => { 341 | if (timer) return; 342 | return () => { 343 | timer = setTimeout(_ => { 344 | func(); 345 | timer = null; 346 | }, wait); 347 | }; 348 | }; 349 | 350 | // const throttle2 = (func, wait) => { 351 | // let last = 0 352 | // return () => { 353 | // let current_time = +new Date() 354 | // if (current_time - last > wait) { 355 | // func.apply(this, arguments) 356 | // last = +new Date() 357 | // } 358 | // } 359 | // } 360 | 361 | /** 362 | * 解析url参数 363 | * @example ?id=12345&a=b 364 | * @return {object} {id:12345, a:b} 365 | * */ 366 | const urlParse = () => { 367 | let url = window.location.search; 368 | let obj = {}; 369 | let reg = /[?&][^?&]+=[^?&]+/g; 370 | let arr = url.match(reg); 371 | if (arr) { 372 | arr.forEach(item => { 373 | let tempArr = item.substr(1).split("="); 374 | let key = decodeURIComponent(tempArr[0]); 375 | let val = decodeURIComponent(tempArr[1]); 376 | obj[key] = val; 377 | }); 378 | } 379 | return obj; 380 | }; 381 | 382 | /** 383 | * 根据对象某属性排序 384 | * @Author wangmeng 385 | * @DateTime 2019-01-03 386 | * @param {String} prop 对象属性 387 | * @param {Boolean} boolean 排序规则 388 | */ 389 | const sortBy = (prop, boolean = true) => { 390 | let flag = boolean ? 1 : -1; 391 | return function(a, b) { 392 | return a[prop] < b[prop] ? flag * -1 : flag; 393 | }; 394 | }; 395 | 396 | /** 397 | * 缓存函数计算的结果 仅支持一个参数传递 398 | * @Author wangmeng 399 | * @DateTime 2019-02-11 400 | * @param {Function} fn 要缓存的函数 401 | * @return {Function} 缓存后的函数 402 | * @useage const cachedComputed = cached(funcA); cachedComputed(1) 403 | */ 404 | 405 | const cached = fn => { 406 | const cache = Object.create(null); 407 | return function cachedFn(str) { 408 | if (!cache[str]) { 409 | cache[str] = fn(str); 410 | } 411 | return cache[str]; 412 | }; 413 | }; 414 | 415 | export { 416 | scrollToTop, 417 | validateEmail, 418 | validateNumber, 419 | validataPhone, 420 | validataFixedLineTelephone, 421 | validataOS, 422 | isWeiXin, 423 | isAndroid, 424 | isIOS, 425 | getScrollTop, 426 | getClientHeight, 427 | getScrollHeight, 428 | timeToNextDay, 429 | getCookie, 430 | getPPU, 431 | H5Login, 432 | isAPP, 433 | copyTextToClipboard, 434 | simpleDebounce, 435 | debounce, // 节流 436 | throttle, // 防抖 437 | // throttle2, //防抖 438 | urlParse, 439 | sortBy, 440 | animateScroll 441 | }; 442 | -------------------------------------------------------------------------------- /generators/app/templates/dist/skeleton.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": "skeleton.js", 3 | "files": { 4 | "skeleton.js": "module.exports =\n/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"/dist/\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_vue__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_vue___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_vue__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_skeleton_vue__ = __webpack_require__(2);\n\n\n\nconsole.log('Skeleton...')\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (new __WEBPACK_IMPORTED_MODULE_0_vue___default.a({\n components: {\n skeleton: __WEBPACK_IMPORTED_MODULE_1__components_skeleton_vue__[\"a\" /* default */]\n },\n template: ''\n}));\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports) {\n\nmodule.exports = require(\"vue\");\n\n/***/ }),\n/* 2 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__node_modules_vue_loader_lib_template_compiler_index_id_data_v_740ad5dd_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_skeleton_vue__ = __webpack_require__(9);\nfunction injectStyle (ssrContext) {\nvar i\n;(i=__webpack_require__(3),i.__inject__&&i.__inject__(ssrContext),i)\n}\nvar normalizeComponent = __webpack_require__(8)\n/* script */\nvar __vue_script__ = null\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = \"ee7e1cbe\"\nvar Component = normalizeComponent(\n __vue_script__,\n __WEBPACK_IMPORTED_MODULE_0__node_modules_vue_loader_lib_template_compiler_index_id_data_v_740ad5dd_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_skeleton_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/skeleton.vue\"\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a '\n }\n return css\n}\n\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports) {\n\n/**\n * Translates the list format produced by css-loader into something\n * easier to manipulate.\n */\nmodule.exports = function listToStyles (parentId, list) {\n var styles = []\n var newStyles = {}\n for (var i = 0; i < list.length; i++) {\n var item = list[i]\n var id = item[0]\n var css = item[1]\n var media = item[2]\n var sourceMap = item[3]\n var part = {\n id: parentId + ':' + i,\n css: css,\n media: media,\n sourceMap: sourceMap\n }\n if (!newStyles[id]) {\n styles.push(newStyles[id] = { id: id, parts: [part] })\n } else {\n newStyles[id].parts.push(part)\n }\n }\n return styles\n}\n\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports) {\n\n/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file.\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nmodule.exports = function normalizeComponent (\n rawScriptExports,\n compiledTemplate,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */\n) {\n var esModule\n var scriptExports = rawScriptExports = rawScriptExports || {}\n\n // ES6 modules interop\n var type = typeof rawScriptExports.default\n if (type === 'object' || type === 'function') {\n esModule = rawScriptExports\n scriptExports = rawScriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (compiledTemplate) {\n options.render = compiledTemplate.render\n options.staticRenderFns = compiledTemplate.staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = injectStyles\n }\n\n if (hook) {\n var functional = options.functional\n var existing = functional\n ? options.render\n : options.beforeCreate\n\n if (!functional) {\n // inject component registration as beforeCreate hook\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n } else {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return existing(h, context)\n }\n }\n }\n\n return {\n esModule: esModule,\n exports: scriptExports,\n options: options\n }\n}\n\n\n/***/ }),\n/* 9 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"skeleton page\" }, [\n _vm._ssrNode(\n '
    ' +\n _vm._ssrList(8, function(i) {\n return '
  • '\n }) +\n '
' +\n _vm._ssrList(6, function(i) {\n return '
'\n })\n )\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ })\n/******/ ]);" 5 | }, 6 | "maps": {} 7 | } --------------------------------------------------------------------------------