├── static └── .gitkeep ├── src ├── store │ ├── getters.js │ ├── mutation-types.js │ ├── actions.js │ ├── mutations.js │ └── index.js ├── mixins │ ├── index.js │ ├── analysis.js │ └── storage.js ├── style │ ├── index.scss │ └── reset.scss ├── App.vue ├── router │ ├── routes.js │ └── index.js ├── utils │ ├── mint-ui.js │ ├── flexible.js │ └── index.js ├── api │ └── axios.js ├── main.js ├── filters │ └── index.js └── components │ └── Demo.vue ├── .eslintignore ├── test ├── unit │ ├── setup.js │ ├── .eslintrc │ └── specs │ │ └── HelloWorld.spec.js └── e2e │ ├── specs │ └── test.js │ ├── custom-assertions │ └── elementCount.js │ ├── nightwatch.conf.js │ └── runner.js ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── .editorconfig ├── .gitignore ├── index.html ├── .postcssrc.js ├── .babelrc ├── .eslintrc.js ├── README.md └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mixins/index.js: -------------------------------------------------------------------------------- 1 | import './analysis' 2 | import './storage' 3 | -------------------------------------------------------------------------------- /src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const EXAMPLE_MUTATION = 'EXAMPLE_MUTATION' 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | const actions = { 2 | 3 | } 4 | 5 | export default actions 6 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-types' 2 | 3 | const mutations = { 4 | [types.EXAMPLE_MUTATION] (state, payload) { 5 | state.foo += payload 6 | }, 7 | } 8 | 9 | export default mutations 10 | -------------------------------------------------------------------------------- /src/style/index.scss: -------------------------------------------------------------------------------- 1 | @import "./reset.scss"; 2 | 3 | .fade-enter-active, .fade-leave-active { 4 | transition: opacity .3s ease; 5 | } 6 | .fade-enter, .fade-leave-to /* .fade-leave-active 在 <2.1.8 中 */ { 7 | opacity: 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | .cache 11 | fabfile.pyc 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | function lazyLoading (path) { 2 | return () => import(`@/components/${path}`) 3 | } 4 | 5 | const routes = [ 6 | { 7 | path: '/demo', 8 | name: 'Demo', 9 | meta: {title: 'Demo Page'}, 10 | component: lazyLoading('Demo'), 11 | }, 12 | { 13 | path: '*', 14 | redirect: '/demo', 15 | }, 16 | ] 17 | 18 | export default routes 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | H5 template 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "postcss-nested": {}, 8 | "postcss-mixins": {}, 9 | "postcss-px2rem": { 10 | remUnit: 37.5 11 | }, 12 | "autoprefixer": {} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false 5 | }], 6 | "stage-0" 7 | ], 8 | "plugins": [ 9 | "transform-runtime", 10 | ["component", [ 11 | { 12 | "libraryName": "mint-ui", 13 | "style": true 14 | } 15 | ] 16 | ] 17 | ], 18 | "env": { 19 | "test": { 20 | "presets": ["env", "stage-2"] } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import mutations from './mutations' 4 | import actions from './actions' 5 | import getters from './getters' 6 | 7 | Vue.use(Vuex) 8 | 9 | const state = { 10 | foo: 1, 11 | } 12 | 13 | const store = new Vuex.store({ 14 | state, 15 | actions, 16 | getters, 17 | mutations, 18 | strict: process.env.NODE_ENV !== 'production', 19 | }) 20 | -------------------------------------------------------------------------------- /test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /src/mixins/analysis.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.mixin({ 4 | methods: { 5 | sendEvent (category, action, label, value) { 6 | window._hmt.push(['_trackEvent', category, action, label, value]) 7 | console.log('_trackEvent', category, action, label, value || null) 8 | }, 9 | }, 10 | beforeRouteEnter (to, from, next) { 11 | const { meta, path } = to 12 | if (meta.pv) window._hmt.push(['_trackPageview', path]) 13 | next() 14 | }, 15 | }) 16 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import routes from './routes' 4 | import { setTitle } from '@/utils' 5 | 6 | Vue.use(Router) 7 | 8 | const router = new Router({ 9 | mode: 'history', 10 | routes, 11 | }) 12 | 13 | router.beforeEach((to, from, next) => { 14 | console.log(to) 15 | const { title } = to.meta 16 | if (title) setTitle(title) 17 | 18 | next() 19 | }) 20 | 21 | router.afterEach((to, from) => { 22 | // ... 23 | }) 24 | 25 | export default router 26 | -------------------------------------------------------------------------------- /src/utils/mint-ui.js: -------------------------------------------------------------------------------- 1 | import { 2 | Navbar, 3 | TabItem, 4 | Spinner, 5 | Button, 6 | InfiniteScroll, 7 | } from 'mint-ui' 8 | 9 | const UIComponents = [ 10 | Navbar, 11 | TabItem, 12 | Spinner, 13 | Button, 14 | ] 15 | 16 | const JSComponents = [ 17 | InfiniteScroll, 18 | ] 19 | 20 | export default function registerComponents (Vue) { 21 | UIComponents.forEach(component => { 22 | Vue.component(component.name, component) 23 | }) 24 | 25 | JSComponents.forEach(component => { 26 | Vue.use(component) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/mixins/storage.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | const { localStorage, sessionStorage } = window 3 | 4 | function storageGenerator (storage) { 5 | const name = storage === localStorage 6 | ? 'storage' 7 | : 'session' 8 | 9 | return { 10 | [`${name}Get`]: key => JSON.parse(storage.getItem(key)), 11 | [`${name}Set`] (key, val) { 12 | storage.setItem(key, JSON.stringify(val)) 13 | }, 14 | [`${name}Remove`] (...keys) { 15 | keys.forEach(key => { storage.removeItem(key) }) 16 | }, 17 | [`${name}Clear`] () { 18 | storage.clear() 19 | }, 20 | } 21 | } 22 | 23 | Vue.mixin({ 24 | methods: { 25 | ...storageGenerator(localStorage), 26 | ...storageGenerator(sessionStorage), 27 | }, 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Qs from 'qs' 3 | 4 | const instance = axios.create({ 5 | baseURL: '/api/', 6 | timeout: 60000, 7 | paramsSerializer: function (params) { 8 | return Qs.stringify(params, {arrayFormat: 'brackets'}) 9 | }, 10 | }) 11 | 12 | instance.interceptors.request.use((config) => { 13 | return config 14 | }) 15 | 16 | instance.interceptors.response.use((response) => { 17 | return response.data 18 | }) 19 | 20 | export default { 21 | get: (url, params) => instance.get(url, { params }), 22 | put: (url, body) => instance.put(url, body), 23 | post: (url, body) => instance.post(url, body), 24 | patch: (url, body) => instance.patch(url, body), 25 | delete: (url, id) => instance.delete(url + id), 26 | } 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 26 | 'comma-dangle': ['error', 'always-multiline'] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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 | import * as filters from './filters' 7 | import './mixins' 8 | import registerComponents from './utils/mint-ui' 9 | import './utils/flexible' 10 | import attachFastClick from 'fastclick' 11 | import 'normalize.css' 12 | 13 | Vue.config.productionTip = false 14 | 15 | // register filters 16 | Object.keys(filters).forEach(key => { 17 | Vue.filter(key, filters[key]) 18 | }) 19 | 20 | // register mint-ui registerComponents 21 | registerComponents(Vue) 22 | 23 | // eliminating the 300ms delay 24 | attachFastClick.attach(document.body) 25 | 26 | /* eslint-disable no-new */ 27 | new Vue({ 28 | el: '#app', 29 | router, 30 | template: '', 31 | components: { App }, 32 | }) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # h5-template 2 | 3 | > Quickly start a H5 project. 4 | 5 | ## instruction 6 | 7 | available config: 8 | 9 | - base project structure 10 | - use `lib-flexible` for adaptable layout 11 | - bootstrap postcss plugins in webpack 12 | - `mint-ui` for default UI components, provide component register 13 | - base axios config 14 | - base vuex config 15 | - base vue-router config, use webpack dynamic import for lazyloading routers 16 | - provide router hook for setting document title after router changes 17 | - register global mixins for `localStorage`, `sessionStorage` and Baidu analysis 18 | - add trailing comma rules in Eslint config 19 | - add filter register 20 | - use `fastclick` to eliminating the 300ms delay 21 | - add some util functions 22 | 23 | ## start 24 | 25 | - `$ cd project_path` 26 | - `$ npm i` 27 | - `$ npm run dev` 28 | 29 | Then your web page will run on `http://localhost:8212` 30 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [formatDate description] 3 | * @param {[type]} time [description] 4 | * @param {String} formatStr [description] 5 | * @param {Boolean} [ms=true] [description] 6 | * @return {String} [description] 7 | */ 8 | 9 | export function formatDate ( 10 | time, 11 | formatStr, 12 | ms = true, 13 | ) { 14 | if (!time) return 15 | 16 | const t = new Date(ms ? time : time * 1000) 17 | const tf = i => i < 10 ? `0${i}` : i 18 | 19 | return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/g, a => { 20 | switch (a) { 21 | case 'yyyy': 22 | return tf(t.getFullYear()) 23 | case 'MM': 24 | return tf(t.getMonth() + 1) 25 | case 'mm': 26 | return tf(t.getMinutes()) 27 | case 'dd': 28 | return tf(t.getDate()) 29 | case 'HH': 30 | return tf(t.getHours()) 31 | case 'ss': 32 | return tf(t.getSeconds()) 33 | } 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/Demo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 47 | 48 | 59 | -------------------------------------------------------------------------------- /src/utils/flexible.js: -------------------------------------------------------------------------------- 1 | (function flexible (window, document) { 2 | var docEl = document.documentElement 3 | var dpr = window.devicePixelRatio || 1 4 | 5 | // adjust body font size 6 | function setBodyFontSize () { 7 | if (document.body) { 8 | document.body.style.fontSize = (12 * dpr) + 'px' 9 | } else { 10 | document.addEventListener('DOMContentLoaded', setBodyFontSize) 11 | } 12 | } 13 | setBodyFontSize() 14 | 15 | // set 1rem = viewWidth / 10 16 | function setRemUnit () { 17 | var rem = docEl.clientWidth / 10 18 | docEl.style.fontSize = rem + 'px' 19 | } 20 | 21 | setRemUnit() 22 | 23 | // reset rem unit on page resize 24 | window.addEventListener('resize', setRemUnit) 25 | window.addEventListener('pageshow', function (e) { 26 | if (e.persisted) { 27 | setRemUnit() 28 | } 29 | }) 30 | 31 | // detect 0.5px supports 32 | if (dpr >= 2) { 33 | var fakeBody = document.createElement('body') 34 | var testElement = document.createElement('div') 35 | testElement.style.border = '.5px solid transparent' 36 | fakeBody.appendChild(testElement) 37 | docEl.appendChild(fakeBody) 38 | if (testElement.offsetHeight === 1) { 39 | docEl.classList.add('hairlines') 40 | } 41 | docEl.removeChild(fakeBody) 42 | } 43 | }(window, document)) 44 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * set document title when router change 3 | * @param {String} title title name 4 | */ 5 | export function setTitle (title) { 6 | document.title = title 7 | const iframe = document.createElement('iframe') 8 | iframe.style.visibility = 'hidden' 9 | const iframeCallback = function () { 10 | setTimeout(() => { 11 | iframe.removeEventListener('load', iframeCallback) 12 | document.body.removeChild(iframe) 13 | }, 0) 14 | } 15 | iframe.addEventListener('load', iframeCallback) 16 | document.body.appendChild(iframe) 17 | } 18 | 19 | /** 20 | * cache return for function, ensure that the function is executed only once 21 | * @param {Function} func 22 | */ 23 | 24 | export function onceWrapper (func) { 25 | let isExecuted = false 26 | let promiseObj = null 27 | 28 | return (...arg) => { 29 | if (isExecuted) return promiseObj 30 | isExecuted = true 31 | promiseObj = func(...arg) 32 | return promiseObj 33 | } 34 | } 35 | 36 | /** 37 | * 时间戳格式化. 38 | * @param {number} time 时间戳 . 39 | * @param {string} formatStr 格式 . 40 | * @param {boolean} ms 时间戳是否是秒为单位, 10位true, 13位false . 41 | * @return {string} 格式化后的字符串. 42 | */ 43 | export function format (time, formatStr, ms) { 44 | let t = new Date(ms ? time * 1000 : time) 45 | let tf = function (i) { 46 | return (i < 10 ? '0' : '') + i 47 | } 48 | return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/g, function (a) { 49 | switch (a) { 50 | case 'yyyy': 51 | return tf(t.getFullYear()) 52 | case 'MM': 53 | return tf(t.getMonth() + 1) 54 | case 'mm': 55 | return tf(t.getMinutes()) 56 | case 'dd': 57 | return tf(t.getDate()) 58 | case 'HH': 59 | return tf(t.getHours()) 60 | case 'ss': 61 | return tf(t.getSeconds()) 62 | } 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /src/style/reset.scss: -------------------------------------------------------------------------------- 1 | /*! Pure v0.6.0 | Yahoo! Inc. | BSD License */ 2 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 3 | 4 | // 字体族 5 | $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; 6 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default; 7 | $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default; 8 | $font-family-base: $font-family-sans-serif !default; 9 | 10 | // 颜色 11 | $red: #fb6f6f !default; 12 | $yellow: #f8d200 !default; 13 | $blue: #7098fc !default; 14 | $green: #60d255 !default; 15 | $black: #000000 !default; 16 | $white: #ffffff !default; 17 | 18 | $text-dark: #333333 !default; 19 | $text-normal: #808080 !default; 20 | $text-light: #cccccc !default; 21 | 22 | $color-bg: #f7f7f7 !default; 23 | $color-divider: #ebebeb !default; 24 | $color-padding: #d9d9d9 !default; 25 | 26 | // 所有的标准 HTML5 标签 27 | // https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5/HTML5_element_list 28 | html,head,title,base,link,meta,style,script,noscript, 29 | template,body,section,nav,article,aside,h1,h2,h3,h4,h5,h6,header, 30 | footer,address,main,p,hr,pre,blockquote,ol,ul,li,dl, 31 | dt,dd,figure,figcaption,div,a,em,strong,small,s,cite, 32 | q,dfn,abbr,data,time,code,var,samp,kbd,sub,i,span,br,wbr,ins,del,img,iframe,embed, 33 | object,param,video,audio,source,track,canvas,map,area, 34 | svg,table,caption,colgroup,col,tbody,thead,tfoot, 35 | tr,td,th,form,fieldset,legend,label,input,button,select, 36 | datalist,optgroup,option,textarea { 37 | margin: 0; 38 | padding: 0; 39 | border: 0; 40 | outline: 0; 41 | } 42 | 43 | html { 44 | font-family: $font-family-base; 45 | // 阻止 iOS 屏幕翻转后字体调整, 不影响用户缩放 46 | -webkit-text-size-adjust: 100%; 47 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 48 | // 全局继承盒模型 49 | box-sizing: border-box; 50 | background-color: #f4f4f4; 51 | } 52 | 53 | *, 54 | *:before, 55 | *:after { 56 | box-sizing: inherit; 57 | } 58 | 59 | // 去掉 heading 字体加粗 60 | h1, 61 | h2, 62 | h3, 63 | h4, 64 | h5, 65 | h6 { 66 | font-weight: normal; 67 | } 68 | 69 | a { 70 | text-decoration: none; 71 | } 72 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.3 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 | }, 15 | 16 | // Various Dev Server settings 17 | host: 'localhost', // can be overwritten by process.env.HOST 18 | port: 8212, // can be overwritten by process.env.HOST, if port is in use, a free one will be determined 19 | autoOpenBrowser: false, 20 | errorOverlay: true, 21 | notifyOnErrors: true, 22 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 23 | 24 | // Use Eslint Loader? 25 | // If true, your code will be linted during bundling and 26 | // linting errors and warnings will be shown in the console. 27 | useEslint: true, 28 | // If true, eslint errors and warnings will also be shown in the error overlay 29 | // in the browser. 30 | showEslintErrorsInOverlay: false, 31 | 32 | /** 33 | * Source Maps 34 | */ 35 | 36 | // https://webpack.js.org/configuration/devtool/#development 37 | devtool: 'eval-source-map', 38 | 39 | // If you have problems debugging vue-files in devtools, 40 | // set this to false - it *may* help 41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 42 | cacheBusting: true, 43 | 44 | // CSS Sourcemaps off by default because relative paths are "buggy" 45 | // with this option, according to the CSS-Loader README 46 | // (https://github.com/webpack/css-loader#sourcemaps) 47 | // In our experience, they generally work as expected, 48 | // just be aware of this issue when enabling this option. 49 | cssSourceMap: false, 50 | }, 51 | 52 | build: { 53 | // Template for index.html 54 | index: path.resolve(__dirname, '../dist/index.html'), 55 | 56 | // Paths 57 | assetsRoot: path.resolve(__dirname, '../dist'), 58 | assetsSubDirectory: 'static', 59 | assetsPublicPath: './', 60 | 61 | /** 62 | * Source Maps 63 | */ 64 | 65 | productionSourceMap: true, 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "h5-template", 3 | "version": "1.0.0", 4 | "description": "h5 template project", 5 | "author": "fusheng <982503660@qq.cn>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --host 0.0.0.0 --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "unit": "jest test/unit/specs --coverage", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs", 14 | "build": "node build/build.js" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.17.1", 18 | "clipboard": "^1.7.1", 19 | "fastclick": "^1.0.6", 20 | "mint-ui": "^2.2.10", 21 | "normalize.css": "^7.0.0", 22 | "qs": "^6.5.1", 23 | "vue": "^2.5.2", 24 | "vue-router": "^3.0.1", 25 | "vuex": "^3.0.1" 26 | }, 27 | "devDependencies": { 28 | "autodll-webpack-plugin": "^0.3.5", 29 | "autoprefixer": "^7.1.2", 30 | "babel-core": "^6.22.1", 31 | "babel-eslint": "^7.1.1", 32 | "babel-jest": "^21.0.2", 33 | "babel-loader": "^7.1.1", 34 | "babel-plugin-component": "^0.10.1", 35 | "babel-plugin-transform-runtime": "^6.22.0", 36 | "babel-preset-env": "^1.3.2", 37 | "babel-preset-stage-0": "^6.24.1", 38 | "babel-register": "^6.22.0", 39 | "chalk": "^2.0.1", 40 | "chromedriver": "^2.27.2", 41 | "copy-webpack-plugin": "^4.0.1", 42 | "cross-spawn": "^5.0.1", 43 | "css-loader": "^0.28.0", 44 | "eslint": "^3.19.0", 45 | "eslint-config-standard": "^10.2.1", 46 | "eslint-friendly-formatter": "^3.0.0", 47 | "eslint-loader": "^1.7.1", 48 | "eslint-plugin-html": "^3.0.0", 49 | "eslint-plugin-import": "^2.7.0", 50 | "eslint-plugin-node": "^5.2.0", 51 | "eslint-plugin-promise": "^3.4.0", 52 | "eslint-plugin-standard": "^3.0.1", 53 | "eventsource-polyfill": "^0.9.6", 54 | "extract-text-webpack-plugin": "^3.0.0", 55 | "file-loader": "^1.1.4", 56 | "friendly-errors-webpack-plugin": "^1.6.1", 57 | "html-webpack-plugin": "^2.30.1", 58 | "jest": "^21.2.0", 59 | "nightwatch": "^0.9.12", 60 | "node-notifier": "^5.1.2", 61 | "node-sass": "^4.7.2", 62 | "optimize-css-assets-webpack-plugin": "^3.2.0", 63 | "ora": "^1.2.0", 64 | "portfinder": "^1.0.13", 65 | "postcss-import": "^11.0.0", 66 | "postcss-loader": "^2.0.8", 67 | "postcss-mixins": "^6.2.0", 68 | "postcss-nested": "^2.1.2", 69 | "postcss-px2rem": "^0.3.0", 70 | "pug": "^2.0.0-rc.4", 71 | "rimraf": "^2.6.0", 72 | "sass-loader": "^6.0.6", 73 | "selenium-server": "^3.0.1", 74 | "semver": "^5.3.0", 75 | "shelljs": "^0.7.6", 76 | "url-loader": "^0.5.8", 77 | "vue-jest": "^1.0.2", 78 | "vue-loader": "^13.3.0", 79 | "vue-style-loader": "^3.0.1", 80 | "vue-template-compiler": "^2.5.2", 81 | "webpack": "^3.6.0", 82 | "webpack-bundle-analyzer": "^2.9.0", 83 | "webpack-dev-server": "^2.9.1", 84 | "webpack-merge": "^4.1.0" 85 | }, 86 | "jest": { 87 | "moduleFileExtensions": [ 88 | "js", 89 | "json", 90 | "vue" 91 | ], 92 | "moduleNameMapper": { 93 | "^@/(.*)$": "/src/$1" 94 | }, 95 | "transform": { 96 | "^.+\\.js$": "/node_modules/babel-jest", 97 | ".*\\.(vue)$": "/node_modules/vue-jest" 98 | }, 99 | "setupFiles": [ 100 | "/test/unit/setup" 101 | ], 102 | "mapCoverage": true, 103 | "coverageDirectory": "/test/unit/coverage", 104 | "collectCoverageFrom": [ 105 | "src/**/*.{js,vue}", 106 | "!src/main.js", 107 | "!src/router/index.js", 108 | "!**/node_modules/**" 109 | ] 110 | }, 111 | "engines": { 112 | "node": ">= 4.0.0", 113 | "npm": ">= 3.0.0" 114 | }, 115 | "browserslist": [ 116 | "> 1%", 117 | "last 2 versions", 118 | "not ie <= 8" 119 | ] 120 | } 121 | --------------------------------------------------------------------------------