├── src ├── utils │ ├── ts │ │ ├── 这里文件只用作IDE的语法分析 │ │ ├── vue │ │ │ ├── plugin.d.ts │ │ │ ├── index.d.ts │ │ │ ├── vnode.d.ts │ │ │ ├── options.d.ts │ │ │ └── vue.d.ts │ │ ├── vuex │ │ │ ├── vue.d.ts │ │ │ ├── helpers.d.ts │ │ │ └── index.d.ts │ │ ├── vuerouter │ │ │ ├── vue.d.ts │ │ │ ├── index.d.ts │ │ │ └── router.d.ts │ │ ├── dewarn │ │ │ ├── dewarn.ts │ │ │ ├── server.ts │ │ │ └── weex.ts │ │ └── blockData.ts │ ├── vendors │ │ └── axios │ │ │ ├── index.js │ │ │ ├── lib │ │ │ ├── cancel │ │ │ │ ├── isCancel.js │ │ │ │ ├── Cancel.js │ │ │ │ └── CancelToken.js │ │ │ ├── helpers │ │ │ │ ├── bind.js │ │ │ │ ├── README.md │ │ │ │ ├── combineURLs.js │ │ │ │ ├── normalizeHeaderName.js │ │ │ │ ├── isAbsoluteURL.js │ │ │ │ ├── spread.js │ │ │ │ ├── deprecatedMethod.js │ │ │ │ ├── parseHeaders.js │ │ │ │ ├── btoa.js │ │ │ │ ├── cookies.js │ │ │ │ ├── buildURL.js │ │ │ │ └── isURLSameOrigin.js │ │ │ ├── core │ │ │ │ ├── README.md │ │ │ │ ├── enhanceError.js │ │ │ │ ├── createError.js │ │ │ │ ├── transformData.js │ │ │ │ ├── settle.js │ │ │ │ ├── InterceptorManager.js │ │ │ │ ├── dispatchRequest.js │ │ │ │ └── Axios.js │ │ │ ├── adapters │ │ │ │ ├── README.md │ │ │ │ └── stream.js │ │ │ ├── axios.js │ │ │ └── defaults.js │ │ │ └── LICENSE │ ├── load-js-file.js │ ├── cache.js │ ├── global.js │ ├── app │ │ ├── app-version-handler.js │ │ ├── handler.js │ │ ├── app-page-helper.js │ │ ├── app-uri-handler.js │ │ └── app-fetch.js │ ├── console.qrcode.js │ ├── class-util.js │ ├── timer.js │ ├── null-util.js │ ├── regex-util.js │ ├── promise-util.js │ ├── error.js │ ├── log.js │ └── array-util.js ├── a_sub_apps │ └── init │ │ ├── data │ │ ├── app.json │ │ └── entrance.json │ │ └── init_snippet.js ├── app-native.js ├── components │ ├── user │ │ ├── UserLoginRegister.vue │ │ └── UserLoginOrRegister.vue │ ├── home │ │ ├── SplitBar.vue │ │ ├── SliderBar.vue │ │ ├── EntranceBar.vue │ │ ├── HotBar.vue │ │ └── Block_RushBuy.vue │ ├── common │ │ ├── Loading.vue │ │ ├── BackButtonBgTransparent.vue │ │ ├── RefreshIndicator.vue │ │ ├── BackButton.vue │ │ ├── TabBar.vue │ │ └── PopupPageQuick.vue │ ├── order │ │ ├── TopBar.vue │ │ ├── OpratorBar.vue │ │ └── AddressBar.vue │ ├── item │ │ ├── ItemSliderBox.vue │ │ ├── ItemDetailBar.vue │ │ ├── TopBar.vue │ │ ├── ItemDetailBox.vue │ │ ├── OpratorBar.vue │ │ └── ItemDetailInfoSheet.vue │ └── address │ │ ├── StreetList.vue │ │ └── StreetPicker.vue ├── app-web.js ├── config.js ├── app-common.js ├── eventbus │ └── index.js ├── views │ ├── HomeTalk.vue │ ├── HomeCar.vue │ ├── HomeUser.vue │ ├── HomeQA.vue │ ├── Order.vue │ ├── AppInitNative.vue │ ├── UserLogin.vue │ ├── AppInitWeb.vue │ └── Item.vue ├── store │ ├── index.js │ ├── modules │ │ ├── user.js │ │ ├── address.js │ │ └── home.js │ └── common.js ├── entry-web.vue ├── index.tmpl.html ├── routes.js └── mixins │ └── index.js ├── assets └── config.js ├── .gitignore ├── tsconfig.json ├── test ├── loader-test.js ├── index.js ├── define.js ├── environment-test.js ├── log-test.js ├── fetch-test.js └── null-util-test.js ├── entry ├── Item.js ├── Order.js ├── HomeCar.js ├── HomeQA.js ├── AppInitWeb.js ├── HomeHome.js ├── HomeTalk.js ├── HomeUser.js ├── UserLogin.js ├── AddressSelector.js ├── AddressUpdater.js ├── AppInitNative.js └── AddressManagement.js ├── .eslintrc ├── .babelrc ├── .gitattributes ├── shop.html ├── webpack.test.config.js ├── webpack.build-entry.js ├── package.json └── README.md /src/utils/ts/这里文件只用作IDE的语法分析: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/config.js: -------------------------------------------------------------------------------- 1 | let CURRENT_IP = '192.168.100.100'; -------------------------------------------------------------------------------- /src/utils/vendors/axios/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/axios'); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *log 4 | *.t.js 5 | 6 | ################# 7 | ## IDEA 8 | ################# 9 | .idea/ -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/cancel/isCancel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function isCancel(value) { 4 | return !!(value && value.__CANCEL__); 5 | }; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./built", 4 | "allowJs": true, 5 | "target": "es5" 6 | }, 7 | "include": [ 8 | "./test/*" 9 | ] 10 | } -------------------------------------------------------------------------------- /test/loader-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/7/15 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/7/15 herbluo created 10 | */ 11 | -------------------------------------------------------------------------------- /src/utils/ts/vue/plugin.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue as _Vue } from "./vue"; 2 | 3 | export type PluginFunction = (Vue: typeof _Vue, options?: T) => void; 4 | 5 | export interface PluginObject { 6 | install: PluginFunction; 7 | [key: string]: any; 8 | } 9 | -------------------------------------------------------------------------------- /entry/Item.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/Item.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/Order.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/Order.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/HomeCar.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/HomeCar.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/HomeQA.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/HomeQA.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /src/a_sub_apps/init/data/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "updateTimestamp": 0, 3 | "appComponentVersion": [{ 4 | "id": 1, 5 | "name": "entrance", 6 | "version": "high-v1" 7 | }, { 8 | "id": 2, 9 | "name": "city", 10 | "version": "low-v1" 11 | }] 12 | } -------------------------------------------------------------------------------- /entry/AppInitWeb.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/AppInitWeb.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/HomeHome.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/HomeHome.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/HomeTalk.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/HomeTalk.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/HomeUser.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/HomeUser.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/UserLogin.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/UserLogin.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /src/app-native.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/26 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/26 herbluo created 10 | */ 11 | // only for native 12 | -------------------------------------------------------------------------------- /entry/AddressSelector.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/AddressSelector.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/AddressUpdater.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/AddressUpdater.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/AppInitNative.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/AppInitNative.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /entry/AddressManagement.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | import App from '../src/views/AddressManagement.vue' 3 | import store from '../src/store' 4 | import '../src/app-common' 5 | 6 | /* eslint-disable no-new */ 7 | new Vue({ 8 | el: '#root', 9 | store, 10 | render: h => h(App) 11 | }) 12 | 13 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/bind.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function bind(fn, thisArg) { 4 | return function wrap() { 5 | var args = new Array(arguments.length); 6 | for (var i = 0; i < args.length; i++) { 7 | args[i] = arguments[i]; 8 | } 9 | return fn.apply(thisArg, args); 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "sourceType": "module" 6 | }, 7 | "globals": { 8 | "Vue": false, 9 | "weex": false 10 | }, 11 | "extends": "standard", 12 | "plugins": [ 13 | "html" 14 | ], 15 | "rules": { 16 | "generator-star-spacing": 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/30 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/30 herbluo created 10 | */ 11 | 12 | import './define' 13 | 14 | import './environment-test' 15 | import './fetch-test' 16 | import './null-util-test' 17 | import './log-test' -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/README.md: -------------------------------------------------------------------------------- 1 | # axios // core 2 | 3 | The modules found in `core/` should be modules that are specific to the domain logic of axios. These modules would most likely not make sense to be consumed outside of the axios module, as their logic is too specific. Some examples of core modules are: 4 | 5 | - Dispatching requests 6 | - Managing interceptors 7 | - Handling config 8 | -------------------------------------------------------------------------------- /src/utils/ts/vuex/vue.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Extends interfaces in Vue.js 3 | */ 4 | 5 | import Vue = require("vue"); 6 | import { Store } from "./index"; 7 | 8 | declare module "vue/types/options" { 9 | interface ComponentOptions { 10 | store?: Store; 11 | } 12 | } 13 | 14 | declare module "vue/types/vue" { 15 | interface Vue { 16 | $store: Store; 17 | } 18 | } -------------------------------------------------------------------------------- /src/components/user/UserLoginRegister.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/README.md: -------------------------------------------------------------------------------- 1 | # axios // helpers 2 | 3 | The modules found in `helpers/` should be generic modules that are _not_ specific to the domain logic of axios. These modules could theoretically be published to npm on their own and consumed by other modules or apps. Some examples of generic modules are things like: 4 | 5 | - Browser polyfills 6 | - Managing cookies 7 | - Parsing HTTP headers 8 | -------------------------------------------------------------------------------- /test/define.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/30 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/30 herbluo created 10 | */ 11 | // Vue.axios 12 | const axios = require('../src/utils/vendors/axios/lib/axios') 13 | if (window) { 14 | window['Vue'] = {axios} 15 | } 16 | 17 | import 'weex-vue-render' -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-class-properties", 7 | "transform-object-rest-spread", 8 | "transform-function-bind", 9 | "transform-async-to-generator", 10 | [ 11 | "transform-runtime", 12 | { 13 | "helpers": false, 14 | "polyfill": false, 15 | "regenerator": true 16 | } 17 | ] 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/components/home/SplitBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/combineURLs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Creates a new URL by combining the specified URLs 5 | * 6 | * @param {string} baseURL The base URL 7 | * @param {string} relativeURL The relative URL 8 | * @returns {string} The combined URL 9 | */ 10 | module.exports = function combineURLs(baseURL, relativeURL) { 11 | return baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, ''); 12 | }; 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/normalizeHeaderName.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = function normalizeHeaderName(headers, normalizedName) { 6 | utils.forEach(headers, function processHeader(value, name) { 7 | if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { 8 | headers[normalizedName] = value; 9 | delete headers[name]; 10 | } 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /src/utils/load-js-file.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/27 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/27 herbluo created 10 | */ 11 | const loadJsFile = (src) => { 12 | const oHead = document.getElementsByTagName('HEAD').item(0) 13 | const oScript = document.createElement('script') 14 | oScript.src = src 15 | oHead.appendChild(oScript) 16 | } 17 | 18 | export { 19 | loadJsFile 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/cancel/Cancel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * A `Cancel` is an object that is thrown when an operation is canceled. 5 | * 6 | * @class 7 | * @param {string=} message The message. 8 | */ 9 | function Cancel(message) { 10 | this.message = message; 11 | } 12 | 13 | // Cancel.prototype.toString = function toString() { 14 | // return 'Cancel' + (this.message ? ': ' + this.message : ''); 15 | // }; 16 | 17 | Cancel.prototype.__CANCEL__ = true; 18 | 19 | module.exports = Cancel; 20 | -------------------------------------------------------------------------------- /src/app-web.js: -------------------------------------------------------------------------------- 1 | // only for web 2 | /* 3 | * 如若当前是web平台,提醒一下 4 | */ 5 | import './utils/log' 6 | import store from './store' 7 | import router from './routes' 8 | import {isWeb} from './utils/global' 9 | import rebuildVuebus from './app-common' 10 | 11 | if (!isWeb) { 12 | console.error('不可将app.js打包至非web平台') 13 | } 14 | 15 | /* 16 | * App主界面 17 | */ 18 | const App = require('./entry-web.vue') 19 | 20 | App.el = '#App' 21 | App.router = router 22 | App.store = store 23 | export default new Vue(App) 24 | 25 | rebuildVuebus({ 26 | router 27 | }) 28 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/7/12 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/7/12 herbluo created 10 | */ 11 | export const serverUrlBase = 'http://192.168.100.100:8080' 12 | export const appUrlBase = 'http://192.168.100.100:89' 13 | 14 | export const logLevel = 'debug' 15 | // export const logLevel = 'info' 16 | // export const logLevel = 'warn' 17 | export const debugModel = true 18 | // export const debugModel = false 19 | -------------------------------------------------------------------------------- /src/utils/cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/18 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/18 herbluo created 10 | */ 11 | const LRU = require('lru-cache') 12 | const options = { 13 | max: 50, 14 | maxAge: 1000 * 60 * 10 // 10分钟 15 | } 16 | const cache = LRU(options) 17 | 18 | export default cache 19 | 20 | // only for de_warn 21 | // eslint-disable-next-line 22 | if (0) { 23 | let cacheT = cache 24 | cache.get = cacheT.get 25 | cache.set = cacheT.set 26 | } 27 | -------------------------------------------------------------------------------- /src/app-common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * for web and native 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/26 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/26 herbluo created 10 | */ 11 | 12 | import store from './store' 13 | import mixins from './mixins' 14 | import {rebuildVuebus} from './eventbus' 15 | 16 | Vue.mixin(mixins) 17 | 18 | const _rebuildVuebus = (config) => { 19 | rebuildVuebus({ 20 | store, 21 | ...config 22 | }) 23 | } 24 | 25 | _rebuildVuebus() 26 | 27 | export default _rebuildVuebus 28 | -------------------------------------------------------------------------------- /src/eventbus/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/27 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/27 herbluo created 10 | */ 11 | /** 12 | * vue 的一个公交车 13 | * 管理一些简单事件 14 | */ 15 | let vuebus = new Vue() 16 | 17 | const rebuildVuebus = (config) => { 18 | vuebus = new Vue(config) 19 | } 20 | 21 | export { 22 | /* 23 | * TODO WARNING `THE MUTABLE EXPORT`, 24 | * TODO YOU COULD CHANGE IT ONLY BY `rebuildVuebus` 25 | */ 26 | vuebus, 27 | rebuildVuebus 28 | } 29 | -------------------------------------------------------------------------------- /test/environment-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/30 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/30 herbluo created 10 | */ 11 | import {assert} from 'chai' 12 | 13 | describe('environment', () => { 14 | it('this is a browser environment', () => { 15 | assert.isTrue(!!window) 16 | }) 17 | it('Vue is declared', () => { 18 | assert.isTrue(!!Vue) 19 | }) 20 | it('axios is functioned', () => { 21 | assert.typeOf(Vue.axios, 'function') 22 | }) 23 | }) -------------------------------------------------------------------------------- /src/components/common/Loading.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 27 | 28 | 33 | 34 | -------------------------------------------------------------------------------- /src/views/HomeTalk.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /src/utils/ts/vuerouter/vue.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Augment the typings of Vue.js 3 | */ 4 | 5 | import Vue = require("vue"); 6 | import VueRouter = require("./index"); 7 | import { Route, RawLocation, NavigationGuard } from "./index"; 8 | 9 | declare module "vue/types/vue" { 10 | interface Vue { 11 | $router: VueRouter; 12 | $route: Route; 13 | } 14 | } 15 | 16 | declare module "vue/types/options" { 17 | interface ComponentOptions { 18 | router?: VueRouter; 19 | beforeRouteEnter?: NavigationGuard; 20 | beforeRouteLeave?: NavigationGuard; 21 | beforeRouteUpdate?: NavigationGuard; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/enhanceError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Update an Error with the specified config, error code, and response. 5 | * 6 | * @param {Error} error The error to update. 7 | * @param {Object} config The config. 8 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 9 | @ @param {Object} [response] The response. 10 | * @returns {Error} The error. 11 | */ 12 | module.exports = function enhanceError(error, config, code, response) { 13 | error.config = config; 14 | if (code) { 15 | error.code = code; 16 | } 17 | error.response = response; 18 | return error; 19 | }; 20 | -------------------------------------------------------------------------------- /src/views/HomeCar.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | 14 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /src/views/HomeUser.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | 14 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/isAbsoluteURL.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Determines whether the specified URL is absolute 5 | * 6 | * @param {string} url The URL to test 7 | * @returns {boolean} True if the specified URL is absolute, otherwise false 8 | */ 9 | module.exports = function isAbsoluteURL(url) { 10 | // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). 11 | // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed 12 | // by any combination of letters, digits, plus, period, or hyphen. 13 | return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); 14 | }; 15 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/createError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var enhanceError = require('./enhanceError'); 4 | 5 | /** 6 | * Create an Error with the specified message, config, error code, and response. 7 | * 8 | * @param {string} message The error message. 9 | * @param {Object} config The config. 10 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 11 | @ @param {Object} [response] The response. 12 | * @returns {Error} The created error. 13 | */ 14 | module.exports = function createError(message, config, code, response) { 15 | var error = new Error(message); 16 | return enhanceError(error, config, code, response); 17 | }; 18 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/transformData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | /** 6 | * Transform the data for a request or a response 7 | * 8 | * @param {Object|String} data The data to be transformed 9 | * @param {Array} headers The headers for the request or response 10 | * @param {Array|Function} fns A single function or Array of functions 11 | * @returns {*} The resulting transformed data 12 | */ 13 | module.exports = function transformData(data, headers, fns) { 14 | /*eslint no-param-reassign:0*/ 15 | utils.forEach(fns, function transform(fn) { 16 | data = fn(data, headers); 17 | }); 18 | 19 | return data; 20 | }; 21 | -------------------------------------------------------------------------------- /src/views/HomeQA.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | 14 | 24 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /src/utils/global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/5/25 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/5/25 herbluo created 10 | */ 11 | /** 12 | * @type {'iOS'|'Android'|'Web'} 13 | */ 14 | const platform = weex.config.platform || weex.config.env.platform 15 | const isWeb = platform === 'Web' 16 | const isDesktop = (() => { 17 | try { 18 | if (navigator.userAgent.match(/(iPhone|iPod|Android|ios|iPad)/i)) { 19 | return false 20 | } 21 | } catch (e) { 22 | return false 23 | } 24 | return true 25 | })() 26 | 27 | export { 28 | platform, 29 | isWeb, 30 | isDesktop 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/spread.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Syntactic sugar for invoking a function and expanding an array for arguments. 5 | * 6 | * Common use case would be to use `Function.prototype.apply`. 7 | * 8 | * ```js 9 | * function f(x, y, z) {} 10 | * var args = [1, 2, 3]; 11 | * f.apply(null, args); 12 | * ``` 13 | * 14 | * With `spread` this example can be re-written. 15 | * 16 | * ```js 17 | * spread(function(x, y, z) {})([1, 2, 3]); 18 | * ``` 19 | * 20 | * @param {Function} callback 21 | * @returns {Function} 22 | */ 23 | module.exports = function spread(callback) { 24 | return function wrap(arr) { 25 | return callback.apply(null, arr); 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /shop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | weex-vue-demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/utils/app/app-version-handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/14 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/14 herbluo created 10 | */ 11 | function firstUpperCase (str) { 12 | return str[0].toUpperCase() + str.slice(1) 13 | } 14 | 15 | const appVersionHandler = (app) => { 16 | if (!app || !app.appComponentVersion || !(app.appComponentVersion instanceof Array)) { 17 | console.warn('参数有误') 18 | return 19 | } 20 | 21 | app.appComponentVersion.forEach((a) => { 22 | app[`app${firstUpperCase(a.name)}Version`] = a.version 23 | }) 24 | 25 | app.appComponentVersion = [] 26 | } 27 | 28 | export { 29 | appVersionHandler 30 | } 31 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 所有mutation事件类型必带后缀 MUTATION 3 | * 常见的action前缀有 FETCH 等; 如无,则必带后缀 ACTION 4 | * 5 | * any question or idea, email to i@closx.com 6 | * @author HerbLuo 7 | * @date 2017/4/22 8 | * @license Licensed under the MIT license. 9 | * 10 | * change logs: 11 | * 2017/4/22 herbluo created 12 | */ 13 | 14 | /* ********* 15 | import 16 | ********* */ 17 | import Vuex from 'vuex' 18 | import {isWeb} from '../utils/global' 19 | 20 | isWeb || Vue.use(Vuex) 21 | 22 | const store = new Vuex.Store({}) 23 | 24 | const registerModuleIfNotExist = (moduleName, module) => { 25 | if (!store.state[moduleName]) { 26 | store.registerModule(moduleName, module) 27 | } 28 | } 29 | 30 | export default store 31 | export { 32 | registerModuleIfNotExist 33 | } 34 | -------------------------------------------------------------------------------- /test/log-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/21 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/21 herbluo created 10 | */ 11 | 12 | import '../src/utils/log' 13 | 14 | describe('log', () => { 15 | it('please open the console and see the output', () => { 16 | 17 | console.log('%cthe color is blue', 'color:blue') 18 | console.log({msg: 'this is an object'}) 19 | 20 | console.info('this is an info') 21 | 22 | console.warn('this is a warn') 23 | console.warn(new Error('this is an error warn')) 24 | console.warn({msg: 'this is an object warn'}) 25 | 26 | console.error('this is an error and with a dialog show') 27 | 28 | }) 29 | }) -------------------------------------------------------------------------------- /src/utils/ts/vuerouter/index.d.ts: -------------------------------------------------------------------------------- 1 | import "./vue"; 2 | import * as R from "./router"; 3 | 4 | // `VueRouter` in `export = VueRouter` must be a namespace 5 | // All available types are exported via this namespace 6 | declare namespace VueRouter { 7 | export type RouterMode = R.RouterMode; 8 | export type RawLocation = R.RawLocation; 9 | export type RedirectOption = R.RedirectOption; 10 | export type RouterOptions = R.RouterOptions; 11 | export type RouteConfig = R.RouteConfig; 12 | export type RouteRecord = R.RouteRecord; 13 | export type Location = R.Location; 14 | export type Route = R.Route; 15 | export type NavigationGuard = R.NavigationGuard; 16 | } 17 | 18 | // TS cannot merge imported class with namespace, declare a subclass to bypass 19 | declare class VueRouter extends R.VueRouter {} 20 | 21 | export = VueRouter; 22 | -------------------------------------------------------------------------------- /webpack.test.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/30 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/30 herbluo created 10 | */ 11 | module.exports = { 12 | devtool: 'source-map', 13 | entry: { 14 | app: 'mocha!babel!./test/index.js', 15 | }, 16 | output: { 17 | path: './dist/test/', 18 | filename: '[name].test.js' 19 | }, 20 | module: { 21 | loaders: [ 22 | { 23 | test: /\.ts$/, 24 | loader: 'ts-loader', 25 | exclude: /node_modules/ 26 | }, 27 | { 28 | test: /\.js$/, 29 | loader: 'babel', 30 | exclude: /node_modules/ 31 | }, 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/settle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var createError = require('./createError'); 4 | 5 | /** 6 | * Resolve or reject a Promise based on response status. 7 | * 8 | * @param {Function} resolve A function that resolves the promise. 9 | * @param {Function} reject A function that rejects the promise. 10 | * @param {object} response The response. 11 | */ 12 | module.exports = function settle(resolve, reject, response) { 13 | var validateStatus = response.config.validateStatus; 14 | // Note: status is not exposed by XDomainRequest 15 | if (!response.status || !validateStatus || validateStatus(response.status)) { 16 | resolve(response); 17 | } else { 18 | reject(createError( 19 | 'Request failed with status code ' + response.status, 20 | response.config, 21 | null, 22 | response 23 | )); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/deprecatedMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*eslint no-console:0*/ 4 | 5 | /** 6 | * Supply a warning to the developer that a method they are using 7 | * has been deprecated. 8 | * 9 | * @param {string} method The name of the deprecated method 10 | * @param {string} [instead] The alternate method to use if applicable 11 | * @param {string} [docs] The documentation URL to get further details 12 | */ 13 | module.exports = function deprecatedMethod(method, instead, docs) { 14 | try { 15 | console.warn( 16 | 'DEPRECATED method `' + method + '`.' + 17 | (instead ? ' Use `' + instead + '` instead.' : '') + 18 | ' This method will be removed in a future release.'); 19 | 20 | if (docs) { 21 | console.warn('For more information about usage see ' + docs); 22 | } 23 | } catch (e) { /* Ignore */ } 24 | }; 25 | -------------------------------------------------------------------------------- /src/entry-web.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 14 | -------------------------------------------------------------------------------- /src/components/common/BackButtonBgTransparent.vue: -------------------------------------------------------------------------------- 1 | 7 | 15 | 16 | 30 | 31 | 43 | 44 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/parseHeaders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | /** 6 | * Parse headers into an object 7 | * 8 | * ``` 9 | * Date: Wed, 27 Aug 2014 08:58:49 GMT 10 | * Content-Type: application/json 11 | * Connection: keep-alive 12 | * Transfer-Encoding: chunked 13 | * ``` 14 | * 15 | * @param {String} headers Headers needing to be parsed 16 | * @returns {Object} Headers parsed into an object 17 | */ 18 | module.exports = function parseHeaders(headers) { 19 | var parsed = {}; 20 | var key; 21 | var val; 22 | var i; 23 | 24 | if (!headers) { return parsed; } 25 | 26 | utils.forEach(headers.split('\n'), function parser(line) { 27 | i = line.indexOf(':'); 28 | key = utils.trim(line.substr(0, i)).toLowerCase(); 29 | val = utils.trim(line.substr(i + 1)); 30 | 31 | if (key) { 32 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 33 | } 34 | }); 35 | 36 | return parsed; 37 | }; 38 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/adapters/README.md: -------------------------------------------------------------------------------- 1 | # axios // adapters 2 | 3 | The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received. 4 | 5 | ## Example 6 | 7 | ```js 8 | var settle = require('./../core/settle'); 9 | 10 | module.exports myAdapter(config) { 11 | // At this point: 12 | // - config has been merged with defaults 13 | // - request transformers have already run 14 | // - request interceptors have already run 15 | 16 | // Make the request using config provided 17 | // Upon response settle the Promise 18 | 19 | return new Promise(function(resolve, reject) { 20 | 21 | var response = { 22 | data: responseData, 23 | status: request.status, 24 | statusText: request.statusText, 25 | headers: responseHeaders, 26 | config: config, 27 | request: request 28 | }; 29 | 30 | settle(resolve, reject, response); 31 | 32 | // From here: 33 | // - response transformers will run 34 | // - response interceptors will run 35 | }); 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /src/index.tmpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shop-WebApp 6 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 |
App 加载中...
24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Matt Zabriskie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/components/order/TopBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 18 | 19 | 48 | 49 | 59 | 60 | -------------------------------------------------------------------------------- /src/utils/console.qrcode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/27 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/27 herbluo created 10 | */ 11 | import QRCode from 'qrcode' 12 | 13 | const canvasSupport = () => { 14 | return !document.createElement('testcanvas').getContext 15 | } 16 | 17 | const isPlatformSupport = (() => { 18 | try { 19 | return canvasSupport() 20 | } catch (e) { 21 | return false 22 | } 23 | })() 24 | 25 | if (isPlatformSupport) { 26 | const canvas = document.createElement('canvas') 27 | console.qrcode = (str) => { 28 | // noinspection JSUnresolvedFunction 29 | QRCode.toCanvas(canvas, str, (error) => { 30 | if (error) { 31 | console.error(error) 32 | return 33 | } 34 | const dataUrl = canvas.toDataURL() 35 | const width = (canvas.width / 1.8) | 0 36 | const height = (canvas.height / 1.8) | 0 37 | console.log('%c', 38 | `padding:${width}px ${height}px; 39 | line-height:${2 * height + 20}px; 40 | background:url(${dataUrl}) no-repeat`) 41 | }) 42 | } 43 | } else { 44 | console.error('浏览器不支持canvas') 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/btoa.js: -------------------------------------------------------------------------------- 1 | // 'use strict'; 2 | 3 | // btoa polyfill for IE<10 courtesy https://github.com/davidchambers/Base64.js 4 | 5 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 6 | 7 | function E() { 8 | this.message = 'String contains an invalid character'; 9 | } 10 | E.prototype = new Error; 11 | E.prototype.code = 5; 12 | // E.name = 'InvalidCharacterError'; 13 | 14 | function btoa(input) { 15 | var str = String(input); 16 | var output = ''; 17 | for ( 18 | // initialize result and counter 19 | var block, charCode, idx = 0, map = chars; 20 | // if the next str index does not exist: 21 | // change the mapping table to "=" 22 | // check if d has no fractional digits 23 | str.charAt(idx | 0) || (map = '=', idx % 1); 24 | // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 25 | output += map.charAt(63 & block >> 8 - idx % 1 * 8) 26 | ) { 27 | charCode = str.charCodeAt(idx += 3 / 4); 28 | if (charCode > 0xFF) { 29 | throw new E(); 30 | } 31 | block = block << 8 | charCode; 32 | } 33 | return output; 34 | } 35 | 36 | module.exports = btoa; 37 | -------------------------------------------------------------------------------- /src/utils/class-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/28 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/28 herbluo created 10 | */ 11 | import _ from 'lodash' 12 | 13 | /** 14 | * 返回一个代理, 15 | * 16 | * 慎用:注意可能不存在Proxy的垫片 17 | * 18 | * 该代理会将传入对象的 19 | * 所有方法的this指向重新绑定到该对象 20 | * 21 | * @author 阮一峰 22 | * @link http://es6.ruanyifeng.com/#docs/class#this-的指向 23 | */ 24 | export function selfish (target) { 25 | const cache = [] 26 | const handler = { 27 | get (target, key) { 28 | const value = Reflect.get(target, key) 29 | if (typeof value !== 'function') { 30 | return value 31 | } 32 | if (!cache[value]) { 33 | cache[value] = value.bind(target) 34 | } 35 | return cache[value] 36 | } 37 | } 38 | return new Proxy(target, handler) 39 | } 40 | 41 | /** 42 | * 43 | * @param obj 需要判断的对象 44 | * @param arr ['属性', '属性.深层属性'] 45 | */ 46 | export function hasProps (obj, arr) { 47 | let has = true 48 | arr.forEach(p => { 49 | /* has 为 false 直接退出 */ 50 | has && 51 | /* 不存在该属性的情况下 */ !_.has(obj, p) && 52 | /* 将has置为 false */(has = false) 53 | }) 54 | return has 55 | } 56 | -------------------------------------------------------------------------------- /src/utils/app/handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/5/23 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/5/23 herbluo created 10 | */ 11 | import _ from 'lodash/core' 12 | import _groupBy from 'lodash/groupBy' 13 | 14 | _.mixin({'groupBy': _groupBy}) 15 | 16 | export const self = t => t 17 | 18 | /** 19 | * 将服务器数据转化成 group数组 并返回 20 | * 21 | * 说明:服务器返回了多组数据(减少http请求数),所以需要分组 22 | * 该函数返回的数据可以缓存并且建议缓存 23 | * 24 | * @param key 25 | * @param. data 第二阶 需处理的数据 26 | * (服务端数据) 27 | * (如返回数据为page数据,需预处理为数组) 28 | */ 29 | export const group = (key = 'index') => data => 30 | _.chain(data) // 服务端原始数据 31 | .forEach(addTimestamp) // 增加时间戳信息 32 | .groupBy(key) // 对数据按index进行分组 33 | .map(self) // 将集合对象object转换成数组对象Array 34 | .value() 35 | 36 | /** 37 | * 过滤期满数据 38 | * 39 | * 默认期满时间为5天 40 | * 41 | * @param timeMs 42 | * @param. data {any []} 43 | */ 44 | export const expireDataFilter = (timeMs = 1000 * 60 * 60 * 24 * 5/* 五天 */) => 45 | data => _.filter(data, d => new Date().getTime() - d.timestamp < timeMs) 46 | 47 | /** 48 | * 增加时间戳信息 49 | */ 50 | export const addTimestamp = o => 51 | o.timestamp || (o.timestamp = new Date().getTime()) 52 | -------------------------------------------------------------------------------- /src/components/common/RefreshIndicator.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 46 | 47 | 58 | 59 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/23 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/23 herbluo created 10 | */ 11 | /* ********* 12 | import 13 | ********* */ 14 | 15 | /* ********* 16 | variables 17 | ********* */ 18 | const actions = {} 19 | const mutations = {} 20 | const getters = {} 21 | 22 | /* ********* 23 | actions 24 | ********* */ 25 | 26 | /* ********* 27 | mutations 28 | ********* */ 29 | /** 30 | * 保存登陆信息,转换登陆状态 31 | * 32 | * payload.access_token 登录成功后服务器返回的token 33 | * payload.username 用户名 34 | */ 35 | const SET_USER_ACCESS_TOKEN_ACTION = 'SET_USER_ACCESS_TOKEN_ACTION' 36 | mutations[SET_USER_ACCESS_TOKEN_ACTION] = function (state, payload) { 37 | state.access_token = payload.access_token 38 | 39 | let username = payload.username 40 | username && (state.username = username) 41 | 42 | state.login = true 43 | } 44 | 45 | /* ********* 46 | exports 47 | ********* */ 48 | export default { 49 | state: { 50 | login: false, 51 | username: '', 52 | access_token: '' 53 | }, 54 | getters: { 55 | ...getters 56 | }, 57 | mutations, 58 | actions 59 | } 60 | 61 | export { 62 | SET_USER_ACCESS_TOKEN_ACTION 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/ts/dewarn/dewarn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ghosted 3 | * @date 2017/3/30. 4 | */ 5 | 6 | declare module namespace { 7 | 8 | interface console { 9 | info(str: any); 10 | log(str: any); 11 | error(str: any); 12 | oldError(str: any); 13 | } 14 | 15 | interface state { 16 | address: { 17 | addresses: Array 18 | }; 19 | app: { 20 | appDataInStorage: any, // 必有一个不为空 21 | appDataInServer: any, // 必有一个不为空 22 | appDataType: any, 23 | entrance: any 24 | }; 25 | block: { 26 | ji_you_jia: { 27 | head: Array, 28 | content: Array 29 | }, 30 | rush_buy: { 31 | content: Array 32 | } 33 | }; 34 | city: { 35 | provCityArea: any; 36 | streetss: any; 37 | }; 38 | home: { 39 | refreshing: boolean 40 | }; 41 | item: { 42 | // 当前浏览的item 43 | currentItemId: number, 44 | // 缓存的item 45 | items: any, 46 | // 缓存的item对应的id(顺序只和数据新旧有关) 47 | itemIds: Array, 48 | // 商品详情 49 | itemDetails: { 50 | 'item-id': { 51 | descirbes: string 52 | } 53 | } 54 | }; 55 | user: { 56 | login: boolean, 57 | username: string, 58 | access_token: string 59 | }; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/views/Order.vue: -------------------------------------------------------------------------------- 1 | 7 | 16 | 17 | 34 | 35 | 61 | 62 | -------------------------------------------------------------------------------- /src/utils/timer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/24 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/24 herbluo created 10 | */ 11 | 12 | const timerMap = [] 13 | 14 | function interval (func, delay, id) { 15 | if (timerMap[id]) { 16 | setTimeout(() => { 17 | func() 18 | interval(func, delay, id) 19 | }, delay) 20 | } 21 | } 22 | 23 | /** 24 | * interval 兼容函数 25 | */ 26 | export function timer (func, delay) { 27 | const length = timerMap.length 28 | timerMap[length] = true 29 | interval(func, delay, length) 30 | return length 31 | } 32 | 33 | export function clearTimer (id) { 34 | timerMap[id] = false 35 | } 36 | 37 | /** 38 | * 返回一个超时的reject 39 | */ 40 | export function getATimeoutReject (time) { 41 | return new Promise((resolve, reject) => { 42 | setTimeout(() => reject(new Error('timeout')), time) 43 | }) 44 | } 45 | 46 | /** 47 | * 返回一个超时的resolve 48 | */ 49 | export function getATimeoutResolve (time) { 50 | return new Promise(resolve => { 51 | setTimeout(resolve, time) 52 | }) 53 | } 54 | 55 | /** 56 | * .....与timeout竞争 57 | * @param promise 58 | * @param time 59 | * @return {Promise.<*>} 60 | */ 61 | export function raceWithTimeout (promise, time) { 62 | return Promise.race([promise, getATimeoutReject(time)]) 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/InterceptorManager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | function InterceptorManager() { 6 | this.handlers = []; 7 | } 8 | 9 | /** 10 | * Add a new interceptor to the stack 11 | * 12 | * @param {Function} fulfilled The function to handle `then` for a `Promise` 13 | * @param {Function} rejected The function to handle `reject` for a `Promise` 14 | * 15 | * @return {Number} An ID used to remove interceptor later 16 | */ 17 | InterceptorManager.prototype.use = function use(fulfilled, rejected) { 18 | this.handlers.push({ 19 | fulfilled: fulfilled, 20 | rejected: rejected 21 | }); 22 | return this.handlers.length - 1; 23 | }; 24 | 25 | /** 26 | * Remove an interceptor from the stack 27 | * 28 | * @param {Number} id The ID that was returned by `use` 29 | */ 30 | InterceptorManager.prototype.eject = function eject(id) { 31 | if (this.handlers[id]) { 32 | this.handlers[id] = null; 33 | } 34 | }; 35 | 36 | /** 37 | * Iterate over all the registered interceptors 38 | * 39 | * This method is particularly useful for skipping over any 40 | * interceptors that may have become `null` calling `eject`. 41 | * 42 | * @param {Function} fn The function to call for each interceptor 43 | */ 44 | InterceptorManager.prototype.forEach = function forEach(fn) { 45 | utils.forEach(this.handlers, function forEachHandler(h) { 46 | if (h !== null) { 47 | fn(h); 48 | } 49 | }); 50 | }; 51 | 52 | module.exports = InterceptorManager; 53 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/cancel/CancelToken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cancel = require('./Cancel'); 4 | 5 | /** 6 | * A `CancelToken` is an object that can be used to request cancellation of an operation. 7 | * 8 | * @class 9 | * @param {Function} executor The executor function. 10 | */ 11 | function CancelToken(executor) { 12 | if (typeof executor !== 'function') { 13 | throw new TypeError('executor must be a function.'); 14 | } 15 | 16 | var resolvePromise; 17 | this.promise = new Promise(function promiseExecutor(resolve) { 18 | resolvePromise = resolve; 19 | }); 20 | 21 | var token = this; 22 | executor(function cancel(message) { 23 | if (token.reason) { 24 | // Cancellation has already been requested 25 | return; 26 | } 27 | 28 | token.reason = new Cancel(message); 29 | resolvePromise(token.reason); 30 | }); 31 | } 32 | 33 | /** 34 | * Throws a `Cancel` if cancellation has been requested. 35 | */ 36 | CancelToken.prototype.throwIfRequested = function throwIfRequested() { 37 | if (this.reason) { 38 | throw this.reason; 39 | } 40 | }; 41 | 42 | /** 43 | * Returns an object that contains a new `CancelToken` and a function that, when called, 44 | * cancels the `CancelToken`. 45 | */ 46 | CancelToken.source = function source() { 47 | var cancel; 48 | var token = new CancelToken(function executor(c) { 49 | cancel = c; 50 | }); 51 | return { 52 | token: token, 53 | cancel: cancel 54 | }; 55 | }; 56 | 57 | module.exports = CancelToken; 58 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/axios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var bind = require('./helpers/bind'); 5 | var Axios = require('./core/Axios'); 6 | var defaults = require('./defaults'); 7 | 8 | /** 9 | * Create an instance of Axios 10 | * 11 | * @param {Object} defaultConfig The default config for the instance 12 | * @return {Axios} A new instance of Axios 13 | */ 14 | function createInstance(defaultConfig) { 15 | var context = new Axios(defaultConfig); 16 | var instance = bind(Axios.prototype.request, context); 17 | 18 | // Copy axios.prototype to instance 19 | utils.extend(instance, Axios.prototype, context); 20 | 21 | // Copy context to instance 22 | utils.extend(instance, context); 23 | 24 | return instance; 25 | } 26 | 27 | // Create the default instance to be exported 28 | var axios = createInstance(defaults); 29 | 30 | // Expose Axios class to allow class inheritance 31 | axios.Axios = Axios; 32 | 33 | // Factory for creating new instances 34 | axios.create = function create(instanceConfig) { 35 | return createInstance(utils.merge(defaults, instanceConfig)); 36 | }; 37 | 38 | // Expose Cancel & CancelToken 39 | axios.Cancel = require('./cancel/Cancel'); 40 | axios.CancelToken = require('./cancel/CancelToken'); 41 | axios.isCancel = require('./cancel/isCancel'); 42 | 43 | // Expose all/spread 44 | axios.all = function all(promises) { 45 | return Promise.all(promises); 46 | }; 47 | axios.spread = require('./helpers/spread'); 48 | 49 | module.exports = axios; 50 | 51 | // Allow use of default import syntax in TypeScript 52 | module.exports.default = axios; 53 | -------------------------------------------------------------------------------- /src/components/order/OpratorBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 18 | 19 | 70 | 71 | 77 | 78 | -------------------------------------------------------------------------------- /src/utils/null-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/7/7 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/7/7 herbluo created 10 | */ 11 | 12 | function nullable (target) { 13 | 14 | target = {value: target} 15 | 16 | let handler = { 17 | get: function (target, key, receiver) { 18 | let targetValue = Reflect.get(target, 'value', receiver) 19 | 20 | if (key === 'value') { 21 | return targetValue 22 | } 23 | 24 | if (targetValue === null) { 25 | return nullable(null) 26 | } 27 | 28 | if (typeof targetValue !== 'object') { 29 | return nullable(undefined) 30 | } 31 | 32 | let valueValue = Reflect.get(targetValue, key, receiver) 33 | 34 | if (typeof valueValue === 'function') { 35 | return (...args) => { 36 | return nullable(valueValue(...args)) 37 | } 38 | } 39 | 40 | return nullable(valueValue) 41 | }, 42 | set: function () { 43 | // 暂不支持 44 | } 45 | } 46 | 47 | return new Proxy(target, handler) 48 | } 49 | 50 | function TRY (func) { 51 | try { 52 | return func() 53 | } catch (e) { 54 | } 55 | } 56 | 57 | /** 58 | * null propagation 59 | * null 传导 60 | * 61 | * 用法1(注意Proxy) 62 | * 允许 NP(null || undefined).uuu.uuu 63 | * 允许 NP({}).uuu.uuu.u.u 64 | * 65 | * 用法2 66 | * 允许 NP(() => undefined.u.uu.u) 67 | * 68 | */ 69 | export function NP (obj) { 70 | if (typeof obj === 'function') { 71 | return TRY(obj) 72 | } 73 | return nullable(obj) 74 | } 75 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/cookies.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = ( 6 | utils.isStandardBrowserEnv() ? 7 | 8 | // Standard browser envs support document.cookie 9 | (function standardBrowserEnv() { 10 | return { 11 | write: function write(name, value, expires, path, domain, secure) { 12 | var cookie = []; 13 | cookie.push(name + '=' + encodeURIComponent(value)); 14 | 15 | if (utils.isNumber(expires)) { 16 | cookie.push('expires=' + new Date(expires).toGMTString()); 17 | } 18 | 19 | if (utils.isString(path)) { 20 | cookie.push('path=' + path); 21 | } 22 | 23 | if (utils.isString(domain)) { 24 | cookie.push('domain=' + domain); 25 | } 26 | 27 | if (secure === true) { 28 | cookie.push('secure'); 29 | } 30 | 31 | document.cookie = cookie.join('; '); 32 | }, 33 | 34 | read: function read(name) { 35 | var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); 36 | return (match ? decodeURIComponent(match[3]) : null); 37 | }, 38 | 39 | remove: function remove(name) { 40 | this.write(name, '', Date.now() - 86400000); 41 | } 42 | }; 43 | })() : 44 | 45 | // Non standard browser env (web workers, react-native) lack needed support. 46 | (function nonStandardBrowserEnv() { 47 | return { 48 | write: function write() {}, 49 | read: function read() { return null; }, 50 | remove: function remove() {} 51 | }; 52 | })() 53 | ); 54 | -------------------------------------------------------------------------------- /src/store/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/16 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/16 herbluo created 10 | */ 11 | 12 | import {vuebus} from '../eventbus' 13 | import {getATimeoutReject} from '../utils/timer' 14 | 15 | const isFromServerMap = {} 16 | 17 | /** 18 | * (获取App版本信息) 19 | * 当闪存版本或服务器版本初始化完毕时 20 | * 21 | * 若执行了 storage类型的回调,可能会在服务端返回结果后再次回调 22 | * 若执行了server类型的回调,必然不会再次回调 23 | * 24 | */ 25 | export function whenVersioned (key, callWhenVersionGetted) { 26 | let state = vuebus.$store.state 27 | 28 | let version 29 | 30 | version = state.appDataInServer[key] 31 | if (version) { 32 | callWhenVersionGetted({ 33 | type: 'server', version 34 | }) 35 | return 36 | } 37 | 38 | version = state.appDataInStorage[key] 39 | if (version) { 40 | callWhenVersionGetted({type: 'storage', version}) 41 | return 42 | } 43 | 44 | vuebus.$watch('$store.state.appDataInServer.' + key, function (v) { 45 | isFromServerMap[key] = true 46 | callWhenVersionGetted({type: 'server', version: v}) 47 | }) 48 | 49 | vuebus.$watch('$store.state.appDataInStorage.' + key, function (v) { 50 | isFromServerMap[key] || callWhenVersionGetted({type: 'storage', version: v}) 51 | }) 52 | } 53 | 54 | /** 55 | * whenVersioned的promise版本 56 | * @param key 57 | * @return {Promise.<*>} 58 | */ 59 | export function whenVersioned$Promise (key) { 60 | return Promise.race([ 61 | new Promise(resolve => whenVersioned(key, resolve)), 62 | getATimeoutReject(8000) 63 | ]) 64 | } 65 | -------------------------------------------------------------------------------- /src/components/item/ItemSliderBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 23 | 24 | 64 | 65 | 75 | 76 | -------------------------------------------------------------------------------- /webpack.build-entry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author _啃Apple的猫 5 | * @see http://www.jianshu.com/p/497f1a9ff33f 6 | * 7 | * change logs: 8 | * 2017/8/22 herbluo created 9 | */ 10 | // build-entry.js 11 | const path = require('path') 12 | const fs = require('fs-extra') 13 | 14 | const srcPath = path.resolve(__dirname, './src/views') // 每个.vue页面 15 | const entryPath = path.resolve(__dirname, './entry/') // 存放入口文件的文件夹 16 | const FILE_TYPE = '.vue' 17 | 18 | const getEntryFileContent = path => { 19 | return `// 入口文件 20 | import App from '${path}${FILE_TYPE}' 21 | import store from '../src/store' 22 | import '../src/app-common' 23 | 24 | /* eslint-disable no-new */ 25 | new Vue({ 26 | el: '#root', 27 | store, 28 | render: h => h(App) 29 | }) 30 | 31 | ` 32 | } 33 | // 导出方法 34 | module.exports = () => { 35 | // 写入每个文件的入口文件 36 | fs.readdirSync(srcPath).forEach(file => { 37 | const fullpath = path.resolve(srcPath, file) 38 | const extname = path.extname(fullpath) 39 | const name = path.basename(file, extname) 40 | if (fs.statSync(fullpath).isFile() && extname === FILE_TYPE) { 41 | //写入vue渲染实例 42 | fs.outputFileSync(path.resolve(entryPath, name + '.js'), getEntryFileContent('../src/views/' + name)) 43 | } 44 | }) 45 | const entry = {} 46 | // 放入多个entry 47 | fs.readdirSync(entryPath).forEach(file => { 48 | const name = path.basename(file, path.extname(path.resolve(entryPath, file))) 49 | entry[name] = path.resolve(entryPath, name + '.js') 50 | }) 51 | return entry 52 | } 53 | 54 | // 作者:_啃Apple的猫 55 | // 链接:http://www.jianshu.com/p/497f1a9ff33f 56 | // 來源:简书 57 | // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 -------------------------------------------------------------------------------- /src/utils/ts/vue/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as V from "./vue"; 2 | import * as Options from "./options"; 3 | import * as Plugin from "./plugin"; 4 | import * as VNode from "./vnode"; 5 | 6 | // `Vue` in `export = Vue` must be a namespace 7 | // All available types are exported via this namespace 8 | declare namespace Vue { 9 | export type CreateElement = V.CreateElement; 10 | 11 | export type Component = Options.Component; 12 | export type AsyncComponent = Options.AsyncComponent; 13 | export type ComponentOptions = Options.ComponentOptions; 14 | export type FunctionalComponentOptions = Options.FunctionalComponentOptions; 15 | export type RenderContext = Options.RenderContext; 16 | export type PropOptions = Options.PropOptions; 17 | export type ComputedOptions = Options.ComputedOptions; 18 | export type WatchHandler = Options.WatchHandler; 19 | export type WatchOptions = Options.WatchOptions; 20 | export type DirectiveFunction = Options.DirectiveFunction; 21 | export type DirectiveOptions = Options.DirectiveOptions; 22 | 23 | export type PluginFunction = Plugin.PluginFunction; 24 | export type PluginObject = Plugin.PluginObject; 25 | 26 | export type VNodeChildren = VNode.VNodeChildren; 27 | export type VNodeChildrenArrayContents = VNode.VNodeChildrenArrayContents; 28 | export type VNode = VNode.VNode; 29 | export type VNodeComponentOptions = VNode.VNodeComponentOptions; 30 | export type VNodeData = VNode.VNodeData; 31 | export type VNodeDirective = VNode.VNodeDirective; 32 | } 33 | 34 | // TS cannot merge imported class with namespace, declare a subclass to bypass 35 | declare class Vue extends V.Vue {} 36 | 37 | export = Vue; 38 | -------------------------------------------------------------------------------- /src/components/common/BackButton.vue: -------------------------------------------------------------------------------- 1 | 7 | 14 | 15 | 32 | 33 | 69 | 70 | -------------------------------------------------------------------------------- /src/utils/ts/blockData.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author HerbLuo 3 | * @date 2017/7/21. 4 | */ 5 | interface HeadContent { 6 | id: number; 7 | type: number|string; 8 | img: string; 9 | text: string; 10 | timestramp?:number; 11 | } 12 | 13 | interface ContentContent { 14 | id: number; 15 | index: number; 16 | img: string; 17 | link: string; 18 | timestramp?:number; 19 | } 20 | 21 | interface Content { 22 | content: T[]; 23 | } 24 | 25 | interface Page { 26 | last: boolean; 27 | totalElements: number; 28 | totalPages: number; 29 | number: number; 30 | size: number; 31 | sort: any; 32 | first: boolean; 33 | numberOfElements: number; 34 | } 35 | 36 | interface ServerData_ { 37 | head: Content & Page; 38 | content: Content & Page; 39 | } 40 | 41 | interface ZippedData_ { 42 | head: HeadContent[][]; 43 | content: ContentContent[][]; 44 | } 45 | 46 | interface GroupedContent { 47 | [key: number]: T[] 48 | } 49 | 50 | interface GroupedData_ { 51 | head: GroupedContent; 52 | content: GroupedContent; 53 | } 54 | 55 | interface PackedData { 56 | version: string; 57 | entity: T; 58 | } 59 | 60 | interface UnhandledUIData_ { 61 | head: HeadContent[]; 62 | content: ContentContent[]; 63 | } 64 | 65 | interface UIData_ { 66 | head: [HeadContent, HeadContent]; 67 | content: [[ContentContent, ContentContent],[ContentContent, ContentContent]]; 68 | } 69 | 70 | interface BlockData_ { 71 | id: number; 72 | name: string; 73 | title: string; 74 | columnType: number; 75 | } 76 | 77 | namespace block { 78 | interface Event { 79 | type: 'storage' | 'server' | 'error' | 'timer'; 80 | data: T; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/buildURL.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | function encode(val) { 6 | return encodeURIComponent(val). 7 | replace(/%40/gi, '@'). 8 | replace(/%3A/gi, ':'). 9 | replace(/%24/g, '$'). 10 | replace(/%2C/gi, ','). 11 | replace(/%20/g, '+'). 12 | replace(/%5B/gi, '['). 13 | replace(/%5D/gi, ']'); 14 | } 15 | 16 | /** 17 | * Build a URL by appending params to the end 18 | * 19 | * @param {string} url The base of the url (e.g., http://www.google.com) 20 | * @param {object} [params] The params to be appended 21 | * @returns {string} The formatted url 22 | */ 23 | module.exports = function buildURL(url, params, paramsSerializer) { 24 | /*eslint no-param-reassign:0*/ 25 | if (!params) { 26 | return url; 27 | } 28 | 29 | var serializedParams; 30 | if (paramsSerializer) { 31 | serializedParams = paramsSerializer(params); 32 | } else if (utils.isURLSearchParams(params)) { 33 | serializedParams = params.toString(); 34 | } else { 35 | var parts = []; 36 | 37 | utils.forEach(params, function serialize(val, key) { 38 | if (val === null || typeof val === 'undefined') { 39 | return; 40 | } 41 | 42 | if (utils.isArray(val)) { 43 | key = key + '[]'; 44 | } 45 | 46 | if (!utils.isArray(val)) { 47 | val = [val]; 48 | } 49 | 50 | utils.forEach(val, function parseValue(v) { 51 | if (utils.isDate(v)) { 52 | v = v.toISOString(); 53 | } else if (utils.isObject(v)) { 54 | v = JSON.stringify(v); 55 | } 56 | parts.push(encode(key) + '=' + encode(v)); 57 | }); 58 | }); 59 | 60 | serializedParams = parts.join('&'); 61 | } 62 | 63 | if (serializedParams) { 64 | url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; 65 | } 66 | 67 | return url; 68 | }; 69 | -------------------------------------------------------------------------------- /src/utils/ts/vuex/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("vue"); 2 | 3 | type Dictionary = { [key: string]: T }; 4 | 5 | export function mapState (map: string[]): Dictionary<() => any>; 6 | export function mapState (namespace: string, map: string[]): Dictionary<() => any>; 7 | export function mapState (map: Dictionary): Dictionary<() => any>; 8 | export function mapState (namespace: string, map: Dictionary): Dictionary<() => any>; 9 | export function mapState ( 10 | map: Dictionary<(this: typeof Vue, state: S, getters: any) => any> 11 | ): Dictionary<() => any>; 12 | export function mapState ( 13 | namespace: string, 14 | map: Dictionary<(this: typeof Vue, state: S, getters: any) => any> 15 | ): Dictionary<() => any>; 16 | 17 | type MutationMethod = (...args: any[]) => void; 18 | export function mapMutations (map: string[]): Dictionary; 19 | export function mapMutations (namespace: string, map: string[]): Dictionary; 20 | export function mapMutations (map: Dictionary): Dictionary; 21 | export function mapMutations (namespace: string, map: Dictionary): Dictionary; 22 | 23 | export function mapGetters (map: string[]): Dictionary<() => any>; 24 | export function mapGetters (namespace: string, map: string[]): Dictionary<() => any>; 25 | export function mapGetters (map: Dictionary): Dictionary<() => any>; 26 | export function mapGetters (namespace: string, map: Dictionary): Dictionary<() => any>; 27 | 28 | type ActionMethod = (...args: any[]) => Promise; 29 | export function mapActions (map: string[]): Dictionary; 30 | export function mapActions (namespace: string, map: string[]): Dictionary; 31 | export function mapActions (map: Dictionary): Dictionary; 32 | export function mapActions (namespace: string, map: Dictionary): Dictionary; 33 | -------------------------------------------------------------------------------- /src/utils/app/app-page-helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/9/2 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/9/2 herbluo created 10 | */ 11 | import dao from '../../dao' 12 | import {createError} from '../error' 13 | 14 | /** 15 | * 读取路由参数 16 | * @param routerName {string} 17 | * @return {Promise} 18 | */ 19 | const readRouterQuery$Promise = (routerName) => { 20 | return dao.get__router_query().then(({data}) => { 21 | let query 22 | try { 23 | query = JSON.parse(data) 24 | } catch (e) { 25 | e.config = data 26 | return Promise.reject(e) 27 | } 28 | 29 | if (query && query.routerName === routerName) { 30 | return Promise.resolve(query) 31 | } 32 | 33 | return Promise.reject(createError('无法读取路由参数', query)) 34 | }) 35 | } 36 | 37 | /** 38 | * 保存路由参数 39 | * @param routerName {string} 40 | * @param routerQuery {object} 41 | * @return {Promise} 42 | */ 43 | const saveRouterQuery$Promise = (routerName, routerQuery) => { 44 | if (typeof routerQuery !== 'object') { 45 | return Promise.reject( 46 | createError('参数routerQuery必须是一个对象', routerQuery)) 47 | } 48 | 49 | routerQuery.routerName = routerName 50 | return dao.set__router_query(JSON.stringify(routerQuery)) 51 | } 52 | 53 | /** 54 | * back page 返回的界面名称 55 | * 例如登录请求,登录成功或失败后需要返回原有界面 56 | * 此方法用于设置原有界面名称 57 | * 58 | * @return {Promise} 59 | */ 60 | const saveBackPage = (pageName) => { 61 | return dao.set__backpage_name(pageName) 62 | } 63 | 64 | /** 65 | * back page 返回的界面名称 66 | * 例如登录请求,登录成功或失败后需要返回原有界面 67 | * 此方法用于读取原有界面名称 68 | * 69 | * @return {Promise} 70 | */ 71 | const readBackPage = () => { 72 | return dao.get__backpage_name().then(({data}) => data) 73 | } 74 | 75 | export { 76 | readRouterQuery$Promise, 77 | saveRouterQuery$Promise, 78 | readBackPage, 79 | saveBackPage 80 | } 81 | -------------------------------------------------------------------------------- /src/utils/ts/vue/vnode.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from "./vue"; 2 | 3 | export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string; 4 | 5 | export type VNodeChildren = VNodeChildrenArrayContents | [ScopedSlot] | string; 6 | export interface VNodeChildrenArrayContents { 7 | [x: number]: VNode | string | VNodeChildren; 8 | } 9 | 10 | export interface VNode { 11 | tag?: string; 12 | data?: VNodeData; 13 | children?: VNode[]; 14 | text?: string; 15 | elm?: Node; 16 | ns?: string; 17 | context?: Vue; 18 | key?: string | number; 19 | componentOptions?: VNodeComponentOptions; 20 | componentInstance?: Vue; 21 | parent?: VNode; 22 | raw?: boolean; 23 | isStatic?: boolean; 24 | isRootInsert: boolean; 25 | isComment: boolean; 26 | } 27 | 28 | export interface VNodeComponentOptions { 29 | Ctor: typeof Vue; 30 | propsData?: Object; 31 | listeners?: Object; 32 | children?: VNodeChildren; 33 | tag?: string; 34 | } 35 | 36 | export interface VNodeData { 37 | key?: string | number; 38 | slot?: string; 39 | scopedSlots?: { [key: string]: ScopedSlot }; 40 | ref?: string; 41 | tag?: string; 42 | staticClass?: string; 43 | class?: any; 44 | staticStyle?: { [key: string]: any }; 45 | style?: Object[] | Object; 46 | props?: { [key: string]: any }; 47 | attrs?: { [key: string]: any }; 48 | domProps?: { [key: string]: any }; 49 | hook?: { [key: string]: Function }; 50 | on?: { [key: string]: Function | Function[] }; 51 | nativeOn?: { [key: string]: Function | Function[] }; 52 | transition?: Object; 53 | show?: boolean; 54 | inlineTemplate?: { 55 | render: Function; 56 | staticRenderFns: Function[]; 57 | }; 58 | directives?: VNodeDirective[]; 59 | keepAlive?: boolean; 60 | } 61 | 62 | export interface VNodeDirective { 63 | readonly name: string; 64 | readonly value: any; 65 | readonly oldValue: any; 66 | readonly expression: any; 67 | readonly arg: string; 68 | readonly modifiers: { [key: string]: boolean }; 69 | } 70 | -------------------------------------------------------------------------------- /src/utils/regex-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 正则工具集 3 | * 4 | * 判断是否为url 5 | * 得到某个url参数 6 | * 7 | * any question or idea, email to i@closx.com 8 | * @author HerbLuo 9 | * @date 2017/3/29 10 | * @license Licensed under the MIT license. 11 | * @version 1.0.0 12 | * 13 | * change logs: 14 | * 2017/3/29 herbluo created 15 | */ 16 | const util = {} 17 | 18 | 19 | /** 20 | * 判断是否为url REGEX 21 | * @type {RegExp} 22 | */ 23 | const regex_isUrl = new RegExp('[a-zA-z]+://[^\s]*') 24 | /** 25 | * 判断是否为url 26 | * @param url 27 | * @return {boolean} 28 | */ 29 | util.isUrl = function (url) { 30 | return regex_isUrl.test(url) 31 | } 32 | 33 | 34 | /** 35 | * 得到某个url参数(不可用于参数数组) 36 | * 37 | * @param url 可选 38 | * @param key 必须 39 | * @return {*} 参数内容 || null 40 | */ 41 | util.urlParamGetter = function (url, key) { 42 | if (typeof url !== 'string' || (key !== undefined && typeof key !== 'string')) { 43 | throw new Error('参数错误, 方法签名为urlParamGetter') 44 | } 45 | 46 | // 存放url 中?后面的部分字符串 47 | let params = '' 48 | 49 | // function (key) 50 | if (key === undefined) { //第二个参数(key) 不存在时,第一个参数(url)为key 51 | key = url 52 | params = window.location.search 53 | } 54 | 55 | // function (url, key) 56 | else { 57 | let arrayt = url.split('?') 58 | for (let i = 1; i < arrayt.length; i++) { 59 | params = params + arrayt[i] 60 | } 61 | } 62 | 63 | /* 64 | * 参数处理完毕 65 | */ 66 | const result = new RegExp(key + '=([^&]*)').exec(params) 67 | return result === null ? null : result[1] 68 | 69 | } 70 | 71 | /** 72 | * 替换或追加某一个url参数 73 | * @param url 必须 74 | * @param key 必须 75 | * @param value 必须 76 | * @return {string} 77 | */ 78 | util.urlParamSetter = function (url, key, value) { 79 | if (url.indexOf(key) < 0) { 80 | // 不存在key 81 | return url.indexOf('?') > 0 ? `${url}&${key}=${value}` : `${url}?${key}=${value}` 82 | } 83 | return url.replace(new RegExp(`${key}=([^&]*)`), `${key}=${value}`) 84 | } 85 | 86 | 87 | /** 88 | * 导出 89 | */ 90 | export default util 91 | -------------------------------------------------------------------------------- /src/store/modules/address.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/7/7 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/7/7 herbluo created 10 | */ 11 | 12 | /* ********* 13 | import 14 | ********* */ 15 | import '../../utils/log' 16 | import api from '../../api' 17 | import _find from 'lodash/find' 18 | import {waitWhileLogin} from '../../utils/app/login' 19 | import * as loader from '../../utils/app/loader' 20 | import {get} from '../../utils/app/app-fetch' 21 | import {createError} from '../../utils/error' 22 | 23 | /* ********* 24 | variables 25 | ********* */ 26 | const actions = {} 27 | const mutations = {} 28 | const getters = {} 29 | 30 | /* ********* 31 | actions 32 | ********* */ 33 | /** 34 | * 获取收货地址 35 | */ 36 | const FETCH_ADDRESS = 'FETCH_ADDRESS' 37 | actions[FETCH_ADDRESS] = async function ({commit, state, rootState}) { 38 | if (state.addresses.length > 0) { 39 | return Promise.resolve() 40 | } 41 | 42 | // 如果未登录,尝试登录 43 | try { 44 | await waitWhileLogin() 45 | } catch (e) { 46 | return Promise.reject(createError('用户未登录', e)) 47 | } 48 | 49 | // 获取收货地址 50 | let user = rootState.user 51 | return loader 52 | .loadFromServer$Promise(get(api.url.getAddress( 53 | user.username, user.access_token))) 54 | .then((data) => { 55 | commit(SAVE_ADDRESSES, {addresses: data}) 56 | }) 57 | } 58 | 59 | /* ********* 60 | mutations 61 | ********* */ 62 | /** 63 | * 保存收货地址 64 | * 65 | * payload.addresses 收货地址数组 66 | */ 67 | const SAVE_ADDRESSES = 'SAVE_ADDRESSES' 68 | mutations[SAVE_ADDRESSES] = function (state, payload) { 69 | state.addresses = payload.addresses 70 | } 71 | 72 | /* ********* 73 | export 74 | ********* */ 75 | export default { 76 | state: { 77 | addresses: [] 78 | }, 79 | getters: { 80 | defaultAddress (state) { 81 | return _find(state.addresses, d => d.areDefault === true) 82 | }, 83 | ...getters 84 | }, 85 | mutations, 86 | actions 87 | } 88 | 89 | export { 90 | FETCH_ADDRESS 91 | } 92 | -------------------------------------------------------------------------------- /src/components/item/ItemDetailBar.vue: -------------------------------------------------------------------------------- 1 | 10 | 33 | 34 | 65 | 66 | 90 | 91 | -------------------------------------------------------------------------------- /src/utils/promise-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/28 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/28 herbluo created 10 | */ 11 | // Promise.prototype.log = function () { 12 | // let P = this.constructor 13 | // return this.then( 14 | // value => P.resolve().then(() => value), 15 | // reason => P.resolve(log.error(reason)).then(() => { 16 | // throw reason 17 | // }) 18 | // ) 19 | // } 20 | // 21 | // /** 22 | // * Promise对象的回调链, 23 | // * 不管以then方法或catch方法结尾, 24 | // * 要是最后一个方法抛出错误, 25 | // * 都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。 26 | // * 因此,我们可以提供一个done方法, 27 | // * 总是处于回调链的尾端, 28 | // * 保证抛出任何可能出现的错误。 29 | // * 30 | // * @author ruanyifeng 31 | // * @see http://es6.ruanyifeng.com/#docs/promise#两个有用的附加方法 32 | // * @param onFulfilled 33 | // * @param onRejected 34 | // */ 35 | // Promise.prototype.done = function (onFulfilled, onRejected) { 36 | // this.then(onFulfilled, onRejected) 37 | // .catch(function (reason) { 38 | // // 抛出一个全局错误 39 | // setTimeout(() => { 40 | // throw reason 41 | // }, 0) 42 | // }) 43 | // } 44 | // /** 45 | // * finally方法用于指定不管Promise对象最后状态如何, 46 | // * 都会执行的操作。 47 | // * 它与done方法的最大区别,它接受一个普通的回调函数作为参数, 48 | // * 该函数不管怎样都必须执行。 49 | // * 50 | // * @author ruanyifeng 51 | // * @see http://es6.ruanyifeng.com/#docs/promise#两个有用的附加方法 52 | // * @param callback 53 | // * @return {Promise.<*>} 54 | // */ 55 | // Promise.prototype.finally = function (callback) { 56 | // let P = this.constructor 57 | // return this.then( 58 | // value => P.resolve(callback()).then(() => value), 59 | // reason => P.resolve(callback()).then(() => { 60 | // throw reason 61 | // }) 62 | // ) 63 | // } 64 | 65 | const pUtil = { 66 | delay (delay) { 67 | return new Promise((resolve) => { 68 | setTimeout(resolve, delay) 69 | }) 70 | }, 71 | rejectDelay (delay) { 72 | return new Promise((resolve, reject) => setTimeout(reject, delay)) 73 | } 74 | } 75 | 76 | export default pUtil 77 | -------------------------------------------------------------------------------- /src/components/home/SliderBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 17 | 18 | 38 | 39 | 66 | 67 | -------------------------------------------------------------------------------- /src/components/item/TopBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 33 | 34 | 77 | 78 | 97 | 98 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/12 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/12 herbluo created 10 | */ 11 | import VueRouter from 'vue-router' 12 | 13 | const AppInitWeb = require('./views/AppInitWeb.vue') 14 | 15 | const HomeHome = require('./views/HomeHome.vue') 16 | const HomeTalk = require('./views/HomeTalk.vue') 17 | const HomeQA = require('./views/HomeQA.vue') 18 | const HomeCar = require('./views/HomeCar.vue') 19 | const HomeUser = require('./views/HomeUser.vue') 20 | 21 | const Item = require('./views/Item.vue') 22 | 23 | const UserLogin = require('./views/UserLogin.vue') 24 | 25 | const Order = require('./views/Order.vue') 26 | 27 | const AddressSelector = require('./views/AddressSelector.vue') 28 | const AddressManagement = require('./views/AddressManagement.vue') 29 | const AddressUpdater = require('./views/AddressUpdater.vue') 30 | 31 | const routes = [{ 32 | path: '/appinitweb/', 33 | name: 'AppInitWeb', 34 | component: AppInitWeb 35 | }, { 36 | path: '/home/home/', 37 | name: 'HomeHome', 38 | component: HomeHome 39 | }, { 40 | path: '/home/talk/', 41 | name: 'HomeTalk', 42 | component: HomeTalk 43 | }, { 44 | path: '/home/qa/', 45 | name: 'HomeQA', 46 | component: HomeQA 47 | }, { 48 | path: '/home/car/', 49 | name: 'HomeCar', 50 | component: HomeCar 51 | }, { 52 | path: '/home/user/', 53 | name: 'HomeUser', 54 | component: HomeUser 55 | }, { 56 | path: '/item/detail/', 57 | name: 'Item', 58 | component: Item 59 | }, { 60 | path: '/user/login/', 61 | name: 'UserLogin', 62 | component: UserLogin 63 | }, { 64 | path: '/order/', 65 | name: 'Order', 66 | component: Order 67 | }, { 68 | path: '/address/selector/', 69 | name: 'AddressSelector', 70 | component: AddressSelector 71 | }, { 72 | path: '/address/management/', 73 | name: 'AddressManagement', 74 | component: AddressManagement 75 | }, { 76 | path: '/address/updater/', 77 | name: 'AddressUpdater', 78 | component: AddressUpdater 79 | }] 80 | 81 | Vue.use(VueRouter) 82 | const router = new VueRouter({routes}) 83 | 84 | export default router 85 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/dispatchRequest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var transformData = require('./transformData'); 5 | var isCancel = require('../cancel/isCancel'); 6 | var defaults = require('../defaults'); 7 | 8 | /** 9 | * Throws a `Cancel` if cancellation has been requested. 10 | */ 11 | function throwIfCancellationRequested(config) { 12 | if (config.cancelToken) { 13 | config.cancelToken.throwIfRequested(); 14 | } 15 | } 16 | 17 | /** 18 | * Dispatch a request to the server using the configured adapter. 19 | * 20 | * @param {object} config The config that is to be used for the request 21 | * @returns {Promise} The Promise to be fulfilled 22 | */ 23 | module.exports = function dispatchRequest(config) { 24 | throwIfCancellationRequested(config); 25 | 26 | // Ensure headers exist 27 | config.headers = config.headers || {}; 28 | 29 | // Transform request data 30 | config.data = transformData( 31 | config.data, 32 | config.headers, 33 | config.transformRequest 34 | ); 35 | 36 | // Flatten headers 37 | config.headers = utils.merge( 38 | config.headers.common || {}, 39 | config.headers[config.method] || {}, 40 | config.headers || {} 41 | ); 42 | 43 | utils.forEach( 44 | ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], 45 | function cleanHeaderConfig(method) { 46 | delete config.headers[method]; 47 | } 48 | ); 49 | 50 | var adapter = config.adapter || defaults.adapter; 51 | 52 | return adapter(config).then(function onAdapterResolution(response) { 53 | throwIfCancellationRequested(config); 54 | 55 | // Transform response data 56 | response.data = transformData( 57 | response.data, 58 | response.headers, 59 | config.transformResponse 60 | ); 61 | 62 | return response; 63 | }, function onAdapterRejection(reason) { 64 | if (!isCancel(reason)) { 65 | throwIfCancellationRequested(config); 66 | 67 | // Transform response data 68 | if (reason && reason.response) { 69 | reason.response.data = transformData( 70 | reason.response.data, 71 | reason.response.headers, 72 | config.transformResponse 73 | ); 74 | } 75 | } 76 | 77 | return Promise.reject(reason); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/home/EntranceBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 55 | 56 | 97 | 98 | -------------------------------------------------------------------------------- /test/fetch-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/30 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/30 herbluo created 10 | */ 11 | 12 | import {expect, assert} from 'chai' 13 | 14 | // 用指定的 mutaions 测试 action 的辅助函数 15 | const testAction = ({action, args, state, expectedMutations, done}) => { 16 | let count = 0 17 | 18 | // 模拟提交 19 | const commit = (type, payload) => { 20 | const mutation = expectedMutations[count] 21 | 22 | try { 23 | expect(mutation.type).to.equal(type) 24 | if (payload) { 25 | if (typeof mutation.payload === 'function') { 26 | expect(mutation.payload(payload)).to.equal(true) 27 | } else { 28 | expect(mutation.payload).to.deep.equal(payload) 29 | } 30 | } 31 | } catch (error) { 32 | done(error) 33 | } 34 | 35 | count++ 36 | if (count >= expectedMutations.length) { 37 | done() 38 | } 39 | } 40 | 41 | // 用模拟的 store 和参数调用 action 42 | let result = action({commit, state}, ...args) 43 | 44 | // 检查是否没有 mutation 被 dispatch 45 | if (expectedMutations.length === 0) { 46 | expect(count).to.equal(0) 47 | done() 48 | } 49 | 50 | return result 51 | } 52 | 53 | import * as item from '../src/store/modules/item' 54 | 55 | describe('fetch data', () => { 56 | describe('item', () => { 57 | 58 | const actions = item['default'].actions 59 | const getState = () => item['default'].state 60 | 61 | // 商品详细信息(文字描述信息)拉取测试 62 | it(item.FETCH_ITEM_DETAIL_DESCRIBE, (done) => { 63 | let state = getState() 64 | testAction({ 65 | action: actions[item.FETCH_ITEM_DETAIL_DESCRIBE], 66 | args: [{itemId: 22}], 67 | state, 68 | expectedMutations: [{ 69 | type: 'SAVE_ITEM_DETAIL_DESCRIBE', payload: payload => { 70 | expect(payload.itemId).to.equal(22) 71 | expect(payload.describes).to.be.an('array').that.have.lengthOf(19) 72 | return true 73 | } 74 | }], 75 | done 76 | }).then().catch(error => { 77 | expect(error).to.equal('__ catch an error') 78 | }) 79 | }) 80 | 81 | 82 | }) 83 | }) -------------------------------------------------------------------------------- /src/utils/error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/3/29 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/3/29 herbluo created 10 | */ 11 | import './log' 12 | 13 | /* ********* 14 | error code 15 | ********* */ 16 | const errorCode = { 17 | /* ********* 18 | run time exception: 1xxx 19 | ********* */ 20 | 'IllegalArgument': ['1001', '参数错误'], 21 | 'ParseJsonError': ['1002', '解析json失败'], 22 | /* ********* 23 | network error: 2xxx 24 | ********* */ 25 | 'NetworkError': ['2000', '网络连接错误'], 26 | 'ServerError': ['2001', '服务端返回错误的结果'] 27 | } 28 | 29 | /* ********* 30 | 错误处理方案 31 | ********* */ 32 | const errorHandler = { 33 | /* ********* 34 | global 35 | ********* */ 36 | /** 37 | * 默认的错误处理方案 38 | */ 39 | default (e, str) { 40 | }, 41 | /* ********* 42 | network 43 | ********* */ 44 | /** 45 | * 检查网络是否良好 46 | * 47 | * TODO 未实现 48 | * @return {Promise} 49 | */ 50 | isNetWorkFine$Promise () { 51 | return new Promise((resolve, reject) => { 52 | resolve() 53 | }) 54 | }, 55 | /** 56 | * 等待网络通畅 57 | * 顺便尝试解决 网络链接错误 58 | * 59 | * TODO 未实现 60 | * @return {Promise.} when net is open 61 | */ 62 | waitWhileNetworkIsOpen$Promise () { 63 | return new Promise((resolve, reject) => { 64 | console.log('请检查网络链接') 65 | }) 66 | }, 67 | /** 68 | * 检查网络是否通畅 69 | * 如不通畅,尝试解决并等待通畅 70 | * 71 | * @return {Promise.<*>} resolve when network is fine, 72 | * reject when network is reopen 73 | */ 74 | checkNetworkAndWaitWhileNetworkIsReopen$Promise () { 75 | return this.isNetWorkFine$Promise() 76 | .then(::Promise.resolve) // made ,这里被坑了 77 | .catch((e) => { 78 | if (e) { 79 | console.error(e) 80 | } 81 | return this.waitWhileNetworkIsOpen$Promise() 82 | .then(::Promise.reject) 83 | }) 84 | } 85 | /* ********* 86 | 87 | ********* */ 88 | } 89 | 90 | /** 91 | * @param errorStr {string} 92 | * @param errorData {object} 93 | * @return {Error} 94 | */ 95 | const createError = (errorStr, errorData) => { 96 | const error = new Error(errorStr) 97 | error.config = errorData 98 | return error 99 | } 100 | /* ********* 101 | export 102 | ********* */ 103 | 104 | export { 105 | errorCode, 106 | errorHandler, 107 | createError 108 | } 109 | -------------------------------------------------------------------------------- /src/views/AppInitNative.vue: -------------------------------------------------------------------------------- 1 | 7 | 14 | 15 | 25 | 26 | 87 | 88 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/helpers/isURLSameOrigin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = ( 6 | utils.isStandardBrowserEnv() ? 7 | 8 | // Standard browser envs have full support of the APIs needed to test 9 | // whether the request URL is of the same origin as current location. 10 | (function standardBrowserEnv() { 11 | var msie = /(msie|trident)/i.test(navigator.userAgent); 12 | var urlParsingNode = document.createElement('a'); 13 | var originURL; 14 | 15 | /** 16 | * Parse a URL to discover it's components 17 | * 18 | * @param {String} url The URL to be parsed 19 | * @returns {Object} 20 | */ 21 | function resolveURL(url) { 22 | var href = url; 23 | 24 | if (msie) { 25 | // IE needs attribute set twice to normalize properties 26 | urlParsingNode.setAttribute('href', href); 27 | href = urlParsingNode.href; 28 | } 29 | 30 | urlParsingNode.setAttribute('href', href); 31 | 32 | // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils 33 | return { 34 | href: urlParsingNode.href, 35 | protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', 36 | host: urlParsingNode.host, 37 | search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', 38 | hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', 39 | hostname: urlParsingNode.hostname, 40 | port: urlParsingNode.port, 41 | pathname: (urlParsingNode.pathname.charAt(0) === '/') ? 42 | urlParsingNode.pathname : 43 | '/' + urlParsingNode.pathname 44 | }; 45 | } 46 | 47 | originURL = resolveURL(window.location.href); 48 | 49 | /** 50 | * Determine if a URL shares the same origin as the current location 51 | * 52 | * @param {String} requestURL The URL to test 53 | * @returns {boolean} True if URL shares the same origin, otherwise false 54 | */ 55 | return function isURLSameOrigin(requestURL) { 56 | var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; 57 | return (parsed.protocol === originURL.protocol && 58 | parsed.host === originURL.host); 59 | }; 60 | })() : 61 | 62 | // Non standard browser envs (web workers, react-native) lack needed support. 63 | (function nonStandardBrowserEnv() { 64 | return function isURLSameOrigin() { 65 | return true; 66 | }; 67 | })() 68 | ); 69 | -------------------------------------------------------------------------------- /src/mixins/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author herbluo modifier 5 | * @date 2017/8/26 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/26 herbluo created 10 | */ 11 | import {debugModel} from '../config' 12 | import {isWeb} from '../utils/global' 13 | import api from '../api/index' 14 | import {loadJsFile} from '../utils/load-js-file' 15 | 16 | /* ********* 17 | 调试相关 控制台输出二维码 18 | ********* */ 19 | const getNativeAppJsPath = ::api.app.getNativeAppJsPath 20 | 21 | /* 22 | * 控制台输出当前界面的二维码 23 | * 便于手机端扫描 24 | * 须在控制台输入 `showQr()` 25 | */ 26 | // web平台,debug模式下,往window里添加showQr方法以显示当前界面的 Native端的 二维码 27 | let showNativeFileUrlQr = (_) => { 28 | } 29 | 30 | const isDesktopWebPlatformAndDebugModel = debugModel && (typeof window === 'object') 31 | 32 | if (isDesktopWebPlatformAndDebugModel) { 33 | // 映射某些 Web平台 和 Native平台 不一样的界面 34 | const web2nativeMap = { 35 | 'AppInitWeb': 'AppInitNative' 36 | } 37 | 38 | // 动态加载Js文件 39 | loadJsFile(api.app.getConsoleQRCodeJs()) 40 | 41 | // 往window里添加 showQr 方法 42 | window.showQr = function (url) { 43 | url = url || getNativeAppJsPath(window.currentRouteName) 44 | ;(typeof console.qrcode === 'function') && ( 45 | console.qrcode(url) 46 | ) 47 | return url 48 | } 49 | showNativeFileUrlQr = (name) => { 50 | window.currentRouteName = web2nativeMap[name] || name 51 | } 52 | } 53 | 54 | /* ********* 55 | 跳转 56 | ********* */ 57 | const navigator = weex.requireModule('navigator') 58 | 59 | /** 60 | * 作者:_啃Apple的猫 61 | * 链接:http://www.jianshu.com/p/497f1a9ff33f 62 | * 來源:简书 63 | * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 64 | */ 65 | export default { 66 | methods: { 67 | push (name, animated, callBackWhenNative) { 68 | if (isWeb) { 69 | this.$router.push({name}) 70 | showNativeFileUrlQr(this.$route.name) 71 | } else { 72 | navigator.push({ 73 | url: getNativeAppJsPath(name), 74 | animated: animated || 'true' 75 | }, () => { 76 | callBackWhenNative && callBackWhenNative(navigator) 77 | }) 78 | } 79 | }, 80 | 81 | pop (animated) { 82 | if (isWeb) { 83 | window.history.back() 84 | showNativeFileUrlQr(this.$route.name) 85 | } else { 86 | navigator.pop({ 87 | animated: animated || 'true' 88 | }) 89 | } 90 | } 91 | } 92 | } 93 | 94 | // 作者:_啃Apple的猫 95 | // 链接:http://www.jianshu.com/p/497f1a9ff33f 96 | // 來源:简书 97 | // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 98 | -------------------------------------------------------------------------------- /src/views/UserLogin.vue: -------------------------------------------------------------------------------- 1 | 7 | 28 | 29 | 38 | 39 | 98 | 99 | -------------------------------------------------------------------------------- /src/components/item/ItemDetailBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 35 | 36 | 107 | 108 | 114 | 115 | -------------------------------------------------------------------------------- /src/utils/ts/dewarn/server.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author HerbLuo 3 | * @date 2017/8/26. 4 | */ 5 | /** 6 | * 主要用于模拟服务端拉取的数据 7 | */ 8 | declare namespace serverdata { 9 | 10 | interface app { 11 | updateTimestamp: number, 12 | appEntranceVersion: string, 13 | appCityVersion: string 14 | } 15 | 16 | interface item { 17 | id: number, 18 | enabled: boolean, 19 | modifyTime: number, 20 | version: string, 21 | name: string, 22 | price: number, 23 | description: string, 24 | picLinksJson: string|Array, 25 | itemSellingInfo: { 26 | itemId: number, 27 | quantity: number, 28 | sales: number, 29 | commentNum: number, 30 | score: number, 31 | inOrdering: number 32 | }, 33 | detailDivId: number, 34 | shop: { 35 | id: number, 36 | enabled: boolean, 37 | name: string, 38 | sellerId: number 39 | } 40 | } 41 | 42 | interface auth { 43 | access_token: string, 44 | tokem_type: string, 45 | refresh_token: string, 46 | expires_in: number, 47 | scope: string 48 | } 49 | 50 | interface address { 51 | id: number 52 | enabled: boolean 53 | address: string 54 | postCode: string 55 | receiverName: string 56 | phone: string 57 | userId: number 58 | areDefault: boolean 59 | } 60 | 61 | interface poi { 62 | adCode: string; 63 | adName: string; 64 | businessArea: string; 65 | cityCode: string; 66 | cityName: string; 67 | direction: string; 68 | distance: number; 69 | email: string; 70 | enter: { 71 | latitude: number; 72 | longitude: number; 73 | }; 74 | indoorData: { 75 | floor: number; 76 | floorName: string; 77 | poiId: string; 78 | }; 79 | indoorMap: boolean; 80 | latLonPoint: { 81 | latitude: number; 82 | longitude: number; 83 | }; 84 | parkingType: string; 85 | photos: [{ 86 | title: string; 87 | url: string; 88 | }]; 89 | poiExtension: { 90 | mRating: string; 91 | opentime: string; 92 | }; 93 | poiId: string; 94 | postcode: string; 95 | provinceCode: string; 96 | provinceName: string; 97 | shopID: string; 98 | snippet: string; 99 | subPois: any[]; 100 | tel: string; 101 | title: string; 102 | typeCode: string; 103 | typeDes: string; 104 | website: string; 105 | } 106 | 107 | interface describe { 108 | id: number 109 | describeJsonArray: string 110 | } 111 | 112 | } -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/core/Axios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var defaults = require('../defaults'); 4 | var utils = require('../utils'); 5 | var InterceptorManager = require('./InterceptorManager'); 6 | var dispatchRequest = require('./dispatchRequest'); 7 | var isAbsoluteURL = require('../helpers/isAbsoluteURL'); 8 | var combineURLs = require('../helpers/combineURLs'); 9 | 10 | /** 11 | * Create a new instance of Axios 12 | * 13 | * @param {Object} instanceConfig The default config for the instance 14 | */ 15 | function Axios(instanceConfig) { 16 | this.defaults = instanceConfig; 17 | this.interceptors = { 18 | request: new InterceptorManager(), 19 | response: new InterceptorManager() 20 | }; 21 | } 22 | 23 | /** 24 | * Dispatch a request 25 | * 26 | * @param {Object} config The config specific for this request (merged with this.defaults) 27 | */ 28 | Axios.prototype.request = function request(config) { 29 | /*eslint no-param-reassign:0*/ 30 | // Allow for axios('example/url'[, config]) a la fetch API 31 | if (typeof config === 'string') { 32 | config = utils.merge({ 33 | url: arguments[0] 34 | }, arguments[1]); 35 | } 36 | 37 | config = utils.merge(defaults, this.defaults, { method: 'get' }, config); 38 | 39 | // Support baseURL config 40 | if (config.baseURL && !isAbsoluteURL(config.url)) { 41 | config.url = combineURLs(config.baseURL, config.url); 42 | } 43 | 44 | // Hook up interceptors middleware 45 | var chain = [dispatchRequest, undefined]; 46 | var promise = Promise.resolve(config); 47 | 48 | this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { 49 | chain.unshift(interceptor.fulfilled, interceptor.rejected); 50 | }); 51 | 52 | this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { 53 | chain.push(interceptor.fulfilled, interceptor.rejected); 54 | }); 55 | 56 | while (chain.length) { 57 | promise = promise.then(chain.shift(), chain.shift()); 58 | } 59 | 60 | return promise; 61 | }; 62 | 63 | // Provide aliases for supported request methods 64 | utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { 65 | /*eslint func-names:0*/ 66 | Axios.prototype[method] = function(url, config) { 67 | return this.request(utils.merge(config || {}, { 68 | method: method, 69 | url: url 70 | })); 71 | }; 72 | }); 73 | 74 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 75 | /*eslint func-names:0*/ 76 | Axios.prototype[method] = function(url, data, config) { 77 | return this.request(utils.merge(config || {}, { 78 | method: method, 79 | url: url, 80 | data: data 81 | })); 82 | }; 83 | }); 84 | 85 | module.exports = Axios; 86 | -------------------------------------------------------------------------------- /src/a_sub_apps/init/data/entrance.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "high-v1", 3 | "entity": [ 4 | { 5 | "id": 1, 6 | "enabled": true, 7 | "index": 0, 8 | "name": "天猫", 9 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/26296458982d1396e14698be8f059ccef9db0917.png", 10 | "link": "wxshop://black" 11 | }, 12 | { 13 | "id": 2, 14 | "enabled": true, 15 | "index": 1, 16 | "name": "聚划算", 17 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/d7b907b22952ac44f9e2d1029f8d364ac4560a52.png", 18 | "link": "wxshop://black" 19 | }, 20 | { 21 | "id": 3, 22 | "enabled": true, 23 | "index": 2, 24 | "name": "天猫国际", 25 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/18224c9afd17fcf3e15bb03ea95c244e469a452d.png", 26 | "link": "wxshop://black" 27 | }, 28 | { 29 | "id": 4, 30 | "enabled": true, 31 | "index": 3, 32 | "name": "外卖", 33 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/992bc9a571c9418bd7e6feb8f49c6e9ab4aa44da.png", 34 | "link": "wxshop://black" 35 | }, 36 | { 37 | "id": 5, 38 | "enabled": true, 39 | "index": 4, 40 | "name": "天猫超市", 41 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/d0cb8b15a7fbc2ed7e1dd58a3fc5b0ae4cf423a5.png", 42 | "link": "wxshop://black" 43 | }, 44 | { 45 | "id": 6, 46 | "enabled": true, 47 | "index": 5, 48 | "name": "充值中心", 49 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/2fcb04d9c79bcf97d1d7e4d9ac1c15627b3f8c13.png", 50 | "link": "wxshop://black" 51 | }, 52 | { 53 | "id": 7, 54 | "enabled": true, 55 | "index": 6, 56 | "name": "飞猪旅行", 57 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/d2bf542dc0a4c015e16d0f5426abff9d17973979.png", 58 | "link": "wxshop://black" 59 | }, 60 | { 61 | "id": 8, 62 | "enabled": true, 63 | "index": 7, 64 | "name": "淘金币", 65 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/e9f60973ac470de8c2aebb77fc634abad9e49cb8.png", 66 | "link": "wxshop://black" 67 | }, 68 | { 69 | "id": 9, 70 | "enabled": true, 71 | "index": 8, 72 | "name": "到家", 73 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/a9737f07a51d5fe46a198dcd167674c5d70564e7.png", 74 | "link": "wxshop://black" 75 | }, 76 | { 77 | "id": 10, 78 | "enabled": true, 79 | "index": 9, 80 | "name": "更多", 81 | "img": "http://closx-shop.oss-cn-qingdao.aliyuncs.com/app/v0/55164ce1a4ca4db3ff00b729575162a56cd05c30.png", 82 | "link": "wxshop://black" 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shop-native", 3 | "description": "shop-native", 4 | "version": "0.1.0", 5 | "private": true, 6 | "main": "index.js", 7 | "keywords": [ 8 | "weex", 9 | "vue" 10 | ], 11 | "scripts": { 12 | "dev": "webpack --watch --progress", 13 | "serve": "node build/init.js -h 192.168.100.100 && serve -p 89", 14 | "start": "node webpack.dev.config.js", 15 | "debug": "weex-devtool -h 192.168.100.100 -p 8089", 16 | "test": "mocha", 17 | "test-server": "webpack-dev-server --progress --config ./webpack.test.config.js --host 0.0.0.0 --port 8091" 18 | }, 19 | "dependencies": { 20 | "axios": "^0.15.3", 21 | "babel-polyfill": "^6.6.1", 22 | "babel-runtime": "^6.6.1", 23 | "core-js": "^2.5.0", 24 | "crypto-js": "^3.1.9-1", 25 | "lodash": "^4.17.4", 26 | "lru-cache": "^4.0.2", 27 | "proxy-polyfill": "^0.1.7", 28 | "qrcode": "^0.9.0", 29 | "vue": "^2.3.3", 30 | "vue-resource": "^1.2.1", 31 | "vue-router": "^2.1.1", 32 | "vuex": "^2.1.1", 33 | "vuex-router-sync": "^4.0.1", 34 | "weex-vue-render": "^0.11.25" 35 | }, 36 | "devDependencies": { 37 | "babel-core": "^6.20.0", 38 | "babel-eslint": "^7.2.3", 39 | "babel-loader": "^6.2.9", 40 | "babel-plugin-transform-async-to-generator": "^6.24.1", 41 | "babel-plugin-transform-class-properties": "^6.24.1", 42 | "babel-plugin-transform-function-bind": "^6.22.0", 43 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 44 | "babel-plugin-transform-runtime": "^6.23.0", 45 | "babel-preset-es2015": "^6.18.0", 46 | "babel-preset-es2016": "^6.24.1", 47 | "babel-preset-es2017": "^6.24.1", 48 | "chai": "^4.0.2", 49 | "css-loader": "^0.26.1", 50 | "eslint": "^4.5.0", 51 | "eslint-config-standard": "^10.2.1", 52 | "eslint-friendly-formatter": "^3.0.0", 53 | "eslint-loader": "^1.9.0", 54 | "eslint-plugin-html": "^3.2.0", 55 | "eslint-plugin-import": "^2.7.0", 56 | "eslint-plugin-node": "^5.1.1", 57 | "eslint-plugin-promise": "^3.5.0", 58 | "eslint-plugin-standard": "^3.0.1", 59 | "file-loader": "^0.10.1", 60 | "friendly-errors-webpack-plugin": "^1.6.1", 61 | "generate-asset-webpack-plugin": "^0.3.0", 62 | "happypack": "^4.0.0-beta.2", 63 | "html-loader": "^0.5.1", 64 | "html-webpack-plugin": "^2.28.0", 65 | "ip": "^1.1.4", 66 | "json-loader": "^0.5.7", 67 | "mocha": "^3.4.2", 68 | "mocha-loader": "^1.1.1", 69 | "serve": "^1.4.0", 70 | "ts-loader": "^2.3.1", 71 | "url-loader": "^0.5.8", 72 | "vue-loader": "^12.2.2", 73 | "vue-template-compiler": "^2.3.3", 74 | "webpack": "^1.14.0", 75 | "webpack-dev-server": "^1.16.2", 76 | "weex-devtool": "^0.2.64", 77 | "weex-loader": "^0.4.1", 78 | "weex-vue-loader": "^0.2.5" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/components/home/HotBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 78 | 79 | 111 | 112 | -------------------------------------------------------------------------------- /src/components/user/UserLoginOrRegister.vue: -------------------------------------------------------------------------------- 1 | 7 | 23 | 24 | 81 | 82 | 120 | 121 | -------------------------------------------------------------------------------- /src/a_sub_apps/init/init_snippet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/26 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/26 herbluo created 10 | */ 11 | import dao from '../../dao' 12 | import {appVersionHandler} from '../../utils/app/app-version-handler' 13 | import {isWeb} from '../../utils/global' 14 | 15 | const modal = weex.requireModule('modal') 16 | 17 | const app = require('./data/app.json') 18 | const provCityArea = require('./data/city.json') 19 | const entranceBar = require('./data/entrance.json') 20 | 21 | /* TODO DEBUG S */ 22 | app.updateTimestamp = new Date().getTime() - (1000 * 60 * 5) 23 | /* TODO DEBUG E */ 24 | appVersionHandler(app) 25 | 26 | /* ********* 27 | 写入数据 28 | ********* */ 29 | // 写入完毕的回调 30 | let callbackWhenFinished 31 | 32 | // 注册回调 33 | const registerWhenInitSnippetFinished = (callback) => { 34 | callbackWhenFinished = callback 35 | } 36 | 37 | // 检查回调是否已注册 38 | const checkCallbackRegistered = () => { 39 | if (typeof callbackWhenFinished !== 'function') { 40 | const msg = '[INIT_SNIPPET] 未注册初始化完毕回调' 41 | modal.toast({ 42 | message: msg, 43 | duration: 2 44 | }) 45 | console.error(msg) 46 | return false 47 | } else { 48 | return true 49 | } 50 | } 51 | 52 | // main 53 | (async function () { 54 | console.log('INIT_SNIPPET 初始化代码开始执行') 55 | 56 | let events 57 | try { 58 | events = await Promise.all([ 59 | dao.set__app(JSON.stringify(app)), 60 | dao.set__entrance_bar(JSON.stringify(entranceBar)), 61 | dao.get__prov_city_area().then(({data}) => { 62 | try { 63 | // 该省份不存在,或者版本不一致 64 | if (!data || data.substring(0, 20).indexOf(app['appCityVersion']) < 0) { 65 | return dao.set__prov_city_area(JSON.stringify(provCityArea)) 66 | } 67 | } catch (e) { 68 | return dao.set__prov_city_area(JSON.stringify(provCityArea)) 69 | } 70 | 71 | return Promise.resolve({result: 'success'}) 72 | }) 73 | ]) 74 | } catch (e) { 75 | modal.toast({ 76 | message: 'INIT_SNIPPET 严重错误,dao模块代码逻辑错误', 77 | duration: 2 78 | }) 79 | throw e 80 | } 81 | 82 | if (!checkCallbackRegistered()) { 83 | return 84 | } 85 | 86 | // 写入失败 87 | for (let i = 0; i < events.length; i++) { 88 | if (events[i].result !== 'success') { 89 | console.warn('数据写入失败 \n初始化代码执行失败') 90 | callbackWhenFinished('fail') 91 | return 92 | } 93 | } 94 | 95 | console.log('INIT_SNIPPET 初始化代码执行完毕') 96 | callbackWhenFinished('success') 97 | })() 98 | 99 | // export 100 | 101 | ;(typeof window === 'object') && isWeb && ( 102 | registerWhenInitSnippetFinished(window.whenInitSnippetFinished) 103 | ) 104 | 105 | export { 106 | registerWhenInitSnippetFinished 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shop-native 2 | 3 | > 4 | > 基于 weex(vuejs)的shop-native 5 | > 同时使用了vuex, vue-router, axios(少许修改)。 6 | > 修改了[部分代码 # run it 5](#repair_async),使得async语法糖在安卓平台下也顺利运行。 7 | > 8 | > 特性: 9 | > 1. 动态加载初始化代码,在app首次打开时,初始化持久层数据。 10 | > 2. 启用了内存缓存和闪存缓存,当数据过旧时,采用服务端数据。 11 | > 3. app创建时,自动启用loading状态,当所有组件加载完毕后,转换状态为loaded,通知 java,oc 端。 12 | 13 | ## file structure 14 | 15 | * `src/*`: all source code 16 | * `app.js`: entrance of the Weex page 17 | * `build/*`: some build scripts 18 | * `dist/*`: where places generated code 19 | * `assets/*`: some assets for Web preview 20 | * `index.html`: a page with qrcode of Weex js bundle 21 | * `shop.html`: Web render 22 | * `.babelrc`: babel config (preset-2015, async, object-rest-spread) 23 | * `.eslintrc`: not used 24 | 25 | #### src structure 26 | * `a_sub_apps` sub app(动态拉取),当前只包括app初始化代码片段 27 | * `api` 包含:服务端api,组件地址,图片大小调整api 28 | * `components` vue组件,@see `views` 29 | * `dao` 数据持久化和读取层 30 | * `eventbus` 包含vuex无法处理和不方便处理的事件:全局事件和简单事件 31 | * `store` vuex store 32 | * `utils` utils 33 | * `views` 路由级界面 34 | 35 | ## npm scripts 36 | 37 | ```bash 38 | # build the two js bundles and watch file changes 39 | npm run dev 40 | 41 | # start a Web server at 192.16.137.1:89 42 | npm run serve 43 | 44 | # start weex-devtool for debugging with native 45 | npm run debug 46 | ``` 47 | 48 | ## run it 49 | 50 | 1. npm install 51 | 52 | 2. 运行[服务端代码](https://github.com/HerbLuo/shop-api)或者跳过此步 53 | 54 | 3. 修改 `/src/api/index.js` 下的服务端地址 *(default.)url.base* 为步骤2中的ip 55 |
或 `http://www.cloudself.cn/shop/` 56 | 57 | 4. 修改 `package.json` 下的 serve script,将ip修改成 本机可用的ip,port 可保持89不变 58 |
修改 `/src/api/index.js` 下的app地址 *(default.)app.appBase* 为上述的ip + port 59 | 60 | 5. 打开 `\node_modules\regenerator-runtime\runtime.js`, 进行如下修改 61 | ``` 62 | // 删除如下代码片段 63 | var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); 64 | GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; 65 | GeneratorFunctionPrototype.constructor = GeneratorFunction; 66 | ``` 67 | ``` 68 | // 同样的位置添加如下代码 69 | var o = Object.create(IteratorPrototype); 70 | var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = { 71 | constructor: GeneratorFunctionPrototype, 72 | __proto__: GeneratorFunctionPrototype.prototype.__proto__ 73 | } 74 | GeneratorFunction.prototype = Gp.constructor; 75 | GeneratorFunctionPrototype.constructor = GeneratorFunction; 76 | ``` 77 | >注释:代码使用了es2017 async API, babel 的转换器中有部分代码不与weex兼容 78 | 这是由于安卓平台下weex默认冻结了Object,导致object的constructor属性无法被设置, 79 | 如只使用iOS平台,可不必进行修改 80 | 81 | 6. npm run serve, npm run debug 82 | 83 | 7. open `http://ip:port/index.html` to show the QR code, 84 |
open `http://ip:port/shop.html` to show the Web render 85 | 86 | ## other 87 | [安卓端](https://github.com/HerbLuo/shop-android) 88 | 89 | [IOS请使用playground](https://github.com/apache/incubator-weex/tree/0.13-dev/ios) 90 | 91 | # License: 92 | 本项目的所有 __代码__ 均可自由使用,修改或者用来干其它任何事情,但需保留署名 93 | 94 | 本项目引用的所有 __图片资源__ 均非本人所有,禁止用于任何商业活动 95 | 96 | 使用其他资源,请联系 i@closx.com 97 | -------------------------------------------------------------------------------- /src/utils/app/app-uri-handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @author unascribed 6 | * @date 2017/5/26 7 | * @license Licensed under the MIT license. 8 | * 9 | * change logs: 10 | * 2017/5/26 herbluo created 11 | */ 12 | import _forEach from 'lodash/forEach' 13 | import {createError} from '../error' 14 | 15 | /** 16 | * this code snippet is from internet 17 | */ 18 | const urlPraseRegex = { 19 | protocol: /([^/]+:)\/\/(.*)/i, 20 | host: /(^[^:/]+)((?:\/|:|$)?.*)/, 21 | port: /:?([^/]*)(\/?.*)/, 22 | pathname: /([^?#]+)(\??[^#]*)(#?.*)/ 23 | } 24 | 25 | function _parseUrl (url) { 26 | const result = { 27 | href: url 28 | } 29 | 30 | _forEach(urlPraseRegex, (regex, key) => { 31 | let regexOut = regex.exec(url) 32 | 33 | result[key] = regexOut[1] 34 | if (key === 'pathname') { 35 | result.search = regexOut[2] 36 | result.hash = regexOut[3] 37 | } 38 | 39 | url = regexOut[2] || '/' // 下一轮循环用的url 40 | }) 41 | 42 | return result 43 | } 44 | 45 | /** 46 | * 得到某个url参数(不可用于参数数组) 47 | * @param key 参数名 48 | * @param params 49 | * @returns {*} 参数内容 || null 50 | */ 51 | const urlParamGetter = (key, params) => { 52 | if (!key) { 53 | return params 54 | } 55 | params = params || window.location.search 56 | const result = new RegExp(key + '=([^&]*)').exec(params) 57 | return result === null ? null : result[1] 58 | } 59 | 60 | class UriConfig { 61 | uri // 完整uri wxshop://router/item/?query={"id":22,"version":"2.0"} 62 | protocol // 协议 wxshop 63 | host // 主机名 router 64 | pathname // 主机名后面的 /item/ 65 | search // 查询参数 ?query={"id":22,"version":"2.0"} 66 | 67 | constructor (uri) { 68 | const res = _parseUrl(uri) 69 | if (!res) { 70 | throw new Error('无法解析uri') 71 | } 72 | 73 | this.uri = uri 74 | this.protocol = res.protocol 75 | this.host = res.host 76 | this.pathname = res.pathname 77 | this.search = res.search 78 | } 79 | 80 | /** 81 | * @return {RouterUriConfig} 82 | */ 83 | toRouterUriConfig () { 84 | if (this.host !== 'router') { 85 | throw createError('无法转换到router', this) 86 | } 87 | return new RouterUriConfig(this) 88 | } 89 | } 90 | 91 | const routerNameGetter$Regex = /^\/+(\w+)\/?/ 92 | const routerQueryGetter$Regex = /^\?query=({\S*})/ 93 | 94 | class RouterUriConfig { 95 | protoConfig 96 | routerName 97 | routerQuery 98 | 99 | constructor (protoConfig) { 100 | this.protoConfig = protoConfig 101 | 102 | let out = routerNameGetter$Regex.exec(protoConfig.pathname) 103 | if (!out || out.length < 2) { 104 | throw createError( 105 | '无法匹配router,可能不是router类型的uri', this.protoConfig) 106 | } 107 | this.routerName = out[1] 108 | 109 | out = routerQueryGetter$Regex.exec(this.protoConfig.search) 110 | if (out && out.length > 1) { 111 | this.routerQuery = JSON.parse(out[1]) 112 | } 113 | } 114 | } 115 | 116 | export { 117 | UriConfig, 118 | urlParamGetter 119 | } 120 | -------------------------------------------------------------------------------- /src/utils/ts/dewarn/weex.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ghosted 3 | * @date 2017/3/30. 4 | */ 5 | 6 | declare namespace dev { 7 | 8 | /** 9 | * Weex module 10 | */ 11 | interface module { 12 | loadSuccess(): void 13 | 14 | onStop(call: Function): void 15 | 16 | registerOnTouchEvent(call: Function): void 17 | 18 | unRegisterOnTouchEvent(): void 19 | } 20 | 21 | interface amap { 22 | chooseLocation(call: Function): void 23 | } 24 | 25 | interface qrview { 26 | scanner(call: Function): void 27 | } 28 | 29 | interface requireModule { 30 | (name: string): any 31 | } 32 | 33 | interface weex { 34 | requireModule: requireModule 35 | config: { 36 | platform: string 37 | } 38 | env: { 39 | platform: string 40 | } 41 | platform: string 42 | } 43 | 44 | interface global { 45 | weex: weex 46 | } 47 | 48 | } 49 | 50 | declare namespace refresh { 51 | interface pullingdownInterface { 52 | dy: number; 53 | pullingDistance: number; 54 | viewHeight: number; 55 | type: "pullingdown"; 56 | } 57 | } 58 | 59 | /* ********* 60 | modal 61 | ********* */ 62 | 63 | declare namespace navigator { 64 | interface navigator { 65 | push(config: object, callback?: Function); 66 | pop(callback?: Function); 67 | setNavBarRightItem(param?: string, callback?: Function); 68 | clearNavBarRightItem(param?: string, callback?: Function); 69 | setNavBarLeftItem(param?: string, callback?: Function); 70 | clearNavBarLeftItem(param?: string, callback?: Function); 71 | setNavBarMoreItem(param?: string, callback?: Function); 72 | clearNavBarMoreItem(param?: string, callback?: Function); 73 | setNavBarTitle(param?: string, callback?: Function); 74 | } 75 | } 76 | 77 | declare namespace storage { 78 | interface Storage { 79 | setItem(key: string, value: string, callback: Function); 80 | 81 | getItem(key: string, callback: Function); 82 | 83 | removeItem(key: string, callback: Function); 84 | 85 | getAllKeys(callback: Function); 86 | } 87 | } 88 | 89 | declare namespace modal { 90 | interface ModalConfig { 91 | message: string 92 | duration: number 93 | } 94 | 95 | interface ConfirmConfig { 96 | message: string 97 | okTitle: string 98 | cancelTitle: string 99 | } 100 | 101 | interface modal { 102 | toast(config: ModalConfig): void 103 | 104 | confirm(config: ConfirmConfig): void 105 | } 106 | } 107 | 108 | declare namespace fetch { 109 | interface FetchOptions { 110 | method: string; 111 | url: string; 112 | headers?: object; 113 | type: 'json' | 'text' | 'jsonp'; 114 | body?: string; 115 | } 116 | 117 | interface Response { 118 | status: number 119 | ok: boolean 120 | statusText: string 121 | data: object | string 122 | headers: object 123 | } 124 | 125 | interface FetchCallback { 126 | (Response): void 127 | } 128 | 129 | interface stream { 130 | fetch(options: FetchOptions, callback: FetchCallback) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/defaults.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var normalizeHeaderName = require('./helpers/normalizeHeaderName'); 5 | 6 | var PROTECTION_PREFIX = /^\)\]\}',?\n/; 7 | var DEFAULT_CONTENT_TYPE = { 8 | 'Content-Type': 'application/x-www-form-urlencoded' 9 | }; 10 | 11 | function setContentTypeIfUnset(headers, value) { 12 | if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { 13 | headers['Content-Type'] = value; 14 | } 15 | } 16 | 17 | function getDefaultAdapter() { 18 | let adapter; 19 | 20 | // if (typeof XMLHttpRequest !== 'undefined') { 21 | // // For browsers use XHR adapter 22 | // adapter = require('./adapters/xhr'); 23 | // } else 24 | if (typeof weex !== 'undefined') { 25 | adapter = require('./adapters/stream') 26 | } else if (typeof process !== 'undefined') { 27 | // For node use HTTP adapter 28 | adapter = require('./adapters/http'); 29 | } 30 | return adapter; 31 | } 32 | 33 | var defaults = { 34 | adapter: getDefaultAdapter(), 35 | 36 | transformRequest: [function transformRequest(data, headers) { 37 | normalizeHeaderName(headers, 'Content-Type'); 38 | if (utils.isFormData(data) || 39 | utils.isArrayBuffer(data) || 40 | utils.isStream(data) || 41 | utils.isFile(data) || 42 | utils.isBlob(data) 43 | ) { 44 | return data; 45 | } 46 | if (utils.isArrayBufferView(data)) { 47 | return data.buffer; 48 | } 49 | if (utils.isURLSearchParams(data)) { 50 | setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); 51 | return data.toString(); 52 | } 53 | if (utils.isObject(data)) { 54 | setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); 55 | return JSON.stringify(data); 56 | } 57 | return data; 58 | }], 59 | 60 | transformResponse: [function transformResponse(data) { 61 | /*eslint no-param-reassign:0*/ 62 | if (typeof data === 'string') { 63 | data = data.replace(PROTECTION_PREFIX, ''); 64 | try { 65 | data = JSON.parse(data); 66 | } catch (e) { /* Ignore */ 67 | } 68 | } 69 | return data; 70 | }], 71 | 72 | timeout: 0, 73 | 74 | xsrfCookieName: 'XSRF-TOKEN', 75 | xsrfHeaderName: 'X-XSRF-TOKEN', 76 | 77 | maxContentLength: -1, 78 | 79 | validateStatus: function validateStatus(status) { 80 | return status >= 200 && status < 300; 81 | } 82 | }; 83 | 84 | defaults.headers = { 85 | common: { 86 | 'Accept': 'application/json, text/plain, */*' 87 | } 88 | }; 89 | 90 | utils.forEach(['delete', 'get', 'head'], function forEachMehtodNoData(method) { 91 | defaults.headers[method] = {}; 92 | }); 93 | 94 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 95 | defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); 96 | }); 97 | 98 | module.exports = defaults; 99 | -------------------------------------------------------------------------------- /src/utils/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/8/19 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/8/19 herbluo created 10 | */ 11 | import _reduce from 'lodash/reduce' 12 | import {logLevel} from '../config' 13 | 14 | /** 15 | * 日志等级,转化后的数据类似 {assert:0, error: 1} 16 | * 17 | * @type {{string: number}} 18 | */ 19 | const LEVEL_MAP = _reduce([ 20 | 'assert', 21 | 'error', 22 | 'warn', 23 | 'info', // 所有未记录的级别,等同于info级别 24 | 'debug' 25 | ], (result, value, key) => { 26 | result[value] = key 27 | return result 28 | }, {}) 29 | 30 | const _level = LEVEL_MAP[logLevel] 31 | 32 | for (let m in console) { 33 | if (console.hasOwnProperty(m) && typeof console[m] === 'function') { 34 | // 日志等级过滤(未指定等级的按info计算) 35 | if ((LEVEL_MAP[m] || LEVEL_MAP['info']) > _level) { 36 | console[m] = () => { 37 | } 38 | } 39 | } 40 | } 41 | 42 | /* 43 | * 针对 warn 和 error特殊处理 44 | */ 45 | const fmt = require('util').format 46 | const modal = (() => weex.requireModule('modal'))() 47 | 48 | // noinspection JSUndefinedPropertyAssignment, JSUnresolvedVariable 49 | console.oldError = console.oldError || ::console.error 50 | // noinspection JSUndefinedPropertyAssignment, JSUnresolvedVariable 51 | console.oldWarn = console.oldWarn || ::console.warn 52 | 53 | const _showErrorDialog = (msg) => { 54 | const duration = msg.length / 18 55 | modal.toast({ 56 | message: msg, 57 | duration: duration < 1 ? 1 : duration 58 | }) 59 | } 60 | 61 | const __levelMap = { 62 | 'ERROR': 'red', 63 | 'WARN': 'orange' 64 | } 65 | 66 | /** 67 | * 格式化参数(添加颜色信息) 68 | * 返回 console.log 可用的的参数数组 69 | * 70 | * @param levelStr {'ERROR'|'WARN'} 71 | * @param args {Arguments} 72 | * @param args[0] {string} 73 | * @private 74 | */ 75 | const _formatArgs = (levelStr, args) => { 76 | args[0] = `[${new Date().getTime()}] ${levelStr} ${args[0]}\n` 77 | return [ 78 | '%c%s', 79 | 'color:' + __levelMap[levelStr], 80 | fmt(...args) 81 | ] 82 | } 83 | 84 | /** 85 | * console.error 参数一样,功能相似 不支持 %c 86 | * 87 | */ 88 | const printError = function () { 89 | if (typeof arguments[0] === 'string') { 90 | console.log(..._formatArgs('ERROR', arguments)) 91 | } else { 92 | // noinspection JSUnresolvedFunction 93 | console.oldError(...arguments) 94 | } 95 | _showErrorDialog(fmt(...arguments)) 96 | } 97 | 98 | /** 99 | * console.warn 100 | */ 101 | const printWarn = function () { 102 | if (typeof arguments[0] === 'string') { 103 | console.log(..._formatArgs('WARN', arguments)) 104 | } else { 105 | // noinspection JSUnresolvedFunction 106 | console.oldError(...arguments) 107 | } 108 | } 109 | 110 | /* 111 | * export 112 | */ 113 | console.error = printError 114 | console.warn = printWarn 115 | 116 | const Style = { 117 | styleGray: 'color:gray', 118 | styleBlue: 'color:blue', 119 | styleGreen: 'color:green', 120 | styleRed: 'color:red' 121 | } 122 | 123 | const logger = console 124 | export {logger, Style} 125 | -------------------------------------------------------------------------------- /src/store/modules/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/6/23 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/6/23 herbluo created 10 | */ 11 | /* ********* 12 | import 13 | ********* */ 14 | 15 | import '../../utils/log' 16 | import _find from 'lodash/find' 17 | 18 | /* ********* 19 | variables 20 | ********* */ 21 | const actions = {} 22 | const mutations = {} 23 | const getters = {} 24 | 25 | const refreshNeededComponentMap = [] // 保存需要刷新的组件 26 | 27 | /* ********* 28 | actions 29 | ********* */ 30 | /** 31 | * home 32 | * 状态转换 33 | * 34 | * 将状态设置为刷新中 35 | * 调用所有注册了 ON_REFRESHING 事件的组件 home_refreshNeededComponentMap.forEach(c.cb) 36 | * 37 | * @param commit 38 | */ 39 | const REFRESHING_ACTION = 'REFRESHING_ACTION' 40 | actions[REFRESHING_ACTION] = function ({commit}) { 41 | console.log('[STORE/HOME] 模块刷新中') 42 | // 状态转化为刷新中 43 | commit(_SET_STATE_REFRESHING) 44 | 45 | // 调用所有注册的refreshing 46 | refreshNeededComponentMap.forEach((componentMap, index) => { 47 | // 刷新各组件,参数为刷新完毕的回调 48 | componentMap.callWhenRefreshing(() => { 49 | commit(_SET_COMPONENT_REFRESHED, {index}) 50 | 51 | // 寻找是否存在刷新中的组件 52 | _find(refreshNeededComponentMap, val => !val.finished) || 53 | commit(SET_REFRESHED_MUTATION) // 未找到(全部已刷新) 54 | }) 55 | }) 56 | } 57 | 58 | /* ********* 59 | mutations 60 | ********* */ 61 | /** 62 | * home 63 | * 状态转换 64 | * 65 | * 将刷新中状态强制转换为刷新完毕 66 | * 可直接提交该请求,不必检查refreshing的状态 67 | * 68 | * 建议仅用于timeout, 69 | * 正常情况下,该状态会由子组件自动转换 70 | */ 71 | const SET_REFRESHED_MUTATION = 'SET_REFRESHED' 72 | mutations[SET_REFRESHED_MUTATION] = function (state) { 73 | if (state.refreshing === true) { 74 | state.refreshing = false 75 | } 76 | } 77 | 78 | /** 79 | * home 80 | * 注册事件 81 | * 82 | * 某些需刷新的组件 83 | * 注册 ON_REFRESHING(刷新中) 事件 84 | * 85 | * payload.callWhenRefreshing 当刷新时的事件 86 | * 87 | * @type {string} 88 | */ 89 | const ON_REFRESHING_MUTATION = 'ON_REFRESHING_MUTATION' 90 | mutations[ON_REFRESHING_MUTATION] = function (state, payload) { 91 | refreshNeededComponentMap.push({ 92 | finished: false, 93 | callWhenRefreshing: payload.callWhenRefreshing 94 | }) 95 | } 96 | 97 | /* 98 | * 私有 99 | * 将home状态设置为刷新中 100 | */ 101 | const _SET_STATE_REFRESHING = '_SET_STATE_REFRESHING' 102 | mutations[_SET_STATE_REFRESHING] = function (state) { 103 | state.refreshing = true 104 | } 105 | 106 | /* 107 | * 私有 108 | * 将某一组件的状态设置为 刷新完毕(由子组件间接调用) 109 | */ 110 | const _SET_COMPONENT_REFRESHED = '_SET_COMPONENT_REFRESHED' 111 | mutations[_SET_COMPONENT_REFRESHED] = function (state, payload) { 112 | refreshNeededComponentMap[payload.index].finished = true 113 | } 114 | 115 | /* ********* 116 | export 117 | ********* */ 118 | export default { 119 | state: { 120 | refreshing: false 121 | }, 122 | getters: { 123 | ...getters 124 | }, 125 | mutations, 126 | actions 127 | } 128 | 129 | export { 130 | REFRESHING_ACTION, 131 | ON_REFRESHING_MUTATION, 132 | SET_REFRESHED_MUTATION 133 | } 134 | -------------------------------------------------------------------------------- /src/utils/ts/vuex/index.d.ts: -------------------------------------------------------------------------------- 1 | import _Vue = require("vue"); 2 | import { WatchOptions } from "vue"; 3 | 4 | // augment typings of Vue.js 5 | import "./vue"; 6 | 7 | export * from "./helpers"; 8 | 9 | export declare class Store { 10 | constructor(options: StoreOptions); 11 | 12 | readonly state: S; 13 | readonly getters: any; 14 | 15 | replaceState(state: S): void; 16 | 17 | dispatch: Dispatch; 18 | commit: Commit; 19 | 20 | subscribe

(fn: (mutation: P, state: S) => any): () => void; 21 | watch(getter: (state: S) => T, cb: (value: T, oldValue: T) => void, options?: WatchOptions): void; 22 | 23 | registerModule(path: string, module: Module): void; 24 | registerModule(path: string[], module: Module): void; 25 | 26 | unregisterModule(path: string): void; 27 | unregisterModule(path: string[]): void; 28 | 29 | hotUpdate(options: { 30 | actions?: ActionTree; 31 | mutations?: MutationTree; 32 | getters?: GetterTree; 33 | modules?: ModuleTree; 34 | }): void; 35 | } 36 | 37 | export declare function install(Vue: typeof _Vue): void; 38 | 39 | export interface Dispatch { 40 | (type: string, payload?: any, options?: DispatchOptions): Promise; 41 |

(payloadWithType: P, options?: DispatchOptions): Promise; 42 | } 43 | 44 | export interface Commit { 45 | (type: string, payload?: any, options?: CommitOptions): void; 46 |

(payloadWithType: P, options?: CommitOptions): void; 47 | } 48 | 49 | export interface ActionContext { 50 | dispatch: Dispatch; 51 | commit: Commit; 52 | state: S; 53 | getters: any; 54 | rootState: R; 55 | rootGetters: any; 56 | } 57 | 58 | export interface Payload { 59 | type: string; 60 | } 61 | 62 | export interface DispatchOptions { 63 | root?: boolean; 64 | } 65 | 66 | export interface CommitOptions { 67 | silent?: boolean; 68 | root?: boolean; 69 | } 70 | 71 | export interface StoreOptions { 72 | state?: S; 73 | getters?: GetterTree; 74 | actions?: ActionTree; 75 | mutations?: MutationTree; 76 | modules?: ModuleTree; 77 | plugins?: Plugin[]; 78 | strict?: boolean; 79 | } 80 | 81 | export type Getter = (state: S, getters: any, rootState: R, rootGetters: any) => any; 82 | export type Action = (injectee: ActionContext, payload: any) => any; 83 | export type Mutation = (state: S, payload: any) => any; 84 | export type Plugin = (store: Store) => any; 85 | 86 | export interface Module { 87 | namespaced?: boolean; 88 | state?: S; 89 | getters?: GetterTree; 90 | actions?: ActionTree; 91 | mutations?: MutationTree; 92 | modules?: ModuleTree; 93 | } 94 | 95 | export interface GetterTree { 96 | [key: string]: Getter; 97 | } 98 | 99 | export interface ActionTree { 100 | [key: string]: Action; 101 | } 102 | 103 | export interface MutationTree { 104 | [key: string]: Mutation; 105 | } 106 | 107 | export interface ModuleTree { 108 | [key: string]: Module; 109 | } 110 | -------------------------------------------------------------------------------- /src/components/home/Block_RushBuy.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 27 | 28 | 56 | 57 | 117 | 118 | -------------------------------------------------------------------------------- /src/components/item/OpratorBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 38 | 39 | 107 | 108 | 126 | 127 | -------------------------------------------------------------------------------- /src/utils/array-util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/28 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/28 herbluo created 10 | */ 11 | 12 | const util = { 13 | 14 | /** 15 | * 冒泡排序 16 | * @param array 17 | * @param isAGreaterThanB a > b, 从小到大排,a < b 从大到小排 18 | */ 19 | sortBy (array, isAGreaterThanB) { 20 | const length = array.length 21 | for (let i = 0; i < length - 1; ++i) { 22 | for (let j = 0; j < length - i - 1; ++j) { 23 | if (isAGreaterThanB(array[j], array[j + 1])) { 24 | [array[j], array[j + 1]] = [array[j + 1], array[j]] 25 | } 26 | } 27 | } 28 | }, 29 | 30 | /** 31 | * 分组 根据属性进行 32 | * @param array 33 | * @param propertyName 属性名 34 | * @param groups 原有的groups 可选 默认为 [] 35 | * @returns {Array} [{groupId(等同于属性): any, group: Array}] 36 | */ 37 | groupBy_PropertyEqual (array, propertyName, groups = []) { 38 | let length = array.length 39 | 40 | for (let i = 0; i < length; i++) { 41 | let isNew = true 42 | let groupsLength = groups.length 43 | 44 | for (let j = 0; j < groupsLength; j++) { 45 | if (array[i][propertyName] === groups[j].groupId) { 46 | isNew = false 47 | groups[j].group.push(array[i]) 48 | } 49 | } 50 | if (isNew) { 51 | groups.push({ 52 | groupId: array[i][propertyName], 53 | group: [array[i]] 54 | }) 55 | } 56 | } 57 | return groups 58 | }, 59 | 60 | /** 61 | * shop native专用, 62 | * 其它项目亦可用 63 | * 64 | * 从每一个分组完毕的组里面采集一个对象, 65 | * 最后构成一个数组并返回。 66 | * 67 | * 该数组长度与组长度相同 68 | * 69 | * @param groups [{group: []}] 70 | * @param index start from 0 71 | * @param sorted 'DESC' 降序, 'ASC' 升序 72 | * @private 73 | */ 74 | _pickItemFromEachGroup (groups, index, sorted = false) { 75 | const items = [] 76 | for (let i = 0; i < groups.length; i++) { 77 | let group = groups[i].group 78 | let length = group.length 79 | items.push(group[length > index ? index : length - 1]) 80 | } 81 | if (sorted !== false) { 82 | util.sortBy(items, sorted === 'DESC' ? (a, b) => a < b : (a, b) => a > b) 83 | } 84 | return items 85 | }, 86 | 87 | /** 88 | * 判断数组中是否包含某个元素 89 | */ 90 | includes: function (array, val) { 91 | if (!isArray(array)) { 92 | return false 93 | } 94 | for (let i = 0; i < array.length; i++) { 95 | if (typeof val === 'function' ? val(array[i]) : array[i] === val) { 96 | return true 97 | } 98 | } 99 | if (isNaN(val)) { 100 | return isArrayIncludeNaN(array) 101 | } 102 | return false 103 | } 104 | } 105 | 106 | function isArray (obj) { 107 | return obj instanceof Array 108 | } 109 | 110 | function isArrayIncludeNaN (array) { 111 | for (let i = 0; i < array.length; i++) { 112 | if (isNaN(array[i])) { 113 | return true 114 | } 115 | } 116 | return false 117 | } 118 | 119 | function isNaN (val) { 120 | // eslint-disable-next-line 121 | return val !== val 122 | } 123 | 124 | export default util 125 | -------------------------------------------------------------------------------- /src/views/AppInitWeb.vue: -------------------------------------------------------------------------------- 1 | 7 | 14 | 15 | 24 | 25 | 111 | 112 | -------------------------------------------------------------------------------- /src/views/Item.vue: -------------------------------------------------------------------------------- 1 | 7 | 20 | 21 | 24 | 25 | 106 | 107 | -------------------------------------------------------------------------------- /src/utils/ts/vuerouter/router.d.ts: -------------------------------------------------------------------------------- 1 | import Vue = require("vue"); 2 | import { ComponentOptions, PluginFunction } from "vue"; 3 | 4 | type Component = ComponentOptions | typeof Vue; 5 | type Dictionary = { [key: string]: T }; 6 | 7 | export type RouterMode = "hash" | "history" | "abstract"; 8 | export type RawLocation = string | Location; 9 | export type RedirectOption = RawLocation | ((to: Route) => RawLocation); 10 | export type NavigationGuard = ( 11 | to: Route, 12 | from: Route, 13 | next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void 14 | ) => any 15 | 16 | declare class VueRouter { 17 | constructor (options?: RouterOptions); 18 | 19 | app: Vue; 20 | mode: RouterMode; 21 | currentRoute: Route; 22 | 23 | beforeEach (guard: NavigationGuard): void; 24 | afterEach (hook: (to: Route, from: Route) => any): void; 25 | push (location: RawLocation, onComplete?: Function, onAbort?: Function): void; 26 | replace (location: RawLocation, onComplete?: Function, onAbort?: Function): void; 27 | go (n: number): void; 28 | back (): void; 29 | forward (): void; 30 | getMatchedComponents (to?: RawLocation): Component[]; 31 | onReady (cb: Function): void; 32 | addRoutes (routes: RouteConfig[]): void; 33 | resolve (to: RawLocation, current?: Route, append?: boolean): { 34 | location: Location; 35 | route: Route; 36 | href: string; 37 | // backwards compat 38 | normalizedTo: Location; 39 | resolved: Route; 40 | }; 41 | 42 | static install: PluginFunction; 43 | } 44 | 45 | export interface RouterOptions { 46 | routes?: RouteConfig[]; 47 | mode?: RouterMode; 48 | base?: string; 49 | linkActiveClass?: string; 50 | scrollBehavior?: ( 51 | to: Route, 52 | from: Route, 53 | savedPosition: { x: number, y: number } | undefined 54 | ) => { x: number, y: number } | { selector: string } | void; 55 | } 56 | 57 | type RoutePropsFunction = (route: Route) => Object; 58 | 59 | export interface RouteConfig { 60 | path: string; 61 | name?: string; 62 | component?: Component; 63 | components?: Dictionary; 64 | redirect?: RedirectOption; 65 | alias?: string | string[]; 66 | children?: RouteConfig[]; 67 | meta?: any; 68 | beforeEnter?: NavigationGuard; 69 | props?: boolean | Object | RoutePropsFunction; 70 | } 71 | 72 | export interface RouteRecord { 73 | path: string; 74 | components: Dictionary; 75 | instances: Dictionary; 76 | name?: string; 77 | parent?: RouteRecord; 78 | redirect?: RedirectOption; 79 | matchAs?: string; 80 | meta: any; 81 | beforeEnter?: ( 82 | route: Route, 83 | redirect: (location: RawLocation) => void, 84 | next: () => void 85 | ) => any; 86 | props: boolean | Object | RoutePropsFunction | Dictionary; 87 | } 88 | 89 | export interface Location { 90 | name?: string; 91 | path?: string; 92 | hash?: string; 93 | query?: Dictionary; 94 | params?: Dictionary; 95 | append?: boolean; 96 | replace?: boolean; 97 | } 98 | 99 | export interface Route { 100 | path: string; 101 | name?: string; 102 | hash: string; 103 | query: Dictionary; 104 | params: Dictionary; 105 | fullPath: string; 106 | matched: RouteRecord[]; 107 | redirectedFrom?: string; 108 | meta?: any; 109 | } 110 | -------------------------------------------------------------------------------- /src/components/item/ItemDetailInfoSheet.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 30 | 31 | 98 | 99 | 141 | 142 | -------------------------------------------------------------------------------- /src/utils/app/app-fetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * fetch的拓展 3 | * 4 | * 部分源码参考 Axios 5 | * 这是它的许可证 6 | * Copyright (c) 2014 Matt Zabriskie 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | * 26 | * any question or idea, email to i@closx.com 27 | * @author HerbLuo 28 | * @date 2017/8/31 29 | * @license Licensed under the MIT license. 30 | * 31 | * change logs: 32 | * 2017/8/31 herbluo created 33 | */ 34 | const stream = weex.requireModule('stream') 35 | 36 | const beforeRequest = (config) => { 37 | const body = config.body 38 | if (body) { 39 | if (typeof body === 'object') { 40 | config.body = JSON.stringify(body) 41 | } 42 | if (typeof config.body !== 'string') { 43 | return false 44 | } 45 | } 46 | return true 47 | } 48 | 49 | const afterResponse = (response) => { 50 | let data = response.data 51 | if (typeof data === 'string') { 52 | try { 53 | data = JSON.parse(data) 54 | } catch (e) { /* Ignore */ 55 | } 56 | } 57 | response.data = data 58 | } 59 | 60 | const settle = (resolve, reject, response) => { 61 | if (response.status && response.ok) { 62 | resolve(response) 63 | } else { 64 | const error = new Error( 65 | `Request failed with status code ${response.status}`) 66 | error.code = response.code 67 | error.response = response 68 | reject(error) 69 | } 70 | } 71 | 72 | /** 73 | * @param config {{method, url, type?}} 74 | * @return {Promise} 75 | */ 76 | const request = (config) => { 77 | if (!beforeRequest(config)) { 78 | return Promise.reject(new Error('请求参数错误')) 79 | } 80 | 81 | const method = config.method 82 | const url = config.url 83 | const type = config.type || 'json' 84 | 85 | return new Promise((resolve, reject) => { 86 | stream.fetch({method, url, type}, (response) => { 87 | afterResponse(response) 88 | settle(resolve, reject, response) 89 | }) 90 | }) 91 | } 92 | 93 | /** 94 | * @return {Promise} 95 | */ 96 | const get = (url) => { 97 | return request({ 98 | method: 'get', 99 | url 100 | }) 101 | } 102 | 103 | /** 104 | * @param url {string} 105 | * @param [body] {object} 106 | * @return {Promise} 107 | */ 108 | const post = (url, body) => { 109 | return request({ 110 | method: 'post', 111 | url, 112 | body 113 | }) 114 | } 115 | 116 | export { 117 | get, 118 | post 119 | } 120 | -------------------------------------------------------------------------------- /src/components/address/StreetList.vue: -------------------------------------------------------------------------------- 1 | 7 | 29 | 30 | 56 | 135 | 136 | -------------------------------------------------------------------------------- /test/null-util-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/7/8 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/7/8 herbluo created 10 | */ 11 | import {expect} from 'chai' 12 | import {NP} from '../src/utils/null-util' 13 | import _ from 'lodash' 14 | 15 | describe('null-util', () => { 16 | describe('null propagation function test', () => { 17 | 18 | let testObj; 19 | (() => { 20 | 21 | function getBean() { 22 | return { 23 | a: undefined, 24 | b: null, 25 | c: 0, 26 | d: -1, 27 | e: 1, 28 | f: '', 29 | g: {}, 30 | h: [], 31 | i: [1, 2], 32 | x: function (v) { 33 | return v + 2 34 | }, 35 | y: () => { 36 | } 37 | } 38 | 39 | } 40 | 41 | testObj = { 42 | ...getBean(), 43 | o: getBean() 44 | } 45 | 46 | })() 47 | 48 | it('base property access is fine', () => { 49 | // base 50 | _.forEach({ 51 | a: undefined, 52 | b: null, 53 | c: 0, 54 | d: -1, 55 | e: 1, 56 | f: '', 57 | }, (entity, key) => { 58 | expect(NP(testObj)[key].value).to.equal(entity) 59 | }) 60 | // object and array 61 | expect(NP(testObj).g.value).to.deep.equal({}) 62 | expect(NP(testObj).h.value).to.deep.equal([]) 63 | expect(NP(testObj).i.value).to.deep.equal([1, 2]) 64 | }) 65 | 66 | it('property which is null or undefined could be propagation', () => { 67 | expect(NP(undefined).value).to.equal(undefined) 68 | expect(NP(undefined).u.value).to.equal(undefined) 69 | expect(NP(testObj).u.value).to.equal(undefined) 70 | expect(NP(testObj).u.u.value).to.equal(undefined) 71 | expect(NP(testObj).d.u.value).to.equal(undefined) 72 | }) 73 | 74 | it('chain of property access is fine', () => { 75 | _.forEach({ 76 | a: undefined, 77 | b: null, 78 | c: 0, 79 | d: -1, 80 | e: 1, 81 | f: '', 82 | }, (entity, key) => { 83 | expect(NP(testObj).o[key].value).to.equal(entity) 84 | }) 85 | expect(NP(testObj).o.g.value).to.deep.equal({}) 86 | expect(NP(testObj).o.h.value).to.deep.equal([]) 87 | expect(NP(testObj).o.i.value).to.deep.equal([1, 2]) 88 | expect(NP(testObj).o.e.u.value).to.equal(undefined) 89 | }) 90 | 91 | it('property which is null-return function could be propagation', () => { 92 | expect(NP(testObj).x(-1).value).to.equal(1) 93 | expect(NP(testObj).x(0).value).to.equal(2) 94 | expect(NP(testObj).x(1).value).to.equal(3) 95 | expect(NP(testObj).y().value).to.equal(undefined) 96 | expect(NP(testObj).y().u.value).to.equal(undefined) 97 | expect(NP(testObj).o.x(0).value).to.equal(2) 98 | }) 99 | 100 | 101 | 102 | 103 | }) 104 | }) -------------------------------------------------------------------------------- /src/utils/ts/vue/options.d.ts: -------------------------------------------------------------------------------- 1 | import { Vue, CreateElement } from "./vue"; 2 | import { VNode, VNodeData, VNodeDirective } from "./vnode"; 3 | 4 | type Constructor = { 5 | new (...args: any[]): any; 6 | } 7 | 8 | export type Component = typeof Vue | ComponentOptions | FunctionalComponentOptions; 9 | export type AsyncComponent = ( 10 | resolve: (component: Component) => void, 11 | reject: (reason?: any) => void 12 | ) => Promise | Component | void; 13 | 14 | export interface ComponentOptions { 15 | data?: Object | ((this: V) => Object); 16 | props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; 17 | propsData?: Object; 18 | computed?: { [key: string]: ((this: V) => any) | ComputedOptions }; 19 | methods?: { [key: string]: (this: V, ...args: any[]) => any }; 20 | watch?: { [key: string]: ({ handler: WatchHandler } & WatchOptions) | WatchHandler | string }; 21 | 22 | el?: Element | String; 23 | template?: string; 24 | render?(this: V, createElement: CreateElement): VNode; 25 | renderError?: (h: () => VNode, err: Error) => VNode; 26 | staticRenderFns?: ((createElement: CreateElement) => VNode)[]; 27 | 28 | beforeCreate?(this: V): void; 29 | created?(this: V): void; 30 | beforeDestroy?(this: V): void; 31 | destroyed?(this: V): void; 32 | beforeMount?(this: V): void; 33 | mounted?(this: V): void; 34 | beforeUpdate?(this: V): void; 35 | updated?(this: V): void; 36 | activated?(this: V): void; 37 | deactivated?(this: V): void; 38 | 39 | directives?: { [key: string]: DirectiveOptions | DirectiveFunction }; 40 | components?: { [key: string]: Component | AsyncComponent }; 41 | transitions?: { [key: string]: Object }; 42 | filters?: { [key: string]: Function }; 43 | 44 | provide?: Object | (() => Object); 45 | inject?: { [key: string]: string | symbol } | Array; 46 | 47 | model?: { 48 | prop?: string; 49 | event?: string; 50 | }; 51 | 52 | parent?: Vue; 53 | mixins?: (ComponentOptions | typeof Vue)[]; 54 | name?: string; 55 | extends?: ComponentOptions | typeof Vue; 56 | delimiters?: [string, string]; 57 | } 58 | 59 | export interface FunctionalComponentOptions { 60 | props?: string[] | { [key: string]: PropOptions | Constructor | Constructor[] }; 61 | functional: boolean; 62 | render(this: never, createElement: CreateElement, context: RenderContext): VNode; 63 | name?: string; 64 | } 65 | 66 | export interface RenderContext { 67 | props: any; 68 | children: VNode[]; 69 | slots(): any; 70 | data: VNodeData; 71 | parent: Vue; 72 | injections: any 73 | } 74 | 75 | export interface PropOptions { 76 | type?: Constructor | Constructor[] | null; 77 | required?: boolean; 78 | default?: any; 79 | validator?(value: any): boolean; 80 | } 81 | 82 | export interface ComputedOptions { 83 | get?(this: V): any; 84 | set?(this: V, value: any): void; 85 | cache?: boolean; 86 | } 87 | 88 | export type WatchHandler = (this: V, val: T, oldVal: T) => void; 89 | 90 | export interface WatchOptions { 91 | deep?: boolean; 92 | immediate?: boolean; 93 | } 94 | 95 | export type DirectiveFunction = ( 96 | el: HTMLElement, 97 | binding: VNodeDirective, 98 | vnode: VNode, 99 | oldVnode: VNode 100 | ) => void; 101 | 102 | export interface DirectiveOptions { 103 | bind?: DirectiveFunction; 104 | inserted?: DirectiveFunction; 105 | update?: DirectiveFunction; 106 | componentUpdated?: DirectiveFunction; 107 | unbind?: DirectiveFunction; 108 | } 109 | -------------------------------------------------------------------------------- /src/components/common/TabBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 23 | 24 | 72 | 73 | 137 | -------------------------------------------------------------------------------- /src/components/common/PopupPageQuick.vue: -------------------------------------------------------------------------------- 1 | 18 | 26 | 27 | 52 | 53 | 158 | 159 | -------------------------------------------------------------------------------- /src/utils/ts/vue/vue.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | AsyncComponent, 4 | ComponentOptions, 5 | FunctionalComponentOptions, 6 | WatchOptions, 7 | WatchHandler, 8 | DirectiveOptions, 9 | DirectiveFunction 10 | } from "./options"; 11 | import { VNode, VNodeData, VNodeChildren, ScopedSlot } from "./vnode"; 12 | import { PluginFunction, PluginObject } from "./plugin"; 13 | 14 | export type CreateElement = { 15 | // empty node 16 | (): VNode; 17 | 18 | // element or component name 19 | (tag: string, children: VNodeChildren): VNode; 20 | (tag: string, data?: VNodeData, children?: VNodeChildren): VNode; 21 | 22 | // component constructor or options 23 | (tag: Component, children: VNodeChildren): VNode; 24 | (tag: Component, data?: VNodeData, children?: VNodeChildren): VNode; 25 | 26 | // async component 27 | (tag: AsyncComponent, children: VNodeChildren): VNode; 28 | (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode; 29 | } 30 | 31 | export declare class Vue { 32 | 33 | constructor(options?: ComponentOptions); 34 | 35 | $data: Object; 36 | readonly $el: HTMLElement; 37 | readonly $options: ComponentOptions; 38 | readonly $parent: Vue; 39 | readonly $root: Vue; 40 | readonly $children: Vue[]; 41 | readonly $refs: { [key: string]: Vue | Element | Vue[] | Element[]}; 42 | readonly $slots: { [key: string]: VNode[] }; 43 | readonly $scopedSlots: { [key: string]: ScopedSlot }; 44 | readonly $isServer: boolean; 45 | readonly $props: any; 46 | 47 | $mount(elementOrSelector?: Element | String, hydrating?: boolean): this; 48 | $forceUpdate(): void; 49 | $destroy(): void; 50 | $set: typeof Vue.set; 51 | $delete: typeof Vue.delete; 52 | $watch( 53 | expOrFn: string, 54 | callback: WatchHandler, 55 | options?: WatchOptions 56 | ): (() => void); 57 | $watch( 58 | expOrFn: (this: this) => T, 59 | callback: WatchHandler, 60 | options?: WatchOptions 61 | ): (() => void); 62 | $on(event: string | string[], callback: Function): this; 63 | $once(event: string, callback: Function): this; 64 | $off(event?: string | string[], callback?: Function): this; 65 | $emit(event: string, ...args: any[]): this; 66 | $nextTick(callback: (this: this) => void): void; 67 | $nextTick(): Promise; 68 | $createElement: CreateElement; 69 | 70 | static config: { 71 | silent: boolean; 72 | optionMergeStrategies: any; 73 | devtools: boolean; 74 | productionTip: boolean; 75 | performance: boolean; 76 | errorHandler(err: Error, vm: Vue, info: string): void; 77 | ignoredElements: string[]; 78 | keyCodes: { [key: string]: number }; 79 | } 80 | 81 | static extend(options: ComponentOptions | FunctionalComponentOptions): typeof Vue; 82 | static nextTick(callback: () => void, context?: any[]): void; 83 | static nextTick(): Promise 84 | static set(object: Object, key: string, value: T): T; 85 | static set(array: T[], key: number, value: T): T; 86 | static delete(object: Object, key: string): void; 87 | static delete(array: T[], key: number): void; 88 | 89 | static directive( 90 | id: string, 91 | definition?: DirectiveOptions | DirectiveFunction 92 | ): DirectiveOptions; 93 | static filter(id: string, definition?: Function): Function; 94 | static component(id: string, definition?: Component | AsyncComponent): typeof Vue; 95 | 96 | static use(plugin: PluginObject | PluginFunction, options?: T): void; 97 | static mixin(mixin: typeof Vue | ComponentOptions): void; 98 | static compile(template: string): { 99 | render(createElement: typeof Vue.prototype.$createElement): VNode; 100 | staticRenderFns: (() => VNode)[]; 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /src/utils/vendors/axios/lib/adapters/stream.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * any question or idea, email to i@closx.com 4 | * @author HerbLuo 5 | * @date 2017/4/17 6 | * @license Licensed under the MIT license. 7 | * 8 | * change logs: 9 | * 2017/4/17 herbluo created 10 | */ 11 | 'use strict'; 12 | 13 | const utils = require('../utils'); 14 | const settle = require('../core/settle'); 15 | const buildURL = require('../helpers/buildURL'); 16 | //noinspection ES6ModulesDependencies 17 | const stream = weex.requireModule('stream'); 18 | const url = require('url'); 19 | const createError = require('../core/createError'); 20 | const enhanceError = require('../core/enhanceError'); 21 | 22 | module.exports = function streamAdapter(config) { 23 | return new Promise(function dispatchStreamRequest(resolve, reject) { 24 | 25 | let {data, headers} = config 26 | let timer 27 | let aborted = false 28 | 29 | // HTTP basic authentication 30 | let auth 31 | if (config.auth) { 32 | let username = config.auth.username || ''; 33 | let password = config.auth.password || ''; 34 | auth = username + ':' + password; 35 | } 36 | 37 | // Parse url 38 | let parsed = url.parse(config.url); 39 | 40 | if (!auth && parsed.auth) { 41 | let urlAuth = parsed.auth.split(':'); 42 | let urlUsername = urlAuth[0] || ''; 43 | let urlPassword = urlAuth[1] || ''; 44 | auth = urlUsername + ':' + urlPassword; 45 | } 46 | 47 | if (auth) { 48 | delete headers.Authorization; 49 | } 50 | 51 | //noinspection JSUnresolvedFunction 52 | let options = { 53 | url: buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, ''), 54 | method: config.method, 55 | headers: headers, 56 | type: 'text' 57 | }; 58 | 59 | stream.fetch(options, res => { 60 | if (aborted) return; 61 | 62 | timer && clearTimeout(timer); 63 | timer = null; 64 | 65 | switch (res.headers['content-encoding']) { 66 | case 'gzip': 67 | case 'compress': 68 | case 'deflate': 69 | throw new Error('未实现') 70 | } 71 | 72 | const response = { 73 | status: res.statusCode, 74 | statusText: res.statusMessage, 75 | headers: res.headers, 76 | config: config, 77 | data: res.data, 78 | }; 79 | 80 | settle(resolve, reject, response); 81 | }); 82 | 83 | const req = { 84 | abort() {}, 85 | end() {} 86 | }; 87 | 88 | // Handle request timeout 89 | if (config.timeout && !timer) { 90 | timer = setTimeout(function handleRequestTimeout() { 91 | req.abort(); 92 | reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED')); 93 | aborted = true; 94 | }, config.timeout); 95 | } 96 | 97 | if (config.cancelToken) { 98 | // Handle cancellation 99 | config.cancelToken.promise.then(function onCanceled(cancel) { 100 | if (aborted) { 101 | return; 102 | } 103 | req.abort(); 104 | reject(cancel); 105 | aborted = true; 106 | }); 107 | } 108 | 109 | // Send the request 110 | if (utils.isStream(data)) { 111 | data.pipe(req); 112 | } else { 113 | req.end(data); 114 | } 115 | }); 116 | }; 117 | 118 | -------------------------------------------------------------------------------- /src/components/order/AddressBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 39 | 40 | 118 | 119 | 153 | 154 | -------------------------------------------------------------------------------- /src/components/address/StreetPicker.vue: -------------------------------------------------------------------------------- 1 | 7 | 31 | 32 | 83 | 84 | 152 | 153 | --------------------------------------------------------------------------------