Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

希望能提供对支付宝小程序js api转换的处理 #41

Open
chaucerling opened this issue Oct 8, 2018 · 5 comments
Open

希望能提供对支付宝小程序js api转换的处理 #41

chaucerling opened this issue Oct 8, 2018 · 5 comments
Assignees

Comments

@chaucerling
Copy link

类似这个项目https://github.com/douzi8/wxToAlipay/tree/8047bb809f32a2c882f24a5584b264018e91b1ab/lib/js

能自定义mapping,封装微信和小程序的差异

@Cap32 Cap32 self-assigned this Oct 9, 2018
@Cap32
Copy link
Member

Cap32 commented Oct 9, 2018

@chaucerling
Copy link
Author

//src/shim.js
function noop() {}

function paramsMap(options, maps = {}) {
	let params = {};

	for (let key in options) {
		let myKey = maps.hasOwnProperty(key) ? maps[key] : key;
		params[myKey] = options[key];
	}

	return params;
}

// 修改 api params 格式
function setStorageSync(key, data) {
	return {
		key,
		data,
	};
}

function removeStorageSync(key) {
	return {
		key,
	};
}

function getStorageSync(key) {
	return my.getStorageSync({
		key: key,
	}).data;
}

function previewImage(options) {
	let params = paramsMap(options);
	let current = params.current;

	if (current) {
		current = options.urls.indexOf(current);
	}

	if (current === -1 || !current) {
		current = 0;
	}

	params.current = current;

	return params;
}

function makePhoneCall(options) {
	return paramsMap(options, {
		phoneNumber: 'number',
	});
}

function request(options) {
	let params = paramsMap(options, {
		header: 'headers',
	});
	let success = params.success || noop;

	params.success = function (res) {
		let result = paramsMap(res, {
			headers: 'header',
			status: 'statusCode',
		});

		success(result);
	};

	return params;
}

/**
 * wx success里面的system字段为'Android 6.0.1'
 */
function getSystemInfo(options) {
	let params = paramsMap(options);
	let success = params.success || noop;

	params.success = function (res) {
		success(getSystemInfoSync(res));
	};

	return params;
}

function getSystemInfoSync(res) {
	res.system = res.platform + ' ' + res.system;

	// 支付宝小程序windowHeight可能拿到0
	if (!res.windowHeight) {
		res.windowHeight = parseInt(res.screenHeight * res.windowWidth / res.screenWidth, 10) - 40;
	}

	return res;
}

/**
 * wx模态弹窗不同的参数对应到支付宝confirm和alert API
 */
function showModal(options) {
	let params = paramsMap(options);
	let showCancel = params.showCancel;

	if (typeof showCancel === 'undefined') {
		showCancel = true;
	}

	// 确认框
	if (showCancel) {
		params.confirmButtonText = params.confirmText;
		params.cancelButtonText = params.cancelText;
	}
	else {
		// 提醒框
		params.buttonText = params.confirmText;
	}

	my[showCancel ? 'confirm' : 'alert'](params);
}

/**
 * 参数{icon: 'loading'} 无法成功映射,建议不要使用
 */
function showToast(options) {
	let params = paramsMap(options, {
		title: 'content',
		icon: 'type',
	});

	return params;
}

/**
 * sucess回调没有取消操作
 * 点击取消或蒙层时,回调fail, errMsg 为 "showActionSheet:fail cancel"
 */
function showActionSheet(options) {
	let params = paramsMap(options, {
		itemList: 'items',
	});

	let success = params.success || noop;
	let fail = params.fail || noop;

	params.success = function ({
		index: tapIndex,
	}) {
		if (tapIndex === -1) {
			fail({
				errMsg: 'showActionSheet:fail cancel',
			});
		}
		else {
			success({
				tapIndex,
			});
		}
	};

	return params;
}

// 此函数可以根据业务自行定制, 参数无法统一映射
function login(options) {
	let params = paramsMap(options);
	let success = params.success || noop;

	params.scopes = 'auth_user';

	params.success = function (res) {
		success({
			code: res.authCode,
		});
	};

	return params;
}

// 此函数可以根据业务自行定制, 参数无法统一映射
function requestPayment(options) {
	let params = paramsMap(options, {
		alipay_trade_body: 'orderStr',
	});

	let success = params.success || noop;
	let fail = params.fail || noop;

	params.success = function (res) {
		if (res.resultCode === 9000) {
			success();
		}
		else {
			fail();
		}
	};

	return params;
}

function showLoading(options) {
	let params = paramsMap(options, {
		title: 'content',
	});

	return params;
}

const FUNCTION_MAP = {
	login: 'getAuthCode',
	request: 'httpRequest',
	setNavigationBarTitle: 'setNavigationBar',
	setNavigationBarColor: 'setNavigationBar',
	requestPayment: 'tradePay',
};

// // 处理
const shim = {};
const myShim = () => {
	for (let key in my) {
		if (my.hasOwnProperty(key) && typeof my[key] === 'function') {
			let result = FUNCTION_MAP[key];
			let apiName = result || key;
			let paramsFunc = null;
			let func = null;
			switch (key) {
				case 'login':
					paramsFunc = login;
					break;
				case 'showActionSheet':
					paramsFunc = showActionSheet;
					break;
				case 'showToast':
					paramsFunc = showToast;
					break;
				case 'showLoading':
					paramsFunc = showLoading;
					break;
				case 'request':
					paramsFunc = request;
					break;
				case 'makePhoneCall':
					paramsFunc = makePhoneCall;
					break;
				case 'previewImage':
					paramsFunc = previewImage;
					break;
				case 'setStorageSync':
					paramsFunc = setStorageSync;
					break;
				case 'removeStorageSync':
					paramsFunc = removeStorageSync;
					break;
				case 'getSystemInfo':
					paramsFunc = getSystemInfo;
					break;
				case 'requestPayment':
					paramsFunc = requestPayment;
					break;
				case 'showModal':
					paramsFunc = showModal;
					break;
				case 'getStorageSync':
					func = getStorageSync;
					break;
				case 'getSystemInfoSync':
					func = getSystemInfoSync;
					break;
					// return getSystemInfoSync(my.getSystemInfoSync());
			}
			shim[key] = (options, ...params) => {
				if (func) {
					console.log(`调用 api:${apiName}, params:${JSON.stringify(params)}`);
					return func(options, ...params);
				}
				else if (paramsFunc) {
					console.log(`调用 void api:${apiName}, params:${JSON.stringify(paramsFunc(options, ...params))}`);
					return my[apiName](paramsFunc(options, ...params));
				}
				else {
					console.log(`调用 void api:${apiName}, params:${JSON.stringify(options, ...params)}`);
					return my[apiName](options, ...params);
				}
			};
		}
	}
};
myShim();

export {
	shim as default,
};

然后在具体页面引用 import shim from 'shim.js';,将 wx. 调用改为 shim. 是可以在编译为支付宝小程序时正确执行,不需要改动调用参数

但我想在webpack打包成支付宝小程序时引用 shim.js,并且将 wx 改为 shim

//webpack.config.babel.js
plugins: [
  new EnvironmentPlugin({
    NODE_ENV: 'development',
  }),
  new DefinePlugin({
    __DEV__: isDev,
    __WECHAT__: isWechat,
    __ALIPAY__: isAlipay,
    wx: isWechat ? 'wx' : 'shim',
    my: isWechat ? 'wx' : 'shim',
  }),
  new WXAppWebpackPlugin({
    clear: !isDev,
  }),
  new ProvidePlugin({
    'shim': 'shim',
  }),
  new optimize.ModuleConcatenationPlugin(),
  new IgnorePlugin(/vertx/),
  shouldLint && new StylelintPlugin(),
  min && new MinifyPlugin(),
  new CopyPlugin(copyPatterns, { context: srcDir }),
].filter(Boolean),
///
alias: {
  utils: require('path').resolve(__dirname, 'src/utils'),
  shim: require('path').resolve(__dirname, 'src/shim.js'),
},

这样就报错了
Uncaught ReferenceError: shim is not defined

@Cap32
Copy link
Member

Cap32 commented Oct 11, 2018

Uncaught ReferenceError: shim is not defined 看起来是 runtime error,能否提供更多的调用上下文和 error stack?

不过从你的配置可以看得出一点问题:

//webpack.config.babel.js
// ...
new DefinePlugin({
    __DEV__: isDev,
    __WECHAT__: isWechat,
    __ALIPAY__: isAlipay,
    wx: isWechat ? 'wx' : 'shim',
    my: isWechat ? 'wx' : 'shim',
  })

当打包 target 是支付宝小程序时,my 会变成 shim,而在 src/shim.js 里,for (let key in my) { 这里的 my 将会被转换为 shim,此时 shim 为空对象。因此个人建议 webpack.config.jsDefinePlugin 应该是 my: isWechat ? 'wx' : 'my',不然编译后的代码将无法引用 my 对象

另外,src/shim.js 里面可以通过 if (__WECHAT__ ) {}if (__ALIPAY__) {} 来判断环境差异

@chaucerling
Copy link
Author

我现在写了一个 miniapp.js 来封装一般的 api,在 page.js 里将 wxmy 替换为 miniapp
业务相关的 api,例如登录授权和支付要看具体流程另外封装

// src/utils/miniapp.js
// 整合各小程序的api,命名和调用参数以微信为标准
import promisify from 'utils/promisify.js';

function noop() {}

function paramsMap(options, maps = {}) {
	let params = {};

	for (let key in options) {
		let myKey = maps.hasOwnProperty(key) ? maps[key] : key;
		params[myKey] = options[key];
	}
	return params;
}

function mySystemInfo2wx(systemInfo) {
	systemInfo.system = `${systemInfo.platform} ${systemInfo.system}`;
	// 支付宝小程序windowHeight可能拿到0
	if (!systemInfo.windowHeight) {
		systemInfo.windowHeight = parseInt(systemInfo.screenHeight * systemInfo.windowWidth / systemInfo.screenWidth, 10) - 40;
	}
	return systemInfo;
}

const miniapp = {
	request: (options) => {
		if (__WECHAT__) {
			return wx.request(options);
		}
		if (__ALIPAY__) {
			let params = paramsMap(options, {
				header: 'headers',
			});
			let success = params.success || noop;
			params.success = function (res) {
				let result = paramsMap(res, {
					headers: 'header',
					status: 'statusCode',
				});

				success(result);
			};
			return my.httpRequest(params);
		}
	},
	getLocation: (options) => {
		if (__WECHAT__) {
			// wgs84 返回 gps 坐标,gcj02 返回可用于 wx.openLocation 的坐标
			return wx.getLocation(options);
		}
		if (__ALIPAY__) {
			console.warn('alipay api: getLocation 只支持 gcj02 坐标系');
			options.type = 0; // 高德地图用的是 gcj02
			return my.getLocation(options);
		}
	},
	openLocation: (options) => {
		if (__WECHAT__) {
			return wx.openLocation(options);
		}
		if (__ALIPAY__) {
			options.scale = options.scale || 18;
			return my.getLocation(options);
		}
	},
	getSystemInfo: (options) => {
		if (__WECHAT__) {
			return wx.getSystemInfo(options);
		}
		if (__ALIPAY__) {
			let success = options.success || noop;
			options.success = function (res) {
				success(mySystemInfo2wx(res));
			};
			return my.getSystemInfo(options);
		}
	},
	getSystemInfoSync: () => {
		if (__WECHAT__) {
			return wx.getSystemInfoSync();
		}
		if (__ALIPAY__) {
			return mySystemInfo2wx(my.getSystemInfoSync());
		}
	},
	getStorage: (options) => {
		if (__WECHAT__) {
			return wx.getStorage(options);
		}
		if (__ALIPAY__) {
			let fail = options.fail || noop;
			options.success = function (res) {
				if (!res.data) {
					fail(res);
				}
 else {
					console.log(res);
				}
			};
			return my.getStorage(options);
		}
	},
	getStorageSync: (key) => {
		if (__WECHAT__) {
			return wx.getStorageSync(key);
		}
		if (__ALIPAY__) {
			return my.getStorageSync({
				key: key,
			}).data;
		}
	},
	setStorage: (options) => {
		if (__WECHAT__) {
			return wx.setStorage(options);
		}
		if (__ALIPAY__) {
			return my.setStorage(options);
		}
	},
	setStorageSync: (key, data) => {
		if (__WECHAT__) {
			return wx.setStorageSync(key, data);
		}
		if (__ALIPAY__) {
			return my.setStorageSync({
				key: key,
				data: data,
			});
		}
	},
	removeStorage: (options) => {
		if (__WECHAT__) {
			return wx.removeStorage(options);
		}
		if (__ALIPAY__) {
			return my.removeStorage(options);
		}
	},
	removeStorageSync: (key) => {
		if (__WECHAT__) {
			return wx.removeStorageSync(key);
		}
		if (__ALIPAY__) {
			return my.removeStorageSync({
				key: key,
			});
		}
	},
	clearStorage: (options) => {
		if (__WECHAT__) {
			return wx.clearStorage(options);
		}
		if (__ALIPAY__) {
			return my.clearStorage(options);
		}
	},
	clearStorageSync: () => {
		if (__WECHAT__) {
			return wx.clearStorageSync();
		}
		if (__ALIPAY__) {
			return my.clearStorageSync();
		}
	},
	redirectTo: (options) => {
		if (__WECHAT__) {
			return wx.redirectTo(options);
		}
		if (__ALIPAY__) {
			return my.redirectTo(options);
		}
	},
	navigateTo: (options) => {
		if (__WECHAT__) {
			return wx.navigateTo(options);
		}
		if (__ALIPAY__) {
			return my.navigateTo(options);
		}
	},
	navigateBack: (options) => {
		if (__WECHAT__) {
			return wx.navigateBack(options);
		}
		if (__ALIPAY__) {
			return my.navigateBack(options);
		}
	},
	showModal: (options) => {
		if (__WECHAT__) {
			return wx.showModal(options);
		}
		if (__ALIPAY__) {
			// 支付宝不支持自定义按钮颜色
			if (options.showCancel) {
				let params = paramsMap(options, {
					confirmButtonText: 'confirmText',
					cancelButtonText: 'cancelText',
				});
				return my.confirm(params);
			}
			else {
				let params = paramsMap(options, {
					buttonText: 'confirmText',
				});
				return my.alert(params);
			}
		}
	},
	showToast: (options) => {
		if (__WECHAT__) {
			return wx.showToast(options);
		}
		if (__ALIPAY__) {
			let params = paramsMap(options, {
				content: 'title',
				type: 'icon',
			});
			params.duration = params.duration || 1500;
			if (params.type === 'loading') {
				params.type = 'none';
				console.warn('alipay api: showToast 不支持 type=loading, 暂时使用 none 代替');
			}
			return my.showToast(options);
		}
	},
	hideToast: () => {
		if (__WECHAT__) {
			return wx.hideToast();
		}
		if (__ALIPAY__) {
			return my.hideToast();
		}
	},
	showShareMenu: (options) => {
		if (__WECHAT__) {
			return wx.showShareMenu(options);
		}
		if (__ALIPAY__) {
			return null;
		}
	},
	makePhoneCall: (options) => {
		if (__WECHAT__) {
			return wx.makePhoneCall(options);
		}
		if (__ALIPAY__) {
			let params = paramsMap(options, {
				number: 'phoneNumber',
			});
			return my.makePhoneCall(params);
		}
	},
	setNavigationBarTitle: (options) => {
		if (__WECHAT__) {
			return wx.setNavigationBarTitle(options);
		}
		if (__ALIPAY__) {
			return my.setNavigationBar(options);
		}
	},
	setNavigationBarColor: (options) => {
		if (__WECHAT__) {
			return wx.setNavigationBarColor(options);
		}
		if (__ALIPAY__) {
			console.warn('alipay api: setNavigationBar 不支持 frontColor, animation');
			return my.setNavigationBar(options);
		}
	},
};

miniapp.pro = {};
for (let key in miniapp) {
	if (miniapp.hasOwnProperty(key) && typeof miniapp[key] === 'function') {
		miniapp.pro[key] = promisify(miniapp[key]);
	}
}

export {
	miniapp as default,
};

webpack 添加了对 json 和 wxml 的转换

//webpack.config.babel.js
module: {
  rules: [isWechat ? {} : {
      test: /\.json$/,
      loader: 'string-replace-loader',
      options: {
        multiple: [{
            search: 'navigationBarTitleText',
            replace: 'defaultTitle',
            flags: 'g',
          },
          {
            search: 'navigationBarBackgroundColor',
            replace: 'titleBarColor',
            flags: 'g',
          },
          {
            search: 'enablePullDownRefresh',
            replace: 'pullRefresh',
            flags: 'g',
          },
          {
            search: 'usingComponents',
            replace: 'usingComponents',
            flags: 'g',
          }
        ],
      },
    },
    // *.wxml
    isWechat ? {} : {
      test: /\.wxml$/,
      include: /src/,
      loader: 'string-replace-loader',
      options: {
        multiple: [{
            search: 'wx:if',
            replace: 'a:if',
            flags: 'g',
          },
          {
            search: 'wx:elif',
            replace: 'a:elif',
            flags: 'g',
          },
          {
            search: 'wx:else',
            replace: 'a:else',
            flags: 'g',
          },
          {
            search: 'wx:for',
            replace: 'a:for',
            flags: 'g',
          },
          {
            search: 'wx:for-index',
            replace: 'a:for-index',
            flags: 'g',
          },
          {
            search: 'wx:for-item',
            replace: 'a:for-item',
            flags: 'g',
          },
          {
            search: 'bindtap',
            replace: 'onTap',
            flags: 'g',
          },
          {
            search: 'catchtap',
            replace: 'catchTap',
            flags: 'g',
          },
          {
            search: 'bindinput',
            replace: 'onInput',
            flags: 'g',
          },
          {
            search: 'bindchange',
            replace: 'onChange',
            flags: 'g',
          },
          {
            search: 'bindfocus',
            replace: 'onFocus',
            flags: 'g',
          },
          {
            search: 'bindsubmit',
            replace: 'onSubmit',
            flags: 'g',
          },
          {
            search: 'bindscrolltolower',
            replace: 'onScrollToLower',
            flags: 'g',
          },
        ],
      },
    },
  ]
}

//  全局引入 miniapp,供 page.js 调用
new ProvidePlugin({
    'miniapp': [require('path').resolve(__dirname, 'src/utils/miniapp.js'), 'default'],
}),

@chaucerling
Copy link
Author

我在开发支付宝小程序过程中遇到的一些兼容问题,然后在这个项目的基础上添加了一些功能,保证代码能以微信的标准做开发
写了一个example,如果这边考虑整合,我可以提交一个pr
https://github.com/chaucerling/wxapp-boilerplate-alipay-example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants