├── 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 |
2 |
3 |
4 |
5 |
6 |
7 |
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 |
2 | transition(name='fade')
3 | div.demo
4 | mt-button.btn(@click="pop(1)") 点击弹出Toast
5 | mt-button.btn(@click="pop(2)") 点击弹出自定义时长的Toast
6 | mt-button.btn(@click="pop(3)") 自定义Toast位置
7 |
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 |
--------------------------------------------------------------------------------