Files
juzi/taobao/utils/permissions.js
T
xsl fed52930cc feat(miniapp): align UI with H5 and harden device debugging
五个页面按 h5/css 的 style.css → ui-20261009 → ui-20261012 覆盖顺序逐条重排,
换算规则固定为 calc(N * var(--u)) → 2N rpx,固定 px 的字号保持 px。

- app.acss:换回 H5 设计 token(方角小倒角按钮 10rpx、卡片 14rpx、.chev 折线箭头)
- splash/profile/history:底图与按钮文字图改用 H5 资源,隐私/政策弹窗按 H5 重排
- report:环形分用 conic-gradient、人脸裁切窗口叠加 face_maps、标记点百分比盒、
  侧脸皱纹 pending/unavailable 文案;用 Playwright 抓 H5 94 个选择器几何比对,
  整页高 2438.7 vs 2438.67
- camera:淘宝 <camera> 组件在真机整页崩溃(嵌了微信专有的 <cover-view>),
  改为默认系统相机 + `config.nativeCamera` 开关 + pages/camtest 冒烟页验证组件
- permissions:淘宝用带前缀的 scope.camera(裸名返回 5022 Scope:camera信息不存在),
  先试带前缀再退回裸名,拒绝后不再重复弹窗
- voice:非 HTTPS 资源导致的异常不再冒泡成整页崩溃;app.js 加开发版错误 toast
- 开发版入口:权限诊断(启动页/相机页)、相机组件测试
- 测试 20/20(新增权限与相机能力用例)
2026-11-01 23:30:38 +08:00

111 lines
5.3 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) {
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 };