// 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 不存在/同步抛错:当作空设置,走后续兜底 }); } // 平台是否具备内置相机能力(淘宝部分版本没有 / 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) { 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 };