├── static └── .gitkeep ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── favicon.ico ├── .gitignore ├── src ├── store │ ├── mutation-type.js │ ├── getters.js │ ├── action.js │ ├── mutations.js │ └── index.js ├── service │ └── getData.js ├── page │ ├── index.js │ ├── wmmy │ │ └── my.vue │ ├── wmshop │ │ └── shop.vue │ └── wm │ │ └── home.vue ├── style │ ├── mixin.sass │ └── base.sass ├── utils │ ├── env.js │ ├── rem.js │ ├── tap.js │ ├── fetch.js │ └── mUtils.js ├── main.js ├── components │ ├── Nothing.vue │ ├── Category.vue │ ├── footer │ │ └── foot.vue │ └── header │ │ └── head.vue ├── router │ └── router.js ├── App.vue ├── assets │ └── error.svg └── test.js ├── .editorconfig ├── .postcssrc.js ├── .babelrc ├── index.html ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ppya0812/vue-shop-cli/HEAD/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | node_modules/ 4 | dist/ 5 | npm-debug.log 6 | yarn-error.log 7 | -------------------------------------------------------------------------------- /src/store/mutation-type.js: -------------------------------------------------------------------------------- 1 | export const GET_USEINFO = 'GET_USEINFO' 2 | export const INCREMENT = 'INCREMENT' 3 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | // store的计算属性 暂时没发现有什么必要性 2 | export default { 3 | filterList: state => { 4 | return state.todoList.filter(todo => todo.done) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.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/service/getData.js: -------------------------------------------------------------------------------- 1 | // import Fetch from '../utils/fetch'; 2 | // const fetch = Fetch() 3 | /** 4 | * 获取首页默认地址 5 | */ 6 | 7 | // export const cityGuess = () => fetch.get('GE', '/v1/cities', { 8 | // type: 'guess' 9 | // }); 10 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | 4 | module.exports = { 5 | "plugins": { 6 | // to edit target browsers: use "browserlist" field in package.json 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/page/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'home': { 3 | title: '首页', 4 | back: false 5 | }, 6 | 'shop': { 7 | title: '购物', 8 | back: true 9 | }, 10 | 'my': { 11 | title: '我的', 12 | back: true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/page/wmmy/my.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /src/store/action.js: -------------------------------------------------------------------------------- 1 | // Action 提交的是 mutation 2 | 3 | import { GET_USEINFO, INCREMENT } from './mutation-type' 4 | 5 | export default { 6 | getUserInfo({ commit, state }, data) { 7 | commit(GET_USEINFO, data) 8 | }, 9 | increment({ commit, state }, data) { 10 | commit(INCREMENT, data) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/page/wmshop/shop.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "presets": [ 4 | ["latest", { 5 | "es2015": { "modules": false } 6 | }], 7 | "stage-2" 8 | ], 9 | "plugins": ["transform-vue-jsx", "transform-runtime"], 10 | "comments": false, 11 | "env": { 12 | "test": { 13 | "presets": ["latest", "stage-2"], 14 | "plugins": [ "istanbul" ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | // 更改vuex的store中的状态 2 | import { GET_USEINFO, INCREMENT } from './mutation-type' 3 | 4 | export default { 5 | [GET_USEINFO](state, payload) { 6 | state.userInfo = {...state.userInfo, ...payload} 7 | }, 8 | [INCREMENT](state, payload) { 9 | // change state 10 | state.count += payload.amount 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/style/mixin.sass: -------------------------------------------------------------------------------- 1 | 2 | // $red-default: #ff2d4b 3 | $bg-default: #007AFF 4 | .ellipsis 5 | overflow: hidden 6 | text-overflow: ellipsis 7 | white-space: nowrap 8 | 9 | .multi-ellipsis 10 | overflow: hidden 11 | text-overflow: ellipsis 12 | display: -webkit-box 13 | white-space: pre-wrap 14 | -webkit-line-clamp: 2 15 | -webkit-box-orient: vertical 16 | -------------------------------------------------------------------------------- /src/utils/env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 配置编译环境和线上环境之间的切换 3 | * 4 | * baseUrl: 域名地址 5 | * routerMode: 路由模式 6 | * imgBaseUrl: 图片所在域名地址 7 | * 8 | */ 9 | let baseUrl; 10 | let routerMode; 11 | const imgBaseUrl = 'http://img.waimai.baidu.com'; 12 | 13 | if (process.env.NODE_ENV == 'development') { 14 | baseUrl = ''; 15 | routerMode = 'hash' 16 | }else{ 17 | baseUrl = 'http://waimai.baidu.com'; 18 | routerMode = 'hash' 19 | } 20 | 21 | export {baseUrl, routerMode, imgBaseUrl} 22 | -------------------------------------------------------------------------------- /src/utils/rem.js: -------------------------------------------------------------------------------- 1 | export default function rem(designWidth, remSize) { 2 | let percent = remSize / designWidth; 3 | let doc = window.document; 4 | let docEl = doc.documentElement; 5 | 6 | function setRootSize() { 7 | let width = docEl.getBoundingClientRect().width; 8 | // 绝对值(px) 9 | docEl.style.fontSize = width * percent + 'px'; 10 | // 相对值 11 | // docEl.style.fontSize = width * percent * 100 / 16 + '%'; 12 | } 13 | setRootSize(); 14 | window.addEventListener('resize', setRootSize); 15 | } 16 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import routes from './router/router' 4 | import store from './store/' 5 | // import {routerMode} from './config/env' 6 | import rem from './utils/rem' 7 | import tap from './utils/tap' 8 | import fetch from './utils/fetch'; 9 | 10 | Vue.use(VueRouter) 11 | Vue.use(tap) 12 | rem(320, 10) // 1rem -- 10px 13 | 14 | // 全局注册数据请求 15 | Vue.prototype.fetch = fetch; 16 | 17 | const router = new VueRouter({ 18 | routes, 19 | // mode: routerMode, 20 | strict: process.env.NODE_ENV !== 'production' 21 | }) 22 | 23 | new Vue({ 24 | router, 25 | store 26 | }).$mount('#app') 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 购物 14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/Nothing.vue: -------------------------------------------------------------------------------- 1 | 8 | 23 | 39 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import App from '../App' 2 | const home = r => require.ensure([], () => r(require('../page/wm/home')), 'home') 3 | const shop = r => require.ensure([], () => r(require('../page/wmshop/shop')), 'shop') 4 | const my = r => require.ensure([], () => r(require('../page/wmmy/my')), 'my') 5 | 6 | export default [{ 7 | path: '/', 8 | component: App, // 顶层路由,对应index.html 9 | children: [ 10 | { path: '/home', component: home }, // 首页 11 | { path: '/shop', component: shop }, // 购物 12 | { path: '/my', component: my }, // 购物 13 | { path: '*', component: home } // 地址为空时跳转home页面 14 | // { path: '/shop', component: shop , 15 | // children: [ 16 | // { 17 | // path: 'rating', 18 | // component: rating, 19 | // } 20 | // ] 21 | // }, 22 | ] 23 | }] 24 | -------------------------------------------------------------------------------- /src/components/Category.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | 30 | 45 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 46 | 47 | 51 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import actions from './action' 4 | import getters from './getters' 5 | import mutations from './mutations' 6 | 7 | Vue.use(Vuex) 8 | 9 | const state = { 10 | userInfo: { 11 | name: 'ppya' 12 | }, 13 | todoList: [ 14 | { id: 1, text: '1...haha', done: true }, 15 | { id: 2, text: '2...lala', done: true }, 16 | { id: 3, text: '3...xixi', done: false } 17 | ], 18 | categoryList: [ 19 | {cat_id: '1', name: 'ppya分类'}, 20 | {cat_id: '2', name: '分类1'}, 21 | {cat_id: '3', name: '水果'}, 22 | {cat_id: '4', name: '蛋糕'}, 23 | {cat_id: '5', name: '食品'}, 24 | {cat_id: '6', name: '撸猫'}, 25 | {cat_id: '7', name: '狗狗'}, 26 | {cat_id: '8', name: '分类1'}, 27 | {cat_id: '9', name: '水果'}, 28 | {cat_id: '10', name: '蛋糕'}, 29 | {cat_id: '11', name: '食品'}, 30 | {cat_id: '12', name: '撸猫'}, 31 | {cat_id: '13', name: '狗狗'} 32 | ], 33 | count: 18 34 | } 35 | 36 | export default new Vuex.Store({ 37 | state, 38 | getters, 39 | actions, 40 | mutations 41 | }) 42 | -------------------------------------------------------------------------------- /src/components/footer/foot.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 43 | -------------------------------------------------------------------------------- /src/components/header/head.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | 50 | -------------------------------------------------------------------------------- /src/style/base.sass: -------------------------------------------------------------------------------- 1 | @import "mixin.sass" 2 | * 3 | box-sizing: border-box 4 | margin: 0 5 | padding: 0 6 | 7 | .clearfix:after 8 | content: '\0020' 9 | display: block 10 | height: 0 11 | clear: both 12 | 13 | .clearfix 14 | *zoom: 1 15 | 16 | img 17 | -webkit-touch-callout: none 18 | 19 | html 20 | font-family: PingFangSC-Regular, sans-serif 21 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0) 22 | -webkit-user-select: none 23 | user-select: none 24 | -webkit-text-size-adjust: none 25 | text-size-adjust: none 26 | 27 | ::-webkit-scrollbar, 28 | ::-webkit-scrollbar-track, 29 | ::-webkit-scrollbar-thumb, 30 | ::-webkit-scrollbar-button, 31 | ::-webkit-scrollbar-corner 32 | display: none 33 | background: transparent 34 | 35 | /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ 36 | ::-webkit-scrollbar 37 | width: 0px 38 | height: 0px 39 | background-color: #F5F5F5 40 | 41 | /*定义滚动条轨道 内阴影+圆角*/ 42 | ::-webkit-scrollbar-track 43 | -webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0) 44 | border-radius: 10px 45 | background-color: #F5F5F5 46 | 47 | /*定义滑块 内阴影+圆角*/ 48 | ::-webkit-scrollbar-thumb 49 | border-radius: 10px 50 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3) 51 | background-color: #555 52 | 53 | input[type="button"], input[type="submit"], input[type="search"], input[type="reset"] 54 | -webkit-appearance: none 55 | 56 | textarea 57 | -webkit-appearance: none 58 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | var online = 'http://waimai.baidu.com' 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/dist/', // 服務器訪問dist文件中html內部js.css文件路徑 11 | productionSourceMap: false, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8088, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: { // 接口訪問代理 31 | '/h5ui/': { 32 | target: online, 33 | changeOrigin: true 34 | } 35 | }, 36 | autoOpenBrowser: true, 37 | // CSS Sourcemaps off by default because relative paths are "buggy" 38 | // with this option, according to the CSS-Loader README 39 | // (https://github.com/webpack/css-loader#sourcemaps) 40 | // In our experience, they generally work as expected, 41 | // just be aware of this issue when enabling this option. 42 | cssSourceMap: false 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/page/wm/home.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 64 | 65 | 80 | -------------------------------------------------------------------------------- /src/utils/tap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * usage 3 | *
4 | */ 5 | const DEFAULT = { 6 | speed: 50, 7 | distance: 10, 8 | time: 700 9 | }; 10 | const eventName = 'wmapptap'; 11 | 12 | export default function tap(Vue, options) { 13 | let option = Object.assign({}, DEFAULT, options); 14 | let startState = null; 15 | window.addEventListener('touchstart', function ontouchstart(e) { 16 | startState = { 17 | x: e.changedTouches[0].clientX, 18 | y: e.changedTouches[0].clientY, 19 | time: Date.now() 20 | }; 21 | }); 22 | 23 | window.addEventListener('touchend', function ontouchend(e) { 24 | if (e.changedTouches.length !== 1 || !startState) { 25 | startState = null; 26 | return; 27 | } 28 | let endState = { 29 | x: e.changedTouches[0].clientX, 30 | y: e.changedTouches[0].clientY, 31 | time: Date.now() 32 | }; 33 | let distance = Math.sqrt(Math.pow(endState.x - startState.x, 2) + Math.pow(endState.y - startState.y, 2)); 34 | let time = endState.time - startState.time; 35 | let speed = distance / time * 1000; 36 | if (distance <= option.distance && time <= option.time && speed <= option.speed) { 37 | let ev = document.createEvent('Event'); 38 | ev.initEvent(eventName, true, true); 39 | ev.pageX = e.changedTouches[0].pageX; 40 | ev.pageY = e.changedTouches[0].pageY; 41 | e.target.dispatchEvent(ev); 42 | } 43 | }); 44 | Vue.directive('tap', { 45 | bind: function (el, binding, vnode) { 46 | el.addEventListener(eventName, function ontap(e) { 47 | if (binding.modifiers.self && e.target !== el) { 48 | return; 49 | } 50 | if (binding.modifiers.stop) { 51 | e.stopPropagation(); 52 | } 53 | if (binding.modifiers.prevent) { 54 | e.preventDefault(); 55 | } 56 | if (typeof binding.value.callback !== 'function') { 57 | return; 58 | } 59 | let argus = binding.value.argus || []; 60 | argus.push(e); 61 | binding.value.callback.apply(vnode.context, argus); 62 | }); 63 | } 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /src/utils/fetch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | * use method 4 | * fetch().get(url, data) 5 | *.then() 6 | * .catch() 7 | */ 8 | 9 | const param = require('jquery-param'); // for 深度解析 10 | 11 | function isObject(data) { 12 | return Object.prototype.toString.call(data) === '[object Object]' || 13 | Object.prototype.toString.call(data) === '[object Array]'; 14 | } 15 | 16 | function hasContentType(headers) { 17 | return Object.keys(headers).some(function (name) { 18 | return name.toLowerCase() === 'content-type'; 19 | }); 20 | } 21 | 22 | function setHeaders(xhr, headers) { 23 | headers = headers || {}; 24 | 25 | if (!hasContentType(headers)) { 26 | headers['Content-Type'] = 'application/x-www-form-urlencoded'; 27 | } 28 | 29 | Object.keys(headers).forEach(function (name) { 30 | xhr.setRequestHeader(name, headers[name]); 31 | }); 32 | } 33 | 34 | function xhrConnection(type, url, data, options) { 35 | if (isObject(data)) { 36 | data = param(data); 37 | } 38 | return new Promise(function (resolve, reject) { 39 | let xhr = new XMLHttpRequest(); 40 | if (type === 'get') { 41 | url = url.replace(/#.*$/, ''); 42 | let divider = url.indexOf('?') !== -1 ? '&' : '?'; 43 | url = [url, data].join(divider); 44 | data = null; 45 | } 46 | 47 | xhr.open(type, url || '', true); 48 | setHeaders(xhr, options.headers); 49 | xhr.onload = function () { 50 | if (xhr.status >= 200 && xhr.status < 300) { 51 | let result; 52 | try { 53 | result = JSON.parse(xhr.responseText); 54 | } catch (e) { 55 | window.wmErrorReport && window.wmErrorReport(e); 56 | result = xhr.responseText; 57 | } 58 | resolve(result); 59 | } else { 60 | reject(Error(xhr.statusText)); 61 | } 62 | }; 63 | xhr.onerror = function () { 64 | reject(Error('Network Error')); 65 | }; 66 | xhr.send(data); 67 | }); 68 | } 69 | 70 | export default function Ajax(options) { 71 | options = options || {}; 72 | 73 | let ajax = {}; 74 | 75 | let httpMethods = ['get', 'post', 'put', 'delete']; 76 | 77 | httpMethods.forEach(function (method) { 78 | ajax[method] = function (url, data) { 79 | return xhrConnection(method, url, data, options); 80 | }; 81 | }); 82 | return ajax; 83 | } 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-shop-cli", 3 | "version": "1.0.0", 4 | "description": "a vue project for e-commerce", 5 | "author": "ppya ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "build": "node build/build.js", 10 | "lint": "eslint --ext .js,.vue src" 11 | }, 12 | "dependencies": { 13 | "jquery-param": "^0.2.0", 14 | "object-assign": "^4.1.1", 15 | "vue": "^2.2.1", 16 | "vue-router": "^2.2.0", 17 | "vuex": "^2.0.0" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^6.7.2", 21 | "babel-core": "^6.22.1", 22 | "babel-eslint": "^7.1.1", 23 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 24 | "babel-loader": "^6.2.10", 25 | "babel-plugin-istanbul": "^3.1.2", 26 | "babel-plugin-syntax-jsx": "^6.18.0", 27 | "babel-plugin-transform-runtime": "^6.22.0", 28 | "babel-plugin-transform-vue-jsx": "^3.3.0", 29 | "babel-preset-latest": "^6.22.0", 30 | "babel-preset-stage-2": "^6.22.0", 31 | "babel-register": "^6.22.0", 32 | "chalk": "^1.1.3", 33 | "chromedriver": "^2.27.2", 34 | "connect-history-api-fallback": "^1.3.0", 35 | "copy-webpack-plugin": "^4.0.1", 36 | "cross-env": "^3.1.4", 37 | "cross-spawn": "^5.0.1", 38 | "css-loader": "^0.26.1", 39 | "eslint": "^3.14.1", 40 | "eslint-config-standard": "^6.2.1", 41 | "eslint-friendly-formatter": "^2.0.7", 42 | "eslint-loader": "^1.6.1", 43 | "eslint-plugin-html": "^2.0.0", 44 | "eslint-plugin-promise": "^3.4.0", 45 | "eslint-plugin-standard": "^2.0.1", 46 | "eventsource-polyfill": "^0.9.6", 47 | "express": "^4.14.1", 48 | "extract-text-webpack-plugin": "^2.0.0", 49 | "file-loader": "^0.10.0", 50 | "friendly-errors-webpack-plugin": "^1.1.3", 51 | "function-bind": "^1.1.0", 52 | "html-webpack-plugin": "^2.28.0", 53 | "http-proxy-middleware": "^0.17.3", 54 | "inject-loader": "^2.0.1", 55 | "node-gyp": "^3.4.0", 56 | "node-sass": "^4.5.0", 57 | "opn": "^4.0.2", 58 | "optimize-css-assets-webpack-plugin": "^1.3.0", 59 | "ora": "^1.1.0", 60 | "phantomjs-prebuilt": "^2.1.14", 61 | "rimraf": "^2.6.0", 62 | "sass": "^0.5.0", 63 | "sass-loader": "^4.1.1", 64 | "scss": "^0.2.4", 65 | "scss-loader": "0.0.1", 66 | "selenium-server": "^3.0.1", 67 | "semver": "^5.3.0", 68 | "url-loader": "^0.5.7", 69 | "vue-loader": "^11.0.0", 70 | "vue-style-loader": "^2.0.0", 71 | "vue-template-compiler": "^2.2.1", 72 | "webpack": "^3.0.0", 73 | "webpack-bundle-analyzer": "^2.2.1", 74 | "webpack-dev-middleware": "^1.10.0", 75 | "webpack-hot-middleware": "^2.16.1", 76 | "webpack-merge": "^2.6.1" 77 | }, 78 | "engines": { 79 | "node": ">= 4.0.0", 80 | "npm": ">= 3.0.0" 81 | }, 82 | "browserlist": [ 83 | "> 1%", 84 | "last 2 versions", 85 | "not ie <= 8" 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-shop-cli 2 | 3 | 这是一款基于vue实现的,适用于电商平台的架手架,移动、轻便、可扩展性强。 4 | 5 | ## 技术栈 6 | 7 | vue2 + vue-rotuer2 + vuex2 + webpack + ES6/7 + fetch + sass + svg. (支持反向代理) 8 | 9 | ## 项目布局 10 | 11 | ``` 12 | |-- build // webpack配置文件 13 | |-- config // 项目打包路径 14 | |-- dist // 上线项目文件,放在服务器即可正常访问 15 | | 16 | |-- src // 源码目录 17 | | |-- assets // 公共资源(eg.图片) 18 | | |-- components // 组件 19 | | |-- common // 公共组件 20 | | |-- loading.js // 页面初始化加载数据的动画组件 21 | | |-- mixin.js // 组件混合(包括:指令-下拉加载更多,处理图片地址) 22 | | |-- footer // 底部公共组件 23 | | |-- header // 头部公共组件 24 | | 25 | | |-- pages // 页面组件 26 | | |-- home // 首页 27 | | |-- myshop // 购物页 28 | | |-- my // 我的 29 | | 30 | | |-- plugins // 引用的插件 31 | | 32 | | |-- router // 路由配置 33 | | 34 | | |-- service // 数据交互统一调配 35 | | |-- template // 开发阶段的临时数据 36 | | |-- getData.js // 获取数据的统一调配文件,对接口进行统一管理 37 | | 38 | | |-- store // vuex的状态管理 39 | | |-- modules // store模块 40 | | |-- action.js // 配置actions 41 | | |-- getters.js // 配置getters 42 | | |-- index.js // 引用vuex,创建store 43 | | |-- mutation-types.js // 定义常量muations名 44 | | |-- mutations.js // 配置mutations 45 | | 46 | | |-- utils // 工具函数 && 全局配置 47 | | |-- env.js // 环境切换配置 48 | | |-- fetch.js // 获取数据 49 | | |-- mUtils.js // 常用的js方法 50 | | |-- rem.js // px转换rem 51 | | 52 | | |-- style // 各种样式文件 53 | | |-- common.scss // 公共样式文件 54 | | |-- mixin.scss // 样式配置文件 55 | | 56 | | |-- App.vue // 页面入口文件 57 | | 58 | | |-- main.js // 程序入口文件,加载各种公共组件 59 | | 60 | |-- .babelrc // ES6语法编译配置 61 | |-- .editorconfig // 代码编写规格 62 | |-- .gitignore // 忽略的文件 63 | |-- favicon.ico // 页面左上角小图标 64 | |-- index.html // 入口html文件 65 | |-- package.json // 项目及工具的依赖配置文件 66 | |-- README.md // 说明 67 | ``` 68 | 69 | ## 项目运行 70 | 71 | ``` 72 | 克隆,或者直接下载 73 | git clone https://github.com/ppya0812/vue-shop-cli 74 | 75 | 进入文件夹 76 | cd vue-shop-cli 77 | 78 | 安装依赖 79 | npm install 80 | ``` 81 | 82 | ## 编译环境 83 | 84 | ``` 85 | 开启本地服务器 86 | npm run dev 87 | 88 | 访问 http://localhost:8088 89 | ``` 90 | 91 | ## 线上版本 92 | 93 | ``` 94 | npm run build 95 | 96 | 生成的dist文件夹放在服务器即可正常访问 97 | ``` 98 | -------------------------------------------------------------------------------- /src/assets/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 无违规空页面 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/utils/mUtils.js: -------------------------------------------------------------------------------- 1 | // export const json2url = json => { 2 | // let arr = []; 3 | // for (let i in json) { 4 | // arr.push(i + '=' + json[i]); 5 | // } 6 | // return arr.join('&'); 7 | // } 8 | 9 | /** 10 | * 存储localStorage 11 | */ 12 | export const setStore = (name, content) => { 13 | if (!name) { 14 | return; 15 | } 16 | if (typeof content !== 'string') { 17 | content = JSON.stringify(content); 18 | } 19 | window.localStorage.setItem(name, content); 20 | } 21 | 22 | /** 23 | * 获取localStorage 24 | */ 25 | export const getStore = name => { 26 | if (!name) { 27 | return; 28 | } 29 | return window.localStorage.getItem(name); 30 | } 31 | 32 | /** 33 | * 删除localStorage 34 | */ 35 | export const removeStore = name => { 36 | if (!name) { 37 | return; 38 | } 39 | window.localStorage.removeItem(name); 40 | } 41 | 42 | /** 43 | * 获取style样式 44 | */ 45 | export const getStyle = (element, attr, NumberMode = 'int') => { 46 | let target; 47 | // scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用 48 | if (attr === 'scrollTop') { 49 | target = element.scrollTop; 50 | } else if (element.currentStyle) { 51 | target = element.currentStyle[attr]; 52 | } else { 53 | target = document.defaultView.getComputedStyle(element, null)[attr]; 54 | } 55 | // 在获取 opactiy 时需要获取小数 parseFloat 56 | return NumberMode === 'float' 57 | ? parseFloat(target) 58 | : parseInt(target); 59 | } 60 | 61 | /** 62 | * 页面到达底部,加载更多 63 | */ 64 | export const loadMore = (element, callback) => { 65 | let windowHeight = window.screen.height; 66 | let height; 67 | let setTop; 68 | let paddingBottom; 69 | let marginBottom; 70 | let requestFram; 71 | let oldScrollTop; 72 | 73 | element.addEventListener('scroll', () => { 74 | loadMore(); 75 | }, false) 76 | // 运动开始时获取元素 高度 和 offseTop, pading, margin 77 | element.addEventListener('touchstart', () => { 78 | height = element.offsetHeight; 79 | setTop = element.offsetTop; 80 | paddingBottom = getStyle(element, 'paddingBottom'); 81 | marginBottom = getStyle(element, 'marginBottom'); 82 | }, {passive: true}) 83 | 84 | // 运动过程中保持监听 scrollTop 的值判断是否到达底部 85 | element.addEventListener('touchmove', () => { 86 | loadMore(); 87 | }, {passive: true}) 88 | 89 | // 运动结束时判断是否有惯性运动,惯性运动结束判断是非到达底部 90 | element.addEventListener('touchend', () => { 91 | oldScrollTop = document.body.scrollTop; 92 | moveEnd(); 93 | }, {passive: true}) 94 | 95 | const moveEnd = () => { 96 | requestFram = requestAnimationFrame(() => { 97 | if (document.body.scrollTop !== oldScrollTop) { 98 | oldScrollTop = document.body.scrollTop; 99 | loadMore(); 100 | moveEnd(); 101 | } else { 102 | cancelAnimationFrame(requestFram); 103 | // 为了防止鼠标抬起时已经渲染好数据从而导致重获取数据,应该重新获取dom高度 104 | height = element.offsetHeight; 105 | loadMore(); 106 | } 107 | }) 108 | } 109 | 110 | const loadMore = () => { 111 | if (document.body.scrollTop + windowHeight >= height + setTop + paddingBottom + marginBottom) { 112 | callback(); 113 | } 114 | } 115 | } 116 | 117 | /** 118 | * 显示返回顶部按钮,开始、结束、运动 三个过程中调用函数判断是否达到目标点 119 | */ 120 | export const showBack = callback => { 121 | let requestFram; 122 | let oldScrollTop; 123 | 124 | document.addEventListener('scroll', () => { 125 | showBackFun(); 126 | }, false) 127 | document.addEventListener('touchstart', () => { 128 | showBackFun(); 129 | }, {passive: true}) 130 | 131 | document.addEventListener('touchmove', () => { 132 | showBackFun(); 133 | }, {passive: true}) 134 | 135 | document.addEventListener('touchend', () => { 136 | oldScrollTop = document.body.scrollTop; 137 | moveEnd(); 138 | }, {passive: true}) 139 | 140 | const moveEnd = () => { 141 | requestFram = requestAnimationFrame(() => { 142 | if (document.body.scrollTop !== oldScrollTop) { 143 | oldScrollTop = document.body.scrollTop; 144 | moveEnd(); 145 | } else { 146 | cancelAnimationFrame(requestFram); 147 | } 148 | showBackFun(); 149 | }) 150 | } 151 | 152 | // 判断是否达到目标点 153 | const showBackFun = () => { 154 | if (document.body.scrollTop > 500) { 155 | callback(true); 156 | } else { 157 | callback(false); 158 | } 159 | } 160 | } 161 | 162 | /** 163 | * 运动效果 164 | * @param {HTMLElement} element 运动对象,必选 165 | * @param {JSON} target 属性:目标值,必选 166 | * @param {number} duration 运动时间,可选 167 | * @param {string} mode 运动模式,可选 168 | * @param {function} callback 可选,回调函数,链式动画 169 | */ 170 | export const animate = (element, target, duration = 400, mode = 'ease-out', callback) => { 171 | clearInterval(element.timer); 172 | 173 | // 判断不同参数的情况 174 | if (duration instanceof Function) { 175 | callback = duration; 176 | duration = 400; 177 | } else if (duration instanceof String) { 178 | mode = duration; 179 | duration = 400; 180 | } 181 | 182 | // 判断不同参数的情况 183 | if (mode instanceof Function) { 184 | callback = mode; 185 | mode = 'ease-out'; 186 | } 187 | 188 | // 获取dom样式 189 | const attrStyle = attr => { 190 | if (attr === 'opacity') { 191 | return Math.round(getStyle(element, attr, 'float') * 100); 192 | } else { 193 | return getStyle(element, attr); 194 | } 195 | } 196 | // 根字体大小,需要从此将 rem 改成 px 进行运算 197 | const rootSize = parseFloat(document.documentElement.style.fontSize); 198 | 199 | const unit = {}; 200 | const initState = {}; 201 | 202 | // 获取目标属性单位和初始样式值 203 | Object.keys(target).forEach(attr => { 204 | if (/[^\d^.]+/gi.test(target[attr])) { 205 | unit[attr] = target[attr].match(/[^\d^.]+/gi)[0] || 'px'; 206 | } else { 207 | unit[attr] = 'px'; 208 | } 209 | initState[attr] = attrStyle(attr); 210 | }); 211 | 212 | // 去掉传入的后缀单位 213 | Object.keys(target).forEach(attr => { 214 | if (unit[attr] === 'rem') { 215 | target[attr] = Math.ceil(parseInt(target[attr]) * rootSize); 216 | } else { 217 | target[attr] = parseInt(target[attr]); 218 | } 219 | }); 220 | 221 | let flag = true; // 假设所有运动到达终点 222 | const remberSpeed = {}; // 记录上一个速度值,在ease-in模式下需要用到 223 | element.timer = setInterval(() => { 224 | Object.keys(target).forEach(attr => { 225 | let iSpeed = 0; // 步长 226 | let status = false; // 是否仍需运动 227 | let iCurrent = attrStyle(attr) || 0; // 当前元素属性址 228 | let speedBase = 0; // 目标点需要减去的基础值,三种运动状态的值都不同 229 | let intervalTime; // 将目标值分为多少步执行,数值越大,步长越小,运动时间越长 230 | switch (mode) { 231 | case 'ease-out': 232 | speedBase = iCurrent; 233 | intervalTime = duration * 5 / 400; 234 | break; 235 | case 'linear': 236 | speedBase = initState[attr]; 237 | intervalTime = duration * 20 / 400; 238 | break; 239 | case 'ease-in': 240 | let oldspeed = remberSpeed[attr] || 0; 241 | iSpeed = oldspeed + (target[attr] - initState[attr]) / duration; 242 | remberSpeed[attr] = iSpeed 243 | break; 244 | default: 245 | speedBase = iCurrent; 246 | intervalTime = duration * 5 / 400; 247 | } 248 | if (mode !== 'ease-in') { 249 | iSpeed = (target[attr] - speedBase) / intervalTime; 250 | iSpeed = iSpeed > 0 251 | ? Math.ceil(iSpeed) 252 | : Math.floor(iSpeed); 253 | } 254 | // 判断是否达步长之内的误差距离,如果到达说明到达目标点 255 | switch (mode) { 256 | case 'ease-out': 257 | status = iCurrent !== target[attr]; 258 | break; 259 | case 'linear': 260 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 261 | break; 262 | case 'ease-in': 263 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 264 | break; 265 | default: 266 | status = iCurrent !== target[attr]; 267 | } 268 | 269 | if (status) { 270 | flag = false; 271 | // opacity 和 scrollTop 需要特殊处理 272 | if (attr === 'opacity') { 273 | element.style.filter = 'alpha(opacity:' + (iCurrent + iSpeed) + ')'; 274 | element.style.opacity = (iCurrent + iSpeed) / 100; 275 | } else if (attr === 'scrollTop') { 276 | element.scrollTop = iCurrent + iSpeed; 277 | } else { 278 | element.style[attr] = iCurrent + iSpeed + 'px'; 279 | } 280 | } else { 281 | flag = true; 282 | } 283 | 284 | if (flag) { 285 | clearInterval(element.timer); 286 | if (callback) { 287 | callback(); 288 | } 289 | } 290 | }) 291 | }, 20); 292 | } 293 | -------------------------------------------------------------------------------- /src/test.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([23], { 2 | 0: function(t, e, a) { 3 | t.exports = a(176) 4 | }, 5 | 113: function(t, e) { 6 | "use strict"; 7 | Object.defineProperty(e, "__esModule", { 8 | value: !0 9 | }); 10 | var a = function(t) { 11 | var e = document.createElement("a"); 12 | e.href = t; 13 | var a = ["http:", "https:", "eleme:"]; 14 | return a.indexOf(e.protocol) !== -1 && !(e.hostname && !e.hostname.match(/(^|\.)ele(net)?\.me$/)) 15 | }; 16 | e.default = { 17 | post: function(t, e) { 18 | var a = { 19 | method: "POST", 20 | credentials: "include", 21 | body: JSON.stringify(e) 22 | }; 23 | return window.fetch(t, a).then(function(t) { 24 | var e = t.json(); 25 | return t.ok ? e : e.then(Promise.reject.bind(Promise)) 26 | }) 27 | }, 28 | redirect: function() { 29 | var t = (new window.UParams).redirect; 30 | t && a(t) || (t = "/msite/"), 31 | location.href = t 32 | } 33 | } 34 | }, 35 | 140: function(t, e, a) { 36 | var s, o, i = {}; 37 | a(559), 38 | s = a(329), 39 | o = a(878), 40 | t.exports = s || {}, 41 | t.exports.__esModule && (t.exports = t.exports.default); 42 | var c = "function" == typeof t.exports ? t.exports.options || (t.exports.options = {}) : t.exports; 43 | o && (c.template = o), 44 | c.computed || (c.computed = {}), 45 | Object.keys(i).forEach(function(t) { 46 | var e = i[t]; 47 | c.computed[t] = function() { 48 | return e 49 | } 50 | }) 51 | }, 52 | 176: function(t, e, a) { 53 | "use strict"; 54 | var s = a(1006) 55 | , o = babelHelpers.interopRequireDefault(s); 56 | new Vue({ 57 | el: "body", 58 | components: { 59 | App: o.default 60 | } 61 | }) 62 | }, 63 | 328: function(t, e, a) { 64 | "use strict"; 65 | Object.defineProperty(e, "__esModule", { 66 | value: !0 67 | }); 68 | var s = a(1007) 69 | , o = babelHelpers.interopRequireDefault(s) 70 | , i = a(1008) 71 | , c = babelHelpers.interopRequireDefault(i) 72 | , n = a(4); 73 | e.default = { 74 | components: { 75 | ElemeHeader: n.ElemeHeader, 76 | Message: o.default, 77 | Password: c.default 78 | }, 79 | data: function() { 80 | return { 81 | apihost: "//mainsite-restapi.ele.me", 82 | current: "message", 83 | toast: "", 84 | timer: null, 85 | showToast: !1, 86 | isApp: /Eleme/.test(navigator.userAgent) 87 | } 88 | }, 89 | computed: { 90 | headerOpt: function() { 91 | return { 92 | message: { 93 | title: "登录", 94 | switchName: "密码登录", 95 | switchTarget: "password" 96 | }, 97 | password: { 98 | title: "密码登录", 99 | switchName: "短信登录", 100 | switchTarget: "message" 101 | } 102 | }[this.current] 103 | } 104 | }, 105 | methods: { 106 | switchTo: function(t) { 107 | this.current = t 108 | } 109 | }, 110 | events: { 111 | setToast: function(t) { 112 | var e = this; 113 | clearTimeout(this.timer), 114 | this.toast = t, 115 | this.showToast = !0, 116 | this.timer = setTimeout(function() { 117 | e.showToast = !1 118 | }, 2e3) 119 | } 120 | } 121 | } 122 | }, 123 | 329: function(t, e) { 124 | "use strict"; 125 | Object.defineProperty(e, "__esModule", { 126 | value: !0 127 | }), 128 | e.default = { 129 | props: { 130 | apihost: { 131 | type: String, 132 | default: "//mainsite-restapi.ele.me" 133 | }, 134 | confirmButton: { 135 | type: Function, 136 | default: function() {} 137 | }, 138 | cancelButton: { 139 | type: Function, 140 | default: function() {} 141 | } 142 | }, 143 | data: function() { 144 | return { 145 | captchaCode: "", 146 | userCaptcha: "" 147 | } 148 | }, 149 | computed: { 150 | captchaImage: function() { 151 | if (this.captchaCode) 152 | return this.apihost + "/v1/captchas/" + this.captchaCode 153 | } 154 | }, 155 | methods: { 156 | $fetch: function(t, e) { 157 | return window.fetch(t, e).then(function(t) { 158 | var e = t.json(); 159 | return t.status >= 200 && t.status < 300 ? e : e.then(Promise.reject.bind(Promise)) 160 | }) 161 | }, 162 | reloadCaptcha: function() { 163 | this.$emit("getCaptcha") 164 | }, 165 | submitCaptcha: function() { 166 | return this.userCaptcha ? void this.confirmButton(this.userCaptcha, this.captchaCode) : this.$dispatch("setToast", "请填写验证码") 167 | } 168 | }, 169 | events: { 170 | getCaptcha: function() { 171 | var t = this; 172 | this.userCaptcha = "", 173 | this.$fetch(this.apihost + "/v1/captchas", { 174 | method: "POST", 175 | credentials: "include" 176 | }).then(function(e) { 177 | t.captchaCode = e.code 178 | }).catch(function() { 179 | return {} 180 | }) 181 | } 182 | } 183 | } 184 | }, 185 | 330: function(t, e, a) { 186 | "use strict"; 187 | Object.defineProperty(e, "__esModule", { 188 | value: !0 189 | }); 190 | var s = a(140) 191 | , o = babelHelpers.interopRequireDefault(s) 192 | , i = a(113) 193 | , c = babelHelpers.interopRequireDefault(i); 194 | e.default = { 195 | components: { 196 | Captcha: o.default 197 | }, 198 | props: ["apihost", "current", "is-app"], 199 | data: function() { 200 | return { 201 | isShowLayer: void 0, 202 | interval: null, 203 | countdown: 0, 204 | mobilePhone: "", 205 | msgCaptcha: "", 206 | msgToken: "" 207 | } 208 | }, 209 | computed: { 210 | isMobile: function t() { 211 | var t = /(^(13\d|15[^4,\D]|17[13678]|18\d)\d{8}|170[^346,\D]\d{7})$/; 212 | return t.test(this.mobilePhone) 213 | }, 214 | enableShowLayer: function() { 215 | return !this.isShowLayer && this.isMobile 216 | }, 217 | buttonText: function() { 218 | return "undefined" == typeof this.isShowLayer ? "获取验证码" : this.isShowLayer ? "发送中..." : "重新获取" 219 | } 220 | }, 221 | methods: { 222 | showLayer: function() { 223 | this.enableShowLayer && (this.$broadcast("getCaptcha"), 224 | this.isShowLayer = !0) 225 | }, 226 | hideLayer: function() { 227 | this.isShowLayer = !1 228 | }, 229 | getMsgCaptcha: function(t) { 230 | var e = this 231 | , a = { 232 | mobile: this.mobilePhone, 233 | scene: "login", 234 | type: "sms" 235 | }; 236 | t && (this.isShowLayer = !1, 237 | a.captcha_code = t), 238 | c.default.post(this.apihost + "/v4/mobile/verify_code/send", a).then(function(t) { 239 | e.setCountdown(), 240 | e.msgToken = t.validate_token, 241 | e.isShowLayer = !1 242 | }).catch(function(t) { 243 | "NEED_CAPTCHA" === t.name ? e.showLayer() : e.$dispatch("setToast", t.message) 244 | }) 245 | }, 246 | setCountdown: function() { 247 | var t = this 248 | , e = function() { 249 | if (t.countdown--, 250 | t.countdown <= 0) 251 | return clearInterval(t.interval) 252 | }; 253 | this.countdown = 30, 254 | this.interval = setInterval(e, 1e3) 255 | }, 256 | loginByMsg: function() { 257 | var t = this; 258 | return this.mobilePhone ? this.isMobile ? this.msgToken ? this.msgCaptcha ? void c.default.post(this.apihost + "/v1/login/app_mobile", { 259 | validate_token: this.msgToken, 260 | code: this.msgCaptcha, 261 | mobile: this.mobilePhone 262 | }).then(function() { 263 | c.default.redirect() 264 | }).catch(function(e) { 265 | t.msgCaptcha = "", 266 | t.$dispatch("setToast", e.message) 267 | }) : this.$dispatch("setToast", "请填写验证码") : this.$dispatch("setToast", "请获取验证码") : this.$dispatch("setToast", "请填写合法的手机号") : this.$dispatch("setToast", "请填写手机号") 268 | } 269 | } 270 | } 271 | }, 272 | 331: function(t, e, a) { 273 | "use strict"; 274 | Object.defineProperty(e, "__esModule", { 275 | value: !0 276 | }); 277 | var s = a(140) 278 | , o = babelHelpers.interopRequireDefault(s) 279 | , i = a(1009) 280 | , c = babelHelpers.interopRequireDefault(i) 281 | , n = a(113) 282 | , r = babelHelpers.interopRequireDefault(n); 283 | e.default = { 284 | components: { 285 | Captcha: o.default, 286 | Switch: c.default 287 | }, 288 | props: ["apihost", "current", "is-app"], 289 | data: function() { 290 | return { 291 | captchaCode: "", 292 | userId: "", 293 | userPw: "", 294 | userCaptcha: "", 295 | showPwText: !1 296 | } 297 | }, 298 | computed: { 299 | captchaImage: function() { 300 | return this.captchaCode ? this.apihost + "/v1/captchas/" + this.captchaCode : void 0 301 | } 302 | }, 303 | methods: { 304 | getCaptchaCode: function() { 305 | var t = this; 306 | this.userCaptcha = "", 307 | r.default.post(this.apihost + "/v1/captchas").then(function(e) { 308 | t.captchaCode = e.code 309 | }).catch(function() { 310 | return {} 311 | }) 312 | }, 313 | loginByUserId: function() { 314 | var t = this 315 | , e = this.captchaCode && this.userCaptcha || !this.captchaCode; 316 | return this.userId ? this.userPw ? e ? void r.default.post(this.apihost + "/v2/login", { 317 | username: this.userId, 318 | password: this.userPw, 319 | captcha_code: this.userCaptcha 320 | }).then(function() { 321 | r.default.redirect() 322 | }).catch(function(e) { 323 | t.getCaptchaCode(), 324 | t.$dispatch("setToast", e.message) 325 | }) : this.$dispatch("setToast", "请填写验证码") : this.$dispatch("setToast", "密码不能为空") : this.$dispatch("setToast", "手机/邮箱/用户名 不能为空") 326 | } 327 | } 328 | } 329 | }, 330 | 332: function(t, e) { 331 | "use strict"; 332 | Object.defineProperty(e, "__esModule", { 333 | value: !0 334 | }), 335 | e.default = { 336 | props: { 337 | value: { 338 | type: Boolean, 339 | twoWay: !0 340 | } 341 | }, 342 | methods: { 343 | switchValue: function() { 344 | this.value = !this.value 345 | } 346 | } 347 | } 348 | }, 349 | 448: function(t, e) {}, 350 | 449: function(t, e) {}, 351 | 450: function(t, e) {}, 352 | 559: function(t, e) {}, 353 | 581: function(t, e) {}, 354 | 788: function(t, e) { 355 | t.exports = "
{{ headerOpt.switchName }}
" 356 | }, 357 | 789: function(t, e) { 358 | t.exports = "
已发送({{ countdown }}s)
{{ buttonText }}
温馨提示:未注册饿了么帐号的手机号,登录时将自动注册,且代表您已同意《用户服务协议》
" 359 | }, 360 | 790: function(t, e) { 361 | t.exports = "
看不清
换一张
" 362 | }, 363 | 878: function(t, e) { 364 | t.exports = '

请填写图形验证码

取消 确定
' 365 | }, 366 | 900: function(t, e) { 367 | t.exports = '
abc
···
' 368 | }, 369 | 1006: function(t, e, a) { 370 | var s, o, i = {}; 371 | a(450), 372 | s = a(328), 373 | o = a(788), 374 | t.exports = s || {}, 375 | t.exports.__esModule && (t.exports = t.exports.default); 376 | var c = "function" == typeof t.exports ? t.exports.options || (t.exports.options = {}) : t.exports; 377 | o && (c.template = o), 378 | c.computed || (c.computed = {}), 379 | Object.keys(i).forEach(function(t) { 380 | var e = i[t]; 381 | c.computed[t] = function() { 382 | return e 383 | } 384 | }) 385 | }, 386 | 1007: function(t, e, a) { 387 | var s, o, i = {}; 388 | a(448), 389 | s = a(330), 390 | o = a(789), 391 | t.exports = s || {}, 392 | t.exports.__esModule && (t.exports = t.exports.default); 393 | var c = "function" == typeof t.exports ? t.exports.options || (t.exports.options = {}) : t.exports; 394 | o && (c.template = o), 395 | c.computed || (c.computed = {}), 396 | Object.keys(i).forEach(function(t) { 397 | var e = i[t]; 398 | c.computed[t] = function() { 399 | return e 400 | } 401 | }) 402 | }, 403 | 1008: function(t, e, a) { 404 | var s, o, i = {}; 405 | a(449), 406 | s = a(331), 407 | o = a(790), 408 | t.exports = s || {}, 409 | t.exports.__esModule && (t.exports = t.exports.default); 410 | var c = "function" == typeof t.exports ? t.exports.options || (t.exports.options = {}) : t.exports; 411 | o && (c.template = o), 412 | c.computed || (c.computed = {}), 413 | Object.keys(i).forEach(function(t) { 414 | var e = i[t]; 415 | c.computed[t] = function() { 416 | return e 417 | } 418 | }) 419 | }, 420 | 1009: function(t, e, a) { 421 | var s, o, i = {}; 422 | a(581), 423 | s = a(332), 424 | o = a(900), 425 | t.exports = s || {}, 426 | t.exports.__esModule && (t.exports = t.exports.default); 427 | var c = "function" == typeof t.exports ? t.exports.options || (t.exports.options = {}) : t.exports; 428 | o && (c.template = o), 429 | c.computed || (c.computed = {}), 430 | Object.keys(i).forEach(function(t) { 431 | var e = i[t]; 432 | c.computed[t] = function() { 433 | return e 434 | } 435 | }) 436 | } 437 | }); 438 | --------------------------------------------------------------------------------