feat: 完善淘宝小程序拍照、报告与登录流程

接入本地实时识别和自动三连拍,调整报告裁切、商品跳转、分享标题及居中页眉;完善游客会话、后台渠道隔离、部署脚本与回归测试。
This commit is contained in:
xsl
2026-11-04 15:07:51 +08:00
parent 381da60ae4
commit 774376fa16
147 changed files with 17420 additions and 597 deletions
+21
View File
@@ -0,0 +1,21 @@
page { background:#f1f4f7; color:#16212c; }
.native-toolbar { position:fixed; top:112px; left:0; right:0; z-index:1000; height:144rpx; padding:12rpx 24rpx; box-sizing:border-box; background:#fff; border-bottom:1px solid #ced9e2; }
.native-toolbar-auto { height:228rpx; }
.native-auto-entry { margin:0 0 12rpx; padding:0 12rpx; height:68rpx; line-height:64rpx; background:#126597; color:#fff; font-size:28rpx; font-weight:600; }
.native-toolbar-version { margin-bottom:10rpx; font-size:22rpx; line-height:30rpx; color:#466172; }
.native-toolbar-actions { display:flex; gap:14rpx; }
.native-toolbar-actions button { flex:1; margin:0; font-size:26rpx; line-height:68rpx; }
.native-toolbar-actions .native-copy { background:#126597; color:#fff; font-weight:600; }
.native-test { padding:172rpx 24rpx 64rpx; padding-top:calc(112px + 172rpx); }
.native-title { font-size:36rpx; font-weight:600; }
.native-note { margin:14rpx 0; font-size:24rpx; line-height:1.65; color:#596774; }
.native-preview { height:560rpx; margin:24rpx 0; background:#161d25; border-radius:12rpx; overflow:hidden; }
.native-camera { display:block; width:100%; height:100%; }
.native-empty { padding:120rpx 28rpx; color:#d6e0e8; text-align:center; }
.native-actions { display:flex; flex-wrap:wrap; gap:14rpx; }
.native-actions button { margin:0 0 12rpx; font-size:25rpx; }
.native-card { margin-top:20rpx; padding:22rpx; border-radius:12rpx; background:#fff; font-size:25rpx; line-height:1.75; word-break:break-all; }
.native-status { margin:12rpx 0; color:#165b8c; font-weight:600; }
.native-meta { margin-top:12rpx; font-family:monospace; font-size:23rpx; }
.native-diagnostic-text { display:block; white-space:pre-wrap; font-family:monospace; font-size:23rpx; line-height:1.65; user-select:text; -webkit-user-select:text; }
.native-photo { display:block; width:100%; height:540rpx; margin:18rpx 0; background:#e6eaee; }
+53
View File
@@ -0,0 +1,53 @@
<view class="native-toolbar {{showAutoTest ? 'native-toolbar-auto' : ''}}" style="top:{{toolbarTop}}px;">
<view class="native-toolbar-version">普通相机 · {{probeVersion}}</view>
<button a:if="{{showAutoTest}}" class="native-auto-entry" onTap="onOpenAutoCapture" disabled="{{photoBusy}}">开始自动三连拍测试</button>
<view class="native-toolbar-actions">
<button class="native-copy" size="mini" type="primary" onTap="onCopyDiagnostics">复制诊断信息</button>
<button size="mini" onTap="onGenerateDiagnostics">生成可选文字</button>
</view>
</view>
<view class="native-test" style="padding-top:calc({{toolbarTop}}px + {{showAutoTest ? 256 : 172}}rpx);">
<view class="native-title">普通 camera · 独立诊断</view>
<view class="native-note">仅验证前后镜头、真实帧和手动拍照;不是正式三连拍替代。照片仅本页临时预览,不登录、不上传、不做检测。</view>
<view a:if="{{showAutoTest}}" class="native-note">上方“开始自动三连拍测试”将进入正式取景界面的本地验收,验证识别、转头提示、自动拍摄和压缩,不上传照片、不创建检测任务。</view>
<view class="native-card native-diagnostic">
<view class="native-status">{{copyStatus || '点击顶部按钮生成当前脱敏诊断'}}</view>
<view class="native-note">生成或复制时会刷新实时计数。请求镜头不代表实际镜头;内容不含照片路径、像素、人脸坐标、账号或原始错误文字。</view>
<text a:if="{{diagnosticText}}" class="native-diagnostic-text" selectable="{{true}}">{{diagnosticText}}</text>
<view a:else class="native-note">当前尚未生成。即使没有剪贴板 API,也可点击“生成可选文字”后长按文字选择复制。</view>
</view>
<view class="native-preview">
<camera a:if="{{cameraEnabled}}" id="native-camera-{{cameraSession}}" data-camera-session="{{cameraSession}}" class="native-camera"
mode="normal" device-position="{{devicePosition}}" frame-size="medium" flash="off"
onReady="onNativeReady" onError="onNativeError" onStop="onNativeStop" />
<view a:else class="native-empty">相机已卸载,可点重新打开</view>
</view>
<view class="native-actions">
<button size="mini" onTap="onFlip" disabled="{{photoBusy}}">切换到{{devicePosition === 'front' ? '后置' : '前置'}}</button>
<button size="mini" onTap="onRetryCamera" disabled="{{photoBusy}}">重新打开并监听</button>
<button size="mini" onTap="onStopFrames">停止帧监听</button>
<button size="mini" onTap="onTakePhoto" disabled="{{photoBusy || !cameraEnabled}}">{{photoBusy ? '拍照处理中' : '手动拍一张'}}</button>
</view>
<view class="native-card">
<view>请求镜头:{{devicePosition}} · 会话 {{cameraSession}}</view>
<view>{{cameraStatus}}</view>
<view>onReady 事件:{{nativeReady ? '收到' : '未收到'}}</view>
<view>{{contextStatus}}</view>
<view class="native-status">{{frameStatus}}</view>
<view>start:{{startStatus}}</view><view>stop:{{stopStatus}}</view>
<view>原始帧 {{frameCount}} · 回调 {{frameCallbacks}} · 非标准回调 {{invalidFrameCount}}</view>
<view a:if="{{firstFrameMs}}">首帧等待:{{firstFrameMs}}</view>
<view a:if="{{frameInfo}}" class="native-meta">{{frameInfo}}</view>
<view class="native-note">帧计数只检查正数尺寸和非空字节,不假定 RGBA,也不代表已经得到人脸关键点。</view>
</view>
<view class="native-card">
<view>{{photoStatus}}</view>
<block a:if="{{photoPath}}"><view>拍照时请求镜头:{{photoPosition}}</view>
<image class="native-photo" src="{{photoPath}}" mode="aspectFit" onError="onPreviewError" />
<button size="mini" onTap="onClearPhoto">清除本页预览</button>
</block>
</view>
<view class="native-card">
<button size="mini" onTap="onOpenARProbe" disabled="{{photoBusy}}">AR 相机对照(可选)</button>
</view>
</view>
+269
View File
@@ -0,0 +1,269 @@
var Diagnostics = require('../../utils/ar-camera').diagnostics;
var development = require('../../utils/development');
var PROBE_VERSION = 'CAM-NATIVE-20261102-04';
var KNOWN_FORMATS = ['RGBA', 'RGBA8888', 'RGB', 'RGB888', 'RGBX', 'BGRA', 'BGRA8888', 'BGR', 'BGR888',
'ARGB', 'ARGB8888', 'ABGR', 'ABGR8888', 'NV21', 'NV12', 'YUV420', 'YUV420P', 'YUV420SP', 'I420', 'YV12', 'JPEG', 'JPG', 'PNG'];
function message(error, fallback) {
var value = error && (error.errorMessage || error.errMsg || error.message);
return typeof value === 'string' ? value.slice(0, 160) : fallback;
}
function positive(value) { return typeof value === 'number' && isFinite(value) && value > 0; }
function frameMeta(frame) {
frame = frame || {};
var data = frame.data, bytes = data && data.byteLength;
var meta = { width: positive(frame.width) ? frame.width : 0, height: positive(frame.height) ? frame.height : 0,
dataType: Object.prototype.toString.call(data), byteLength: positive(bytes) ? bytes : 0 };
if (typeof frame.timestamp === 'number' && isFinite(frame.timestamp)) meta.timestamp = frame.timestamp;
if (typeof frame.pixelFormat === 'string') meta.pixelFormat = frame.pixelFormat.slice(0, 40);
if (typeof frame.format === 'string') meta.format = frame.format.slice(0, 40);
return meta;
}
Page({
data: {
probeVersion: PROBE_VERSION, toolbarTop: 112, showAutoTest: false, diagnosticText: '', copyStatus: '',
cameraEnabled: false, cameraSession: 0, devicePosition: 'front', nativeReady: false,
cameraStatus: '正在打开普通 camera', contextStatus: '尚未创建上下文',
frameStatus: '尚未监听', startStatus: '尚未调用', stopStatus: '尚未调用',
frameCallbacks: 0, frameCount: 0, invalidFrameCount: 0, firstFrameMs: '', frameInfo: '',
photoBusy: false, photoStatus: '仅在点击拍照时生成临时照片', photoPath: '', photoPosition: ''
},
onLoad: function () {
this.alive = true; this.visible = false; this.pageReady = false;
this.generation = 0; this.photoOperation = null;
this.diagnosticSystem = Diagnostics.system(my); this.copySequence = 0;
this.setData({ toolbarTop: Diagnostics.toolbarTop(my), showAutoTest: development.nativeFaceProbeEnabled() });
this.contextCapabilities = { takePhoto: false, onCameraFrame: false, listenerStart: false, listenerStop: false };
this.listenerResults = { start: { state: 'not_called', code: 'unavailable' }, stop: { state: 'not_called', code: 'unavailable' } };
this.photoAttempted = false; this.photoResultSuccess = false; this.photoReturnedPath = false;
},
onReady: function () { this.pageReady = true; this.mountCamera(); },
onShow: function () {
this.visible = true;
if (this.photoOperation && this.photoOperation.timedOut) this.setData({ photoBusy: true, photoStatus: '拍照10秒未返回;等待原操作结束,或退出此诊断页后重试' });
this.mountCamera();
},
onHide: function () {
this.visible = false; this.disposeCamera();
this.setData({ cameraEnabled: false, photoBusy: !!this.photoOperation, cameraStatus: '页面已隐藏,相机已卸载' });
},
onUnload: function () {
this.visible = false; this.alive = false; this.disposeCamera();
if (this.photoOperation) clearTimeout(this.photoOperation.timer);
this.photoOperation = null; this.data.photoPath = '';
},
current: function (generation) { return this.alive && this.visible && this.data.cameraEnabled && this.generation === generation; },
diagnosticReport: function () {
var caps = this.contextCapabilities || {}, results = this.listenerResults || {}, meta = this.lastDiagnosticFrame;
var lines = [PROBE_VERSION].concat(this.diagnosticSystem || []);
lines.push('requestedPosition=' + (this.data.devicePosition === 'front' || this.data.devicePosition === 'back' ? this.data.devicePosition : 'unknown'));
lines.push('actualPosition=not_verified; requestedPosition does not identify the actual lens');
lines.push('context.takePhoto=' + !!caps.takePhoto + '; context.onCameraFrame=' + !!caps.onCameraFrame);
lines.push('listener.start=' + !!caps.listenerStart + '; listener.stop=' + !!caps.listenerStop);
lines.push('nativeReady=' + !!this.data.nativeReady + '; cameraMounted=' + !!this.data.cameraEnabled);
lines.push('frameCallbacks=' + (this.frameCallbacks || 0) + '; rawFrames=' + (this.frameCount || 0) + '; invalidCallbacks=' + (this.invalidFrameCount || 0));
if (meta) lines.push('frame.width=' + meta.width + '; frame.height=' + meta.height + '; frame.byteLength=' + meta.byteLength + '; frame.pixelFormat=' + meta.pixelFormat + '; frame.format=' + meta.format);
['start', 'stop'].forEach(function (key) {
var result = results[key] || { state: 'not_called', code: 'unavailable' };
lines.push('listener.' + key + '.state=' + result.state + '; listener.' + key + '.code=' + result.code);
});
lines.push('photo.attempted=' + !!this.photoAttempted + '; photo.pending=' + !!this.photoOperation + '; photo.success=' + !!this.photoResultSuccess + '; photo.returnedPath=' + !!this.photoReturnedPath + '; photo.previewPresent=' + !!this.data.photoPath);
return lines.join('\n');
},
onGenerateDiagnostics: function () {
if (!this.alive) return;
this.setData({ diagnosticText: this.diagnosticReport(), copyStatus: '脱敏诊断已生成,可长按下方文字选择复制。' });
},
onCopyDiagnostics: function () {
if (!this.alive) return;
var that = this, sequence = ++this.copySequence, report = this.diagnosticReport();
this.setData({ diagnosticText: report, copyStatus: '正在复制;也可长按下方脱敏文字选择复制。' });
function status(value) { if (that.alive && that.visible && sequence === that.copySequence) that.setData({ copyStatus: value }); }
try {
if (typeof my.setClipboard !== 'function') { status('当前宿主缺少剪贴板 API,请长按下方脱敏文字选择复制。'); return; }
my.setClipboard({ text: report, success: function () { status('普通相机诊断已复制,仅含版本和相机状态。'); },
fail: function () { status('剪贴板复制失败,请长按下方脱敏文字选择复制。'); } });
} catch (_) { status('剪贴板复制失败,请长按下方脱敏文字选择复制。'); }
},
currentEvent: function (event) {
if (!this.alive || !this.visible || !this.data.cameraEnabled) return false;
var target = event && event.currentTarget, session = target && target.dataset && target.dataset.cameraSession;
var match = target && typeof target.id === 'string' && /^native-camera-(\d+)$/.exec(target.id);
if (session === undefined && match) session = match[1];
return session === undefined || Number(session) === this.data.cameraSession;
},
mountCamera: function () {
if (!this.alive || !this.visible || !this.pageReady || this.data.cameraEnabled) return;
var that = this, generation = ++this.generation, session = this.data.cameraSession + 1;
this.frameCallbacks = 0; this.frameCount = 0; this.invalidFrameCount = 0; this.lastFramePaint = 0;
this.lastDiagnosticFrame = null;
this.contextCapabilities = { takePhoto: false, onCameraFrame: false, listenerStart: false, listenerStop: false };
this.listenerResults = { start: { state: 'not_called', code: 'unavailable' }, stop: { state: 'not_called', code: 'unavailable' } };
this.setData({ cameraEnabled: true, cameraSession: session, nativeReady: false, cameraStatus: '已请求' + (this.data.devicePosition === 'front' ? '前置' : '后置') + ',请观察真实取景',
contextStatus: '正在创建上下文', frameStatus: '尚未监听', startStatus: '尚未调用', stopStatus: '尚未调用',
frameCallbacks: 0, frameCount: 0, invalidFrameCount: 0, firstFrameMs: '', frameInfo: '', photoBusy: !!this.photoOperation }, function () {
if (!that.current(generation) || session !== that.data.cameraSession) return;
if (typeof my.createCameraContext !== 'function') { that.setData({ contextStatus: '缺少 my.createCameraContext', frameStatus: '无法监听' }); return; }
try {
that.cameraContext = my.createCameraContext('native-camera-' + session);
if (!that.cameraContext) throw new Error('未返回 CameraContext');
var methods = Diagnostics.methods(that.cameraContext);
that.contextCapabilities.takePhoto = methods.indexOf('takePhoto') >= 0;
that.contextCapabilities.onCameraFrame = methods.indexOf('onCameraFrame') >= 0;
that.setData({ contextStatus: 'takePhoto: ' + (typeof that.cameraContext.takePhoto === 'function') + ';onCameraFrame: ' + (typeof that.cameraContext.onCameraFrame === 'function') });
that.startFrames(generation);
} catch (error) { if (that.current(generation)) that.setData({ contextStatus: message(error, '创建相机上下文失败'), frameStatus: '无法监听' }); }
});
},
onNativeReady: function (event) { if (this.currentEvent(event)) this.setData({ nativeReady: true }); },
startFrames: function (generation) {
if (!this.current(generation)) return;
var that = this, context = this.cameraContext;
if (!context || typeof context.onCameraFrame !== 'function') { this.listenerResults.start = { state: 'missing_method', code: 'unavailable' }; this.setData({ frameStatus: '当前上下文缺少 onCameraFrame,不能取得实时帧' }); return; }
this.listenerResults.start = { state: 'pending', code: 'unavailable' };
this.framesActive = true; this.framesStartedAt = Date.now();
this.setData({ frameStatus: '等待真实帧;start 成功也不代表收到帧', startStatus: '正在调用' });
this.frameTimer3 = setTimeout(function () {
if (that.current(generation) && that.framesActive && !that.frameCount) that.setData({ frameStatus: '3秒未收到可用原始帧,继续等到10秒(回调 ' + that.frameCallbacks + ' 次)' });
}, 3000);
this.frameTimer10 = setTimeout(function () {
if (!that.current(generation) || !that.framesActive || that.frameCount) return;
that.setData({ frameStatus: '10秒仍未收到可用原始帧,本轮已停止;不能判定宿主支持', frameCallbacks: that.frameCallbacks, invalidFrameCount: that.invalidFrameCount });
that.stopFrames(false);
}, 10000);
try {
this.frameListener = context.onCameraFrame(function (frame) { that.receiveFrame(generation, frame); });
var methods = Diagnostics.methods(this.frameListener);
this.contextCapabilities.listenerStart = methods.indexOf('start') >= 0; this.contextCapabilities.listenerStop = methods.indexOf('stop') >= 0;
if (!this.frameListener || typeof this.frameListener.start !== 'function' || typeof this.frameListener.stop !== 'function') throw new Error('未返回具备 start/stop 的 CameraFrameListener');
this.frameListener.start({
success: function () { if (that.current(generation) && that.framesActive) { that.listenerResults.start = { state: 'success', code: 0 }; that.setData({ startStatus: '调用成功(需另看真实帧计数)' }); } },
fail: function (error) {
if (!that.current(generation) || !that.framesActive) return;
that.listenerResults.start = { state: 'failure', code: diagnosticCode(error) };
that.setData({ startStatus: message(error, '监听启动失败'), frameStatus: '监听启动失败' }); that.stopFrames(false);
}
});
} catch (error) {
if (!this.current(generation)) return;
this.listenerResults.start = { state: 'throw', code: diagnosticCode(error) };
this.setData({ startStatus: message(error, '监听接口调用失败'), frameStatus: '监听接口调用失败' }); this.stopFrames(false);
}
},
receiveFrame: function (generation, frame) {
if (!this.current(generation) || !this.framesActive) return;
this.frameCallbacks++;
var meta = frameMeta(frame), valid = meta.width > 0 && meta.height > 0 && meta.byteLength > 0;
this.lastDiagnosticFrame = { width: meta.width, height: meta.height, byteLength: meta.byteLength,
pixelFormat: diagnosticFormat(meta.pixelFormat), format: diagnosticFormat(meta.format) };
var firstValid = valid && !this.frameCount;
if (valid) {
this.frameCount++;
if (this.frameCount === 1) {
this.clearFrameTimers();
this.setData({ firstFrameMs: Math.max(0, Date.now() - this.framesStartedAt) + 'ms' });
}
} else this.invalidFrameCount++;
// Keep no frame/pixel buffer; publish scalar metadata at most ~3 times/s.
if (this.frameCallbacks === 1 || firstValid || Date.now() - this.lastFramePaint >= 350) {
this.lastFramePaint = Date.now();
this.setData({ frameCallbacks: this.frameCallbacks, frameCount: this.frameCount, invalidFrameCount: this.invalidFrameCount,
frameInfo: JSON.stringify(meta), frameStatus: this.frameCount ? '已收到原始帧;像素格式和人脸识别能力仍需另验' : '收到回调,但尺寸或原始字节缺失,未计为可用帧' });
}
},
clearFrameTimers: function () { clearTimeout(this.frameTimer3); clearTimeout(this.frameTimer10); this.frameTimer3 = null; this.frameTimer10 = null; },
stopFrames: function (showStatus) {
this.framesActive = false; this.clearFrameTimers();
var listener = this.frameListener; this.frameListener = null;
var that = this, generation = this.generation;
if (showStatus && this.current(generation)) this.setData({ frameStatus: '已主动停止监听', frameCallbacks: this.frameCallbacks, frameCount: this.frameCount, invalidFrameCount: this.invalidFrameCount });
if (!listener || typeof listener.stop !== 'function') return;
this.listenerResults.stop = { state: 'pending', code: 'unavailable' };
if (this.current(generation)) this.setData({ stopStatus: '正在调用' });
try {
listener.stop({ success: function () { if (that.current(generation)) { that.listenerResults.stop = { state: 'success', code: 0 }; that.setData({ stopStatus: '调用成功' }); } },
fail: function (error) { if (that.current(generation)) { that.listenerResults.stop = { state: 'failure', code: diagnosticCode(error) }; that.setData({ stopStatus: message(error, '停止监听失败') }); } } });
} catch (error) { if (this.current(generation)) { this.listenerResults.stop = { state: 'throw', code: diagnosticCode(error) }; this.setData({ stopStatus: message(error, '停止监听异常') }); } }
},
onStopFrames: function () { this.stopFrames(true); },
disposeCamera: function () {
this.generation++; this.stopFrames(false); this.cameraContext = null;
},
remount: function (position) {
if (!this.alive || !this.visible) return;
if (this.photoOperation) { this.setData({ photoStatus: '上次拍照尚未结束,请等待;若持续无响应,请退出此诊断页再进入' }); return; }
this.disposeCamera();
var that = this;
this.setData({ cameraEnabled: false, devicePosition: position }, function () { that.mountCamera(); });
},
onFlip: function () { this.remount(this.data.devicePosition === 'front' ? 'back' : 'front'); },
onRetryCamera: function () { this.remount(this.data.devicePosition); },
onNativeError: function (event) {
if (!this.currentEvent(event)) return;
this.disposeCamera();
this.setData({ cameraEnabled: false, cameraStatus: message(event && event.detail, '相机组件报错,请检查系统相机权限'), frameStatus: '相机异常,监听已停止' });
},
onNativeStop: function (event) {
if (!this.currentEvent(event)) return;
this.disposeCamera(); this.setData({ cameraEnabled: false, cameraStatus: '相机组件已异常停止,可点重新打开', frameStatus: '监听已停止' });
},
onTakePhoto: function () {
if (!this.alive || !this.visible || !this.data.cameraEnabled || this.photoOperation) return;
var context = this.cameraContext, that = this;
if (!context || typeof context.takePhoto !== 'function') { this.setData({ photoStatus: '当前上下文缺少 takePhoto' }); return; }
var operation = { generation: this.generation, position: this.data.devicePosition, done: false };
this.photoAttempted = true; this.photoResultSuccess = false; this.photoReturnedPath = false;
this.photoOperation = operation;
this.setData({ photoBusy: true, photoStatus: '正在拍照;仅在本页预览' });
operation.timer = setTimeout(function () {
// A timeout cannot cancel native takePhoto. Keep the lock until its callback
// or page destruction, so a late native export cannot overlap a retry.
if (operation.done || that.photoOperation !== operation) return;
operation.timedOut = true;
if (that.alive && that.visible) that.setData({ photoStatus: '拍照10秒未返回;等待原操作结束,或退出此诊断页后重试' });
}, 10000);
try {
context.takePhoto({ quality: 'high', success: function (result) { that.finishPhoto(operation, 'success', result); },
fail: function (error) { that.finishPhoto(operation, 'fail', error); },
complete: function () { that.finishPhoto(operation, 'complete'); } });
} catch (error) { this.finishPhoto(operation, 'fail', error); }
},
finishPhoto: function (operation, kind, result) {
if (operation.done || this.photoOperation !== operation) return;
operation.done = true; clearTimeout(operation.timer); this.photoOperation = null;
if (!this.alive || !this.visible) return;
this.setData({ photoBusy: false });
if (!this.current(operation.generation)) return;
var file = result && (result.tempImagePath || result.tempFilePath || result.apFilePath);
this.photoReturnedPath = typeof file === 'string' && !!file;
this.photoResultSuccess = kind === 'success' && this.photoReturnedPath;
if (kind === 'success' && typeof file === 'string' && file) this.setData({ photoPath: file, photoPosition: operation.position, photoStatus: '已取得临时照片,请观察照片是否来自所请求镜头' });
else this.setData({ photoStatus: message(result, kind === 'success' ? '拍照成功回调未返回可预览的临时路径' : '拍照未成功返回照片') });
},
onOpenAutoCapture: function () {
if (!development.nativeFaceProbeEnabled() || this.photoOperation || this.data.photoBusy) return;
my.redirectTo({ url: '/pages/camera/camera?debug=local-face' });
},
onOpenARProbe: function () {
if (this.photoOperation || this.data.photoBusy) return;
my.redirectTo({ url: '/pages/camtest/camtest' });
},
onClearPhoto: function () { this.setData({ photoPath: '', photoPosition: '', photoStatus: '本页照片预览已清除' }); },
onPreviewError: function () { if (this.alive && this.visible) this.setData({ photoStatus: '已收到拍照路径,但本页图片预览加载失败' }); }
});
function diagnosticFormat(value) {
return typeof value !== 'string' ? 'not_reported' : (KNOWN_FORMATS.indexOf(value.toUpperCase()) >= 0 ? value.toUpperCase() : 'unrecognized');
}
function diagnosticCode(error) {
if (!error || typeof error !== 'object') return 'unavailable';
var keys = ['error', 'errorCode', 'code'];
for (var index = 0; index < keys.length; index++) {
var descriptor;
try { descriptor = Object.getOwnPropertyDescriptor(error, keys[index]); } catch (_) { continue; }
if (!descriptor || !Object.prototype.hasOwnProperty.call(descriptor, 'value')) continue;
var value = descriptor.value;
if (typeof value === 'number' && isFinite(value) && Math.floor(value) === value && Math.abs(value) <= 999999999) return value;
if (typeof value === 'string' && /^-?\d{1,9}$/.test(value)) return Number(value);
}
return 'unavailable';
}
+5
View File
@@ -0,0 +1,5 @@
{
"defaultTitle": "普通相机诊断",
"pullRefresh": false,
"gestureBack": "NO"
}