├── .gitignore ├── package.json ├── src ├── geo.js ├── pay.js ├── camera.js ├── share.js ├── coms.js ├── nav.js ├── font.js ├── events.js ├── bindingx.js ├── storage.js ├── notice.js ├── tools.js ├── image.js ├── axios.js ├── router.js └── mixins.js ├── index.js ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eros-widget", 3 | "version": "1.0.2-beta.2", 4 | "description": "eros widget", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "eros" 11 | ], 12 | "author": "Zero", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "lodash": "^4.17.5", 16 | "bindingx-parser": "0.0.x" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/geo.js: -------------------------------------------------------------------------------- 1 | const geolocation = weex.requireModule('bmGeolocation') 2 | const Geolocation = Object.create(null) 3 | 4 | Geolocation.install = (Vue) => { 5 | Vue.prototype.$geo = { 6 | get () { 7 | return new Promise((resolve, reject) => { 8 | geolocation.getGeolocation(({ status, errorMsg, data }) => { 9 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 10 | }) 11 | }) 12 | } 13 | } 14 | } 15 | 16 | Vue.use(Geolocation) 17 | -------------------------------------------------------------------------------- /src/pay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: songqi 3 | * @Date: 2017-01-11 4 | * @Last modified by: songqi 5 | * @Last modified time: 2017-02-09 6 | */ 7 | 8 | const pay = weex.requireModule('bmPay') 9 | 10 | var Pay = Object.create(null) 11 | 12 | Pay.install = (Vue, options) => { 13 | Vue.prototype.$pay = { 14 | wechat (params) { 15 | return new Promise((resolve, reject) => { 16 | pay.payByWechat(params, ({ status, errorMsg, data }) => { 17 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 18 | }) 19 | }) 20 | } 21 | } 22 | } 23 | 24 | Vue.use(Pay) 25 | -------------------------------------------------------------------------------- /src/camera.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: songqi 3 | * @Date: 2017-01-11e 4 | * @Last modified by: songqi 5 | * @Last modified time: 2017-03-08 6 | */ 7 | 8 | const camera = weex.requireModule('bmCamera') 9 | const Camera = Object.create(null) 10 | 11 | Camera.install = (Vue, options) => { 12 | Vue.prototype.$camera = { 13 | // 扫一扫 14 | scan () { 15 | return new Promise((resolve, reject) => { 16 | camera.scan(({ status, errorMsg, data }) => { 17 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 18 | }) 19 | }) 20 | } 21 | } 22 | } 23 | 24 | Vue.use(Camera) 25 | -------------------------------------------------------------------------------- /src/share.js: -------------------------------------------------------------------------------- 1 | const share = weex.requireModule('bmShare') 2 | 3 | const Share = Object.create(null) 4 | 5 | Share.install = (Vue, options) => { 6 | Vue.prototype.$share = (cfg) => { 7 | return new Promise((resolve, reject) => { 8 | share.share({ 9 | title: cfg.title, 10 | content: cfg.content || '', 11 | image: cfg.image || '', 12 | url: cfg.url || '', 13 | platforms: cfg.platforms || [] // 传空的话默认全部 14 | }, data => { 15 | resolve(data) 16 | }, err => { 17 | reject(err) 18 | }) 19 | }) 20 | } 21 | } 22 | 23 | Vue.use(Share) 24 | -------------------------------------------------------------------------------- /src/coms.js: -------------------------------------------------------------------------------- 1 | 2 | const _com = weex.requireModule('bmCommunication') 3 | const Coms = Object.create(null) 4 | 5 | Coms.install = (Vue, options) => { 6 | Vue.prototype.$coms = { 7 | call (to = +to, tip = true) { 8 | _com['call']({ to, tip }) 9 | }, 10 | sms (to = [], content = '') { 11 | return new Promise((resolve, reject) => { 12 | _com.sms(to, content, ({ status, errorMsg, data }) => { 13 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 14 | }) 15 | }) 16 | }, 17 | contacts () { 18 | return new Promise((resolve, reject) => { 19 | _com.contacts(({ status, errorMsg, data }) => { 20 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 21 | }) 22 | }) 23 | } 24 | } 25 | } 26 | 27 | Vue.use(Coms) 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | // 配置方法 3 | import './src/mixins.js' 4 | 5 | import './src/font.js' 6 | // 弹窗 7 | import './src/notice.js' 8 | // 获取地理位置 9 | import './src/geo.js' 10 | // 获取相机信息 11 | import './src/camera.js' 12 | // 图片操作相关 13 | import './src/image.js' 14 | // 设置导航 15 | import './src/nav.js' 16 | // 支付相关(已抽离第三方插件) 17 | // import './src/pay.js' 18 | // bindingx 19 | import './src/bindingx.js' 20 | // 存储相关 21 | import './src/storage.js' 22 | // 全局事件 23 | import './src/events.js' 24 | // 分享(已抽离第三方插件) 25 | // import './src/share.js' 26 | // 工具方法 27 | import './src/tools.js' 28 | 29 | import './src/coms.js' 30 | 31 | // 路由 32 | import Router from './src/router.js' 33 | // 发送请求 34 | import Axios from './src/axios.js' 35 | 36 | let instance = null 37 | export default class Widget { 38 | constructor ({ router, ajax }) { 39 | if (!instance) { 40 | Vue.use(new Axios(ajax)) 41 | Vue.use(new Router(router)) 42 | instance = this 43 | } 44 | return instance 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/nav.js: -------------------------------------------------------------------------------- 1 | import isFunction from 'lodash/isFunction' 2 | 3 | const navigator = weex.requireModule('bmNavigator') 4 | const Navigator = Object.create(null) 5 | 6 | Navigator.install = (Vue, options) => { 7 | Vue.prototype.$navigator = { 8 | setLeftItem (options, callback) { 9 | navigator.setLeftItem(options, () => { 10 | isFunction(callback) && callback() 11 | }) 12 | }, 13 | setRightItem (options, callback) { 14 | navigator.setRightItem(options, () => { 15 | isFunction(callback) && callback() 16 | }) 17 | }, 18 | setCenterItem (options, callback) { 19 | navigator.setCenterItem(options, () => { 20 | isFunction(callback) && callback() 21 | }) 22 | }, 23 | setNavigationInfo (options, callback) { 24 | navigator.setNavigationInfo(options, () => { 25 | isFunction(callback) && callback() 26 | }) 27 | } 28 | } 29 | } 30 | 31 | Vue.use(Navigator) 32 | -------------------------------------------------------------------------------- /src/font.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: songqi 3 | * @Date: 2017-01-11 4 | * @Last modified by: songqi 5 | * @Last modified time: 2017-05-08 6 | */ 7 | 8 | const bmFont = weex.requireModule('bmFont') 9 | const Font = Object.create(null) 10 | 11 | Font.install = (Vue, options) => { 12 | Vue.prototype.$font = { 13 | changeFontSize (options) { 14 | return new Promise((resolve, reject) => { 15 | bmFont.changeFontSize({ 16 | fontSize: options.fontSize || 'NORM' 17 | }, ({ status, errorMsg, data }) => { 18 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 19 | }) 20 | }) 21 | }, 22 | 23 | getFontSize () { 24 | return new Promise((resolve, reject) => { 25 | bmFont.getFontSize(({ status, errorMsg, data }) => { 26 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 27 | }) 28 | }) 29 | } 30 | } 31 | } 32 | 33 | Vue.use(Font) 34 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.2 2 | * [feature] $router.open support add background-color to set native background 3 | * [feature] $tools.networkStatus, $tools.watchNetworkStatus, $tools.clearWatchNetworkStatus 4 | * [remove] $tools.isInstallWXApp 5 | * [remove] $tools.getCid 6 | * [remove] $pay 7 | * [remove] $share 8 | 9 | # 1.0.1-beta.8 10 | * [bugfix] backAppeared 和 beforeBackAppeared 不执行的问题 11 | * [bugfix] pickAndUpload 成功后成功回调进入 error 回调 12 | * [optimize] fetch method GET POST 大小写均可 13 | 14 | # 1.0.1-beta.7 15 | * [bugfix] $image.upload status code 判断失误 16 | 17 | # 1.0.1-beta.6 18 | * [optimize] 重写 $bindingx 的引用方法,重写了 bind 方法,支持所有内部暴露出来的方法 19 | * [bugfix] 父子组件同时注册生命周期,执行两次的 bug 20 | 21 | # 1.0.1-beta.3/4/5 22 | * [add] support bindingx. 23 | 24 | # 1.0.1-beta.2 25 | * [bugfix] beforeAppear 和 beforeBackAppear 另个页面返回或者结束后仍会获取到上次的$router.setBackParams数据的 bug. 26 | 27 | # 1.0.1-beta.1 28 | * [bugfix] 修复全局事件注册2次的bug 29 | 30 | # 1.0.1 31 | 生命周期,自定义事件均做调整,如果不更改,请不要升级到 `1.0.1`。 32 | * `bmRouter` 变更为 `eros` 33 | * 添加 `pushMessage`,可在页面中监听推送 34 | * 添加 `appActive`,可在页面中监听 app 切换到后台事件 35 | * 添加 `appDeactive`,可在页面中监听 app 切换至前台事件 36 | * `viewWillAppear` 变更为 `beforeAppear`,beforeBackAppear,通过打开类型来做区分 37 | * `viewDidAppear` 变更为 `appeared`,`backAppeared`,通过打开类型来做区分 38 | * `viewWillDisappear` 变更为 `beforeDisappear` 39 | * `viewDidDisappear` 变更为 `disappeared` 40 | 41 | # 1.0.0 42 | * 从模板中抽离 widget 完成 -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: songqi 3 | * @Date: 2017-02-27 4 | * @Last modified by: songqi 5 | * @Last modified time: 2017-04-10 6 | */ 7 | 8 | // import _isArray from 'lodash/isArray' 9 | var event = weex.requireModule('bmEvents') 10 | // var globalEvent = weex.requireModule('globalEvent') 11 | 12 | const GlobalEvent = Object.create(null) 13 | // const GLOBALEVENT = Object.create(null) 14 | 15 | // // app 被放到后台 appWillResignActive 16 | // globalEvent.addEventListener('appDeactive', function (options) { 17 | // _isArray(GLOBALEVENT['appDeactive']) && GLOBALEVENT['appDeactive'].map((item) => { 18 | // item(options) 19 | // }) 20 | // }) 21 | 22 | // // app 从后台唤起 appDidBecomeActive 23 | // globalEvent.addEventListener('appActive', function (options) { 24 | // _isArray(GLOBALEVENT['appActive']) && GLOBALEVENT['appActive'].map((item) => { 25 | // item(options) 26 | // }) 27 | // }) 28 | 29 | GlobalEvent.install = (Vue, options) => { 30 | // Vue.mixin({ 31 | // beforeCreate () { 32 | // if (this.$options.globalEvent) { 33 | // var ev = this.$options.globalEvent 34 | // for (var i in ev) { 35 | // if (!GLOBALEVENT[i]) { 36 | // GLOBALEVENT[i] = [] 37 | // } 38 | // GLOBALEVENT[i].push(ev[i].bind(this)) 39 | // } 40 | // } 41 | // } 42 | // }) 43 | Vue.prototype.$event = event 44 | } 45 | 46 | Vue.use(GlobalEvent) 47 | -------------------------------------------------------------------------------- /src/bindingx.js: -------------------------------------------------------------------------------- 1 | import { parse } from 'bindingx-parser' 2 | import _cloneDeep from 'lodash/cloneDeep' 3 | 4 | const WeexBinding = weex.requireModule('bindingx') 5 | const BindingxFunction = WeexBinding.bind 6 | 7 | let _WeexBinding = _cloneDeep(WeexBinding) 8 | let Bindingx = Object.create(null) 9 | 10 | // 重写 bind 方法 11 | _WeexBinding.bind = (options, callback) => { 12 | if (!options) { 13 | throw new Error('should pass options for binding') 14 | } 15 | 16 | options.exitExpression = formatExpression(options.exitExpression) 17 | 18 | if (options.props) { 19 | options.props.forEach((prop) => { 20 | prop.expression = formatExpression(prop.expression) 21 | }) 22 | } 23 | 24 | return BindingxFunction(options, options && options.eventType === 'timing' ? fixCallback(callback) : callback) 25 | } 26 | 27 | Bindingx.install = (Vue, options) => { 28 | Vue.prototype.$bindingx = _WeexBinding 29 | } 30 | 31 | const formatExpression = (expression) => { 32 | if (expression === undefined) return 33 | try { 34 | expression = JSON.parse(expression) 35 | } catch (err) { 36 | 37 | } 38 | let resultExpression = {} 39 | if (typeof expression === 'string') { 40 | resultExpression.origin = expression 41 | } else if (expression) { 42 | resultExpression.origin = expression.origin 43 | resultExpression.transformed = expression.transformed 44 | } 45 | if (!resultExpression.transformed && !resultExpression.origin) return 46 | resultExpression.transformed = resultExpression.transformed || parse(resultExpression.origin) 47 | return resultExpression 48 | } 49 | 50 | const fixCallback = (callback) => { 51 | return function(params = {}) { 52 | if (typeof callback === 'function') { 53 | return callback({ 54 | state: params.state === 'end' ? 'exit' : params.state, 55 | t: params.t !== undefined ? params.t : params.deltaT 56 | }) 57 | } 58 | } 59 | } 60 | 61 | Vue.use(Bindingx) 62 | -------------------------------------------------------------------------------- /src/storage.js: -------------------------------------------------------------------------------- 1 | import _isFunction from 'lodash/isFunction' 2 | const storage = weex.requireModule('bmStorage') 3 | 4 | var Storage = Object.create(null) 5 | 6 | Storage.install = (Vue, options) => { 7 | Vue.prototype.$storage = { 8 | set (key, value, callback) { 9 | return new Promise((resolve, reject) => { 10 | storage.setData(key.toString(), JSON.stringify(value), ({ status, data, errorMsg }) => { 11 | _isFunction(callback) && callback.call(this, status === 0) 12 | status === 0 ? resolve(true) : reject(false) 13 | }) 14 | }) 15 | }, 16 | setSync (key, value) { 17 | return storage.setDataSync(key.toString(), JSON.stringify(value)) 18 | }, 19 | get (key, callback) { 20 | return new Promise((resolve, reject) => { 21 | storage.getData(key.toString(), ({ status, data, errorMsg }) => { 22 | _isFunction(callback) && callback.call(this, status === 0) 23 | status === 0 ? resolve(JSON.parse(data)) : reject(false) 24 | }) 25 | }) 26 | }, 27 | getSync (key) { 28 | const { status, data } = storage.getDataSync(key.toString()) 29 | return status === 0 ? JSON.parse(data) : '' 30 | }, 31 | delete (key, callback) { 32 | return new Promise((resolve, reject) => { 33 | storage.deleteData(key.toString(), ({ status, data, errorMsg }) => { 34 | _isFunction(callback) && callback.call(this, status === 0) 35 | status === 0 ? resolve(true) : reject(false) 36 | }) 37 | }) 38 | }, 39 | deleteSync (key) { 40 | const { status } = storage.deleteDataSync(key.toString()) 41 | return status === 0 42 | }, 43 | removeAll (callback) { 44 | return new Promise((resolve, reject) => { 45 | storage.removeData(({ status, data, errorMsg }) => { 46 | _isFunction(callback) && callback.call(this, status === 0) 47 | status === 0 ? resolve(true) : reject(false) 48 | }) 49 | }) 50 | }, 51 | removeAllSync () { 52 | const { status } = storage.removeDataSync() 53 | return status === 0 54 | } 55 | } 56 | } 57 | 58 | Vue.use(Storage) 59 | -------------------------------------------------------------------------------- /src/notice.js: -------------------------------------------------------------------------------- 1 | const modal = weex.requireModule('bmModal') 2 | const Notice = Object.create(null) 3 | 4 | import _isFunction from 'lodash/isFunction' 5 | import _isObject from 'lodash/isObject' 6 | 7 | Notice.install = (Vue, options) => { 8 | Vue.prototype.$notice = { 9 | alert (options) { 10 | return new Promise((resolve, reject) => { 11 | modal.alert({ 12 | // titleAlign: options.titleAlign || 'center', 13 | title: options.title || '', 14 | message: options.message || '', 15 | // messageAlign: options.messageAlign || 'center', 16 | okTitle: options.okTitle || '确定' 17 | }, (params) => { 18 | if (_isFunction(options.callback)) { 19 | options.callback.call(params) 20 | } 21 | resolve() 22 | }) 23 | }) 24 | }, 25 | confirm (options) { 26 | return new Promise((resolve, reject) => { 27 | modal.confirm({ 28 | // titleAlign: options.titleAlign || 'center', 29 | title: options.title || '', 30 | message: options.message || '', 31 | // messageAlign: options.messageAlign || 'center', 32 | cancelTitle: options.cancelTitle || '取消', 33 | okTitle: options.okTitle || '确定' 34 | }, (params) => { 35 | if (_isFunction(options.cancelCallback)) { 36 | options.cancelCallback.call(params) 37 | } 38 | reject() 39 | }, (params) => { 40 | if (_isFunction(options.okCallback)) { 41 | options.okCallback.call(params) 42 | } 43 | resolve() 44 | }) 45 | }) 46 | }, 47 | loading: { 48 | show (message = '') { 49 | modal.showLoading({ message }) 50 | }, 51 | hide () { 52 | setTimeout(() => { 53 | modal.hideLoading() 54 | }, 200) 55 | } 56 | }, 57 | toast (options) { 58 | if (!options) return 59 | if (_isObject(options)) { 60 | modal.toast({ 61 | message: options.message 62 | }) 63 | } else { 64 | modal.toast({ 65 | message: options 66 | }) 67 | } 68 | } 69 | } 70 | } 71 | 72 | Vue.use(Notice) 73 | -------------------------------------------------------------------------------- /src/tools.js: -------------------------------------------------------------------------------- 1 | 2 | const tools = weex.requireModule('bmTool') 3 | const Tools = Object.create(null) 4 | 5 | Tools.install = (Vue, options) => { 6 | Vue.prototype.$tools = { 7 | resignKeyboard () { 8 | return new Promise((resolve, reject) => { 9 | tools.resignKeyboard(({ status, errorMsg, data }) => { 10 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 11 | }) 12 | }) 13 | }, 14 | 15 | // 是否安装微信 16 | // isInstallWXApp () { 17 | // return new Promise((resolve, reject) => { 18 | // tools.isInstallWXApp(({ status, errorMsg, data }) => { 19 | // status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 20 | // }) 21 | // }) 22 | // }, 23 | 24 | // // 获取 cid 25 | // getCid () { 26 | // return new Promise((resolve, reject) => { 27 | // tools.getCid(({ status, errorMsg, data }) => { 28 | // status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 29 | // }) 30 | // }) 31 | // }, 32 | 33 | networkStatus() { 34 | return tools.networkStatus() 35 | }, 36 | 37 | watchNetworkStatus() { 38 | return new Promise((resolve, reject) => { 39 | tools.watchNetworkStatus(({ status, errorMsg, data }) => { 40 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 41 | }) 42 | }) 43 | }, 44 | 45 | clearWatchNetworkStatus() { 46 | tools.clearWatchNetworkStatus() 47 | return true 48 | }, 49 | 50 | // 复制内容到剪切板 51 | copyString (string) { 52 | return new Promise((resolve, reject) => { 53 | tools.copyString(string, ({ status, errorMsg, data }) => { 54 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 55 | }) 56 | }) 57 | }, 58 | 59 | scan () { 60 | return new Promise((resolve, reject) => { 61 | tools.scan(({ status, errorMsg, data }) => { 62 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 63 | }) 64 | }) 65 | } 66 | } 67 | 68 | // support bmchat send options include function option 69 | Vue.prototype.$format = (options) => { 70 | return JSON.stringify(JSON.stringify(options, function (key, val) { 71 | if (typeof val === 'function') { 72 | return val + ''; 73 | } 74 | return val; 75 | })) 76 | } 77 | } 78 | 79 | Vue.use(Tools) 80 | -------------------------------------------------------------------------------- /src/image.js: -------------------------------------------------------------------------------- 1 | const imageModule = weex.requireModule('bmImage') 2 | const browser = weex.requireModule('bmBrowserImg') 3 | const bmAxios = weex.requireModule('bmAxios') 4 | 5 | const Image = Object.create(null) 6 | 7 | Image.install = (Vue, options) => { 8 | Vue.prototype.$image = { 9 | // upload change to pickAndUpload 10 | pickAndUpload ({ maxCount = 1, imageWidth = 0, url = '', allowCrop = false, header = {}, params = {}}) { 11 | return new Promise((resolve, reject) => { 12 | var _params = { 13 | maxCount, 14 | imageWidth, 15 | allowCrop, 16 | header, 17 | params 18 | } 19 | if (url) _params.url = url 20 | imageModule.uploadImage(_params, ({ status, errorMsg, data }) => { 21 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 22 | }) 23 | }) 24 | }, 25 | upload ({ url = '', params = {}, header = {}, source = [] }) { 26 | return new Promise((resolve, reject) => { 27 | bmAxios.uploadImage({ 28 | url, params, header, images: source 29 | }, ({ status, errorMsg, data }) => { 30 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 31 | }) 32 | }) 33 | }, 34 | 35 | browser ({ index = 0, images = [], type = 'network' }) { 36 | return new Promise((resolve, reject) => { 37 | browser.open({ 38 | index, 39 | images, 40 | type 41 | }, ({ status, errorMsg, data }) => { 42 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 43 | }) 44 | }) 45 | }, 46 | camera ({ imageWidth = 0, allowCrop = false }) { 47 | return new Promise((resolve, reject) => { 48 | imageModule.camera({ 49 | imageWidth, 50 | allowCrop 51 | }, ({ status, errorMsg, data }) => { 52 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 53 | }) 54 | }) 55 | }, 56 | pick ({ maxCount = 1, imageWidth = 0, allowCrop = false }) { 57 | return new Promise((resolve, reject) => { 58 | imageModule.pick({ 59 | maxCount, 60 | imageWidth, 61 | allowCrop 62 | }, ({ status, errorMsg, data }) => { 63 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 64 | }) 65 | }) 66 | }, 67 | preview ({ index = 0, images = [] }) { 68 | return new Promise((resolve, reject) => { 69 | imageModule.preview({ 70 | index, 71 | images 72 | }, ({ status, errorMsg, data }) => { 73 | status === 0 ? resolve(data) : reject({ status, errorMsg, data }) 74 | }) 75 | }) 76 | } 77 | } 78 | } 79 | 80 | Vue.use(Image) 81 | -------------------------------------------------------------------------------- /src/axios.js: -------------------------------------------------------------------------------- 1 | import _isFunction from 'lodash/isFunction' 2 | const bmAxios = weex.requireModule('bmAxios') 3 | export default class Axios { 4 | constructor ({ timeout, apis, baseUrl = '', requestHandler, responseHandler }) { 5 | this.apis = apis 6 | this.timeout = timeout 7 | this.baseUrl = baseUrl 8 | this.requestHandler = requestHandler 9 | this.responseHandler = responseHandler 10 | return this 11 | } 12 | install (Vue) { 13 | /** 14 | * Contributor: Eric Xiao. 15 | * Description: extend promise. 16 | * Eros thanks every contributor. 17 | */ 18 | Promise.prototype.finally = function (callback) { 19 | const P = this.constructor; 20 | return this.then( 21 | value => P.resolve(callback()).then(() => value), 22 | reason => P.resolve(callback()).then(() => { throw reason }) 23 | ); 24 | }; 25 | Promise.prototype.done = function (onFulfilled, onRejected) { 26 | this.then(onFulfilled, onRejected) 27 | .catch(function (reason) { 28 | // Throw a global error 29 | setTimeout(() => { throw reason }, 0); 30 | }); 31 | }; 32 | 33 | const self = this 34 | Vue.prototype.$fetch = (options) => { 35 | return new Promise((resolve, reject) => { 36 | if (_isFunction(self.requestHandler)) { 37 | self.requestHandler(options, () => { handleAxios(options, resolve, reject) }) 38 | } else { 39 | handleAxios(options, resolve, reject) 40 | } 41 | }) 42 | } 43 | 44 | Vue.prototype.$get = (options) => { 45 | options.method = 'GET' 46 | return new Promise((resolve, reject) => { 47 | if (_isFunction(self.requestHandler)) { 48 | self.requestHandler(options, () => { handleAxios(options, resolve, reject) }) 49 | } else { 50 | handleAxios(options, resolve, reject) 51 | } 52 | }) 53 | } 54 | 55 | Vue.prototype.$post = (options) => { 56 | options.method = 'POST' 57 | return new Promise((resolve, reject) => { 58 | if (_isFunction(self.requestHandler)) { 59 | self.requestHandler(options, () => { handleAxios(options, resolve, reject) }) 60 | } else { 61 | handleAxios(options, resolve, reject) 62 | } 63 | }) 64 | } 65 | 66 | function handleAxios (options, resolve, reject) { 67 | const { name, url, data, method, header, params } = options 68 | const requestPath = name && pathFormater(name, params) 69 | bmAxios.fetch({ 70 | url: url || (self.baseUrl + requestPath), 71 | data: data || {}, 72 | method: method && method.toUpperCase() || 'GET', 73 | header: header || {}, 74 | timeout: self.timeout || 30000 75 | }, (resData) => { 76 | // 统一的监控 77 | if (_isFunction(self.responseHandler)) { 78 | self.responseHandler(options, resData, resolve, reject) 79 | } else { 80 | resolve(resData) 81 | } 82 | }) 83 | } 84 | 85 | function pathFormater (name, params) { 86 | let _path = self.apis[name] 87 | const matcher = _path.match(/[^{][a-zA-Z0-9]+(?=\})/g) 88 | 89 | if (matcher && matcher.length) { 90 | matcher.forEach(item => { 91 | if (!params[item]) throw new Error(`you are using dynamic params, but ${item} not existed in your params`) 92 | _path = _path.replace(`{${item}}`, params[item] || 'undefined') 93 | }) 94 | } 95 | 96 | return _path 97 | } 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | eros widget is the two encapsulation for eros module. 2 | 3 | ## How to use 4 | 1.install eros-widget in eros init project. 5 | ``` 6 | $ npm i eros-widget -S 7 | ``` 8 | 2.init widget in appboard js bundle.(default: `config/index.js`) 9 | ``` js 10 | import widget from 'eros-Widget' 11 | 12 | const options = {} 13 | 14 | new widget(options) 15 | ``` 16 | 17 | ## Config options 18 | ```js 19 | const options = { 20 | router: { 21 | /** 22 | * 路由配置表 23 | */ 24 | routes: {} 25 | }, 26 | ajax: { 27 | baseUrl: 'http://app.weex-eros.com:52077', 28 | /** 29 | * 接口别名 30 | */ 31 | apis: {}, 32 | // 接口超时时间 33 | timeout: 30000, 34 | 35 | /** 36 | * 请求发送统一拦截器 (可选) 37 | * options 你请求传入的所有参数和配置 38 | * next 39 | */ 40 | requestHandler (options, next) { 41 | console.log('request-opts', options) 42 | next() 43 | }, 44 | /** 45 | * 请求返回统一拦截器 (可选) 46 | * options 你请求传入的所有参数和配置 47 | * resData 服务器端返回的所有数据 48 | * resolve 请求成功请resolve你得结果,这样请求的.then中的成功回调就能拿到你resolve的数据 49 | * reject 请求成功请resolve你得结果,这样请求的.then中的失败回调就能拿到你reject的数据 50 | */ 51 | responseHandler (options, resData, resolve, reject) { 52 | const { status, errorMsg, data } = resData 53 | if (status !== 200) { 54 | console.log(`invoke error status: ${status}`) 55 | console.log(`invoke error message: ${errorMsg}`) 56 | reject(resData) 57 | } else { 58 | // 自定义请求逻辑 59 | resolve(data) 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | `router.routes`: config $router.open path alias 66 | ```js 67 | routes: { 68 | 'demo': { 69 | title: 'weex-eros demo', 70 | url: '/pages/demo/index.js' 71 | } 72 | } 73 | ``` 74 | 75 | #### `ajax.baseUrl`: config you request baseUrl 76 | #### `ajax.apis`: config you request path alias 77 | ```js 78 | apis: { 79 | 'COMMON.getInfo': '/test/info/' 80 | } 81 | ``` 82 | also you can config dynamic path. 83 | ```js 84 | apis: { 85 | 'COMMON.getInfo': '/test/info/{plaform}/{id}' 86 | } 87 | ``` 88 | and we deliver them in $get/$post params option. 89 | ```js 90 | this.$get({ 91 | name: 'COMMON.getInfo', 92 | params: { 93 | platform: 'app', 94 | id: 3 95 | }, 96 | data: { 97 | name: 'eros' 98 | } 99 | }) 100 | ``` 101 | finally our request url become : 102 | ``` 103 | ajax.baseUrl + /test/info/app/3?name=eros 104 | ``` 105 | 106 | #### `ajax.timeout`: request timeout time.(ms) 107 | #### `ajax.requestHandler`: request Interceptor 108 | #### `ajax.responseHandler`: response Interceptor 109 | 110 | ## How to develop 111 | 112 | 1.init eros project. 113 | 114 | ``` 115 | $ eros init 116 | ``` 117 | 118 | 2.cd your project and enter src/js 119 | 120 | ``` 121 | $ cd eros-demo/src/js 122 | ``` 123 | 124 | 3.clone eros-widget.git. 125 | ``` 126 | $ git clone https://github.com/bmfe/eros-widget.git eros-widget 127 | ``` 128 | 129 | 4.add config `eros.dev.js` alias option. 130 | ```js 131 | "ErosWidget": "js/eros-widget" 132 | ``` 133 | 134 | 5.init widget in appboard js bundle.(default: `config/index.js`) 135 | ```js 136 | import ErosWidget from 'ErosWidget' 137 | 138 | const options = {} 139 | 140 | new ErosWidget(options) 141 | ``` 142 | 143 | > welcome your pull request! eros loves you. 144 | 145 | ## Records 146 | # 1.0.1-beta.8 147 | * [bugfix] backAppeared 和 beforeBackAppeared 不执行的问题 148 | * [bugfix] pickAndUpload 成功后成功回调进入 error 回调 149 | * [optimize] fetch method GET POST 大小写均可 150 | 151 | # 1.0.1-beta.7 152 | * [bugfix] $image.upload status code 判断失误 153 | 154 | # 1.0.1-beta.6 155 | * [optimize] 重写 $bindingx 的引用方法,重写了 bind 方法,支持所有内部暴露出来的方法 156 | * [bugfix] 父子组件同时注册生命周期,执行两次的 bug 157 | 158 | # 1.0.1-beta.3/4/5 159 | * [add] support bindingx. 160 | 161 | # 1.0.1-beta.2 162 | * [bugfix] beforeAppear 和 beforeBackAppear 另个页面返回或者结束后仍会获取到上次的$router.setBackParams数据的 bug. 163 | 164 | # 1.0.1-beta.1 165 | * [bugfix] 修复全局事件注册2次的bug 166 | 167 | # 1.0.1 168 | 生命周期,自定义事件均做调整,如果不更改,请不要升级到 `1.0.1`。 169 | * `bmRouter` 变更为 `eros` 170 | * 添加 `pushMessage`,可在页面中监听推送 171 | * 添加 `appActive`,可在页面中监听 app 切换到后台事件 172 | * 添加 `appDeactive`,可在页面中监听 app 切换至前台事件 173 | * `viewWillAppear` 变更为 `beforeAppear`,beforeBackAppear,通过打开类型来做区分 174 | * `viewDidAppear` 变更为 `appeared`,`backAppeared`,通过打开类型来做区分 175 | * `viewWillDisappear` 变更为 `beforeDisappear` 176 | * `viewDidDisappear` 变更为 `disappeared` 177 | 178 | # 1.0.0 179 | * 从模板中抽离 widget 完成 -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: songqi 3 | * @Date: 2017-01-11 4 | * @Last modified by: songqi 5 | * @Last modified time: 2017-04-05 6 | */ 7 | 8 | const modal = weex.requireModule('bmModal') 9 | const router = weex.requireModule('bmRouter') 10 | const storage = weex.requireModule('bmStorage') 11 | const globalEvent = weex.requireModule('globalEvent') 12 | 13 | import isFunction from 'lodash/isFunction' 14 | import _isUndefined from 'lodash/isUndefined' 15 | import _isNumber from 'lodash/isNumber' 16 | 17 | // 客户端默认打开页面的动画 18 | export const DEFAULT_ANIMATETYPE = 'PUSH' 19 | 20 | 21 | export default class Router { 22 | constructor ({ routes }) { 23 | this.routes = routes 24 | return this 25 | } 26 | install (Vue, options) { 27 | const self = this 28 | Vue.prototype.$router = { 29 | open (options = {}) { 30 | const currentPageInfo = this.getUrl(options.name) 31 | if (!currentPageInfo || !currentPageInfo.url) return 32 | return new Promise((resolve, reject) => { 33 | let preOptions = { 34 | url: currentPageInfo.url, 35 | type: options.type || DEFAULT_ANIMATETYPE, 36 | params: options.params || {}, 37 | canBack: _isUndefined(options.canBack) || options.canBack, 38 | gesBack: _isUndefined(options.gesBack) || options.gesBack, 39 | navShow: options.navShow != undefined ? options.navShow : !!currentPageInfo.title, 40 | navTitle: options.navTitle || currentPageInfo.title, 41 | isRunBackCallback: isFunction(options.backCallback) 42 | } 43 | 44 | if(!!options.statusBarStyle) preOptions.statusBarStyle = options.statusBarStyle 45 | if(!!options.backgroundColor) preOptions.backgroundColor = options.backgroundColor 46 | 47 | router.open(preOptions, (data) => { 48 | if (isFunction(options.backCallback)) { 49 | options.backCallback.call(this, data) 50 | } 51 | }) 52 | }) 53 | }, 54 | back (options = {}) { 55 | return new Promise((resolve, reject) => { 56 | router.back({ 57 | type: options.type || DEFAULT_ANIMATETYPE, 58 | length: options.length || 1 59 | }, (data) => { 60 | if (isFunction(options.callback)) { 61 | options.callback.call(this, data) 62 | } 63 | resolve(data) 64 | }) 65 | }) 66 | }, 67 | getParams (callback) { 68 | return new Promise((resolve, reject) => { 69 | router.getParams((params) => { 70 | if (isFunction(callback)) { 71 | callback.call(this, params) 72 | } 73 | resolve(params) 74 | }) 75 | }) 76 | }, 77 | getUrl (page) { 78 | const currentPageInfo = self.routes[page] 79 | if (!currentPageInfo) { 80 | modal.alert({ 81 | message: '跳转页面不存在', 82 | okTitle: '确定' 83 | }) 84 | return false 85 | } 86 | return currentPageInfo 87 | }, 88 | refresh () { 89 | router.refreshWeex() 90 | }, 91 | setBackParams (params) { 92 | _isNumber(params) && params.toString() 93 | storage.setData('router.backParams', JSON.stringify(params)) 94 | }, 95 | toWebView (params) { 96 | if (!params.url) return 97 | params.title = params.title || 'weex-eros' 98 | // params.shareInfo = { 99 | // title: params.shareTitle, 100 | // content: params.content || '', 101 | // image: params.image || '', 102 | // url: params.url || '', 103 | // platforms: params.platforms || [] // 传空的话默认全部 104 | // } 105 | router.toWebView(params) 106 | }, 107 | toMap (options) { 108 | // options = { 109 | // type:'NAVIGATION', //type类型:NAVIGATION(表现方式为:地图上添加起点终点标示大头针,终点标示上面有个导航的按钮) 110 | // title: '页面标题', //页面标题 111 | // navigationInfo: { 112 | // title: '北京朝阳医院', //目的地名称 113 | // address: '北京市朝阳区工体南路8号', //目的地地址 114 | // longitude:'', //目的地经度 115 | // latitude:'' //目的地纬度 116 | // } 117 | // } 118 | router.toMap(options) 119 | }, 120 | openBrowser (url = '') { 121 | if (!url) return 122 | router.openBrowser(url) 123 | }, 124 | setHomePage (url = '') { 125 | router.setHomePage(url) 126 | }, 127 | finish () { 128 | router.finish() 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/mixins.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 消息推送 4 | * options 客户端个推推送的所有消息 5 | */ 6 | 7 | import _isArray from 'lodash/isArray' 8 | import _clone from 'lodash/clone' 9 | 10 | const globalEvent = weex.requireModule('globalEvent') 11 | const storage = weex.requireModule('bmStorage') 12 | const router = weex.requireModule('bmRouter') 13 | const modal = weex.requireModule('bmModal') 14 | 15 | const Mixins = Object.create(null) 16 | const GLOBAL_EVENTS = Object.create(null) 17 | 18 | let EventsMakerInstance = null 19 | 20 | class EventsMaker { 21 | constructor(events) { 22 | if (!EventsMakerInstance) { 23 | let _events = _clone(events) 24 | if(!events || !events.length) return 25 | const beforeAppearPosition = _events.indexOf('beforeAppear') 26 | const beforeBackAppearPosition = _events.indexOf('beforeBackAppear') 27 | if(beforeAppearPosition > -1 && beforeBackAppearPosition > -1) _events.splice(beforeBackAppearPosition, 1) 28 | 29 | const appearedPosition = _events.indexOf('appeared') 30 | const backAppearedPosition = _events.indexOf('backAppeared') 31 | if(appearedPosition > -1 && backAppearedPosition > -1) _events.splice(backAppearedPosition, 1) 32 | 33 | _events.map(event => { 34 | this[`${event}Maker`]() 35 | }) 36 | EventsMakerInstance = this 37 | } 38 | return EventsMakerInstance 39 | } 40 | pushMessageMaker() { 41 | globalEvent.addEventListener('pushMessage', (options) => { 42 | _isArray(GLOBAL_EVENTS['pushMessage']) && GLOBAL_EVENTS['pushMessage'].map((item) => { 43 | item(options) 44 | }) 45 | }) 46 | } 47 | beforeAppearMaker() { 48 | globalEvent.addEventListener('viewWillAppear', (options) => { 49 | if (options.type === 'open' || options.type === 'refresh') { 50 | router.getParams((params) => { 51 | _isArray(GLOBAL_EVENTS['beforeAppear']) &&GLOBAL_EVENTS['beforeAppear'].map((item) => { 52 | item(params, options) 53 | }) 54 | }) 55 | } else if (options.type === 'back') { 56 | storage.getData('router.backParams', ({ status, errorMsg, data }) => { 57 | const result = status === 0 ? JSON.parse(data) : '' 58 | _isArray(GLOBAL_EVENTS['beforeBackAppear']) &&GLOBAL_EVENTS['beforeBackAppear'].map((item) => { 59 | item(result, options) 60 | }) 61 | storage.deleteData('router.backParams') 62 | }) 63 | } 64 | }) 65 | } 66 | beforeBackAppearMaker() { 67 | this.beforeAppearMaker() 68 | } 69 | appearedMaker() { 70 | globalEvent.addEventListener('viewDidAppear', (options) => { 71 | if (options.type === 'open' || options.type === 'refresh') { 72 | router.getParams((params) => { 73 | _isArray(GLOBAL_EVENTS['appeared']) && GLOBAL_EVENTS['appeared'].map((item) => { 74 | item(params, options) 75 | }) 76 | }) 77 | } else if (options.type === 'back') { 78 | storage.getData('router.backParams', ({ status, errorMsg, data }) => { 79 | const result = status === 0 ? JSON.parse(data) : '' 80 | console.log(GLOBAL_EVENTS) 81 | _isArray(GLOBAL_EVENTS['backAppeared']) && GLOBAL_EVENTS['backAppeared'].map((item) => { 82 | item(result, options) 83 | }) 84 | storage.deleteData('router.backParams') 85 | }) 86 | } 87 | }) 88 | } 89 | backAppearedMaker() { 90 | this.appearedMaker() 91 | } 92 | beforeDisappearMaker() { 93 | globalEvent.addEventListener('viewWillDisappear', (options) => { 94 | modal.hideLoading() 95 | _isArray(GLOBAL_EVENTS['beforeDisappear']) && GLOBAL_EVENTS['beforeDisappear'].map((item) => { 96 | item(options) 97 | }) 98 | }) 99 | } 100 | disappearedMaker() { 101 | globalEvent.addEventListener('viewDidDisappear', (options) => { 102 | _isArray(GLOBAL_EVENTS['disappeared']) &&GLOBAL_EVENTS['disappeared'].map((item) => { 103 | item(options) 104 | }) 105 | }) 106 | } 107 | appDeactiveMaker() { 108 | globalEvent.addEventListener('appDeactive', (options) => { 109 | _isArray(GLOBAL_EVENTS['appDeactive']) && GLOBAL_EVENTS['appDeactive'].map((item) => { 110 | item(options) 111 | }) 112 | }) 113 | } 114 | appActiveMaker() { 115 | globalEvent.addEventListener('appActive', (options) => { 116 | _isArray(GLOBAL_EVENTS['appActive']) && GLOBAL_EVENTS['appActive'].map((item) => { 117 | item(options) 118 | }) 119 | }) 120 | } 121 | } 122 | 123 | Mixins.install = (Vue, options) => { 124 | Vue.mixin({ 125 | beforeCreate () { 126 | if (!this.$options.eros) return 127 | const erosEvents = this.$options.eros 128 | const erosEventsMap = Object.keys(this.$options.eros) 129 | new EventsMaker(erosEventsMap) 130 | erosEventsMap.map(event => { 131 | if (!GLOBAL_EVENTS[event]) GLOBAL_EVENTS[event] = [] 132 | GLOBAL_EVENTS[event].push(erosEvents[event].bind(this)) 133 | }) 134 | } 135 | }) 136 | } 137 | 138 | Vue.use(Mixins) 139 | --------------------------------------------------------------------------------