Files
juzi/taobao/utils/permissions.js
T
xsl 774376fa16 feat: 完善淘宝小程序拍照、报告与登录流程
接入本地实时识别和自动三连拍,调整报告裁切、商品跳转、分享标题及居中页眉;完善游客会话、后台渠道隔离、部署脚本与回归测试。
2026-11-04 15:07:51 +08:00

117 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Taobao scopes are independent: userInfo login does not grant camera/album.
//
// 平台差异(2026-11-01 真机实测,SDKVersion=2.9.4):
// my.getSetting 返回 {"scope.userInfo":true,"scope.camera":true}
// my.authorize({scopes:'camera'}) → 失败 {"errorCode":"5022","errorMessage":"Scope:camera信息不存在"}
// my.authorize({scopes:'scope.camera'}) → 成功
// 结论:淘宝用带前缀的 scope.camera / scope.album(和微信一致),裸名不存在。
// 这里优先用带前缀的写法,失败再退回裸名(兼容其它淘宝/SDK 版本)。
var denied = {};
var lastError = null;
function bare(scope) { return scope.replace(/^scope\./, ''); }
function prefixed(scope) { return /^scope\./.test(scope) ? scope : 'scope.' + scope; }
function state(settings, scope) {
settings = settings || {};
var key = bare(scope);
return typeof settings[scope] === 'boolean' ? settings[scope] : settings[key];
}
function settings() {
return new Promise(function (resolve) {
if (typeof my.getSetting !== 'function') { resolve({}); return; }
try {
my.getSetting({
success: function (res) { resolve((res && res.authSetting) || {}); },
fail: function () { resolve({}); }
});
} catch (e) { resolve({}); } // API 不存在/同步抛错:当作空设置,走后续兜底
});
}
// 平台是否具备内置相机能力(淘宝部分版本没有 <camera> / createCameraContext)
function cameraSupported() {
try {
if (typeof my.createCameraContext !== 'function') return false;
if (typeof my.canIUse === 'function') return !!my.canIUse('camera') && !!my.canIUse('createCameraContext');
return true;
} catch (e) { return false; }
}
function describe(err) {
if (err === undefined || err === null) return 'null';
try { return JSON.stringify(err).slice(0, 200); } catch (e) { return String(err).slice(0, 200); }
}
function rejected(scope) {
var err = new Error(scope === 'scope.camera'
? '相机权限未开启:请点「授权设置」开启,或直接用系统相机/相册'
: '相册权限未开启,请在授权设置中开启');
err.permission = scope; err.detail = lastError;
return err;
}
// 授权:先按支付宝/淘宝的裸名字(camera),失败且不是"用户拒绝"时再试微信式(scope.camera)
function authorizeOnce(scopes) {
return new Promise(function (resolve, reject) {
my.authorize({ scopes: scopes,
success: function (res) { resolve({ scopes: scopes, ok: true, res: res }); },
fail: function (err) { reject({ scopes: scopes, ok: false, err: err }); } });
});
}
function ensure(scope) {
// This app only captures still photos and selects existing images. Region
// selection is manual and spoken guidance is playback, never recording.
// Reject unrelated scopes before even querying platform authorization state.
if (scope !== 'scope.camera' && scope !== 'scope.album') {
return Promise.reject(new Error('当前功能不需要此项授权'));
}
return settings().then(function (current) {
if (state(current, scope) === true) { delete denied[scope]; return; }
if (state(current, scope) === false || denied[scope]) throw rejected(scope);
if (typeof my.authorize !== 'function') throw new Error('当前淘宝版本不支持授权,请升级后重试');
return authorizeOnce(prefixed(scope)).catch(function (first) {
lastError = describe(first.err);
return authorizeOnce(bare(scope)).catch(function (second) {
lastError = describe(first.err) + ' / ' + describe(second.err);
denied[scope] = true;
throw rejected(scope);
});
}).then(function () { delete denied[scope]; });
});
}
function open() {
return new Promise(function (resolve, reject) {
if (typeof my.openSetting !== 'function') { reject(new Error('请在小程序右上角的授权设置中开启权限')); return; }
my.openSetting({ success: function (res) {
var current = res.authSetting || {};
['scope.camera', 'scope.album'].forEach(function (scope) { if (state(current, scope) === true) delete denied[scope]; });
resolve(current);
}, fail: function () { reject(new Error('无法打开授权设置,请从小程序右上角进入')); } });
});
}
// 权限诊断:把真机上的真实能力/返回值打出来,供页面展示与截图
function diagnose() {
var info = [];
try { info.push('SDKVersion=' + (my.SDKVersion || '(none)')); } catch (e) { info.push('SDKVersion=error'); }
['camera', 'createCameraContext', 'chooseImage', 'authorize', 'getSetting', 'openSetting', 'getImageInfo', 'createCanvasContext'].forEach(function (api) {
var use = 'n/a';
try { use = typeof my.canIUse === 'function' ? (my.canIUse(api) ? 'yes' : 'no') : 'no-canIUse'; } catch (e) { use = 'canIUse-err'; }
info.push(api + ': typeof=' + (typeof my[api]) + ' canIUse=' + use);
});
return settings().then(function (current) {
info.push('authSetting=' + describe(current));
if (typeof my.authorize !== 'function') { info.push('authorize: 无此 API'); return info; }
return authorizeOnce(prefixed('camera')).then(function (r) {
info.push('authorize(scope.camera)=ok ' + describe(r.res));
}, function (r) {
info.push('authorize(scope.camera)=fail ' + describe(r.err));
return authorizeOnce(bare('camera')).then(function (r2) {
info.push('authorize(camera)=ok ' + describe(r2.res));
}, function (r2) {
info.push('authorize(camera)=fail ' + describe(r2.err));
});
}).then(function () { return info; });
});
}
module.exports = { ensure: ensure, settings: settings, state: state, open: open, diagnose: diagnose, bare: bare, cameraSupported: cameraSupported };