五个页面按 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(新增权限与相机能力用例)
111 lines
5.3 KiB
JavaScript
111 lines
5.3 KiB
JavaScript
// 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 };
|