Preserve H5 flows while adding Taobao identity, native capture, reports and isolated local development. Fix canceled province selection and expired-session logout. Record passing local regressions, two pre-existing H5 assertion failures and pending platform validation.
187 lines
10 KiB
JavaScript
187 lines
10 KiB
JavaScript
var api = require('../../utils/api'), image = require('../../utils/image'), voice = require('../../utils/voice');
|
||
var permissions = require('../../utils/permissions');
|
||
var development = require('../../utils/development');
|
||
var STEPS = [
|
||
{ target: 'front', tip: '请单人正脸出镜', turn: '正脸居中', mask: 0 },
|
||
{ target: 'right', tip: '请右脸出镜', turn: '请向左转头', mask: 2 },
|
||
{ target: 'left', tip: '请左脸出镜', turn: '请向右转头', mask: 1 }
|
||
];
|
||
Page({
|
||
data: { stepIndex: 0, stepText: '1/3', stepTip: STEPS[0].tip, guides: ['正脸居中', '取下眼镜', '光线充足'],
|
||
maskStep: 0, devicePosition: 'front', camError: false, previewUrl: '', doneTip: '', analyzing: false,
|
||
compressWidth: 1, compressHeight: 1, cameraEnabled: false, permissionMessage: '拍摄面部照片用于肌肤检测,请开启相机权限。',
|
||
debugMode: false, debugComplete: false, debugPhotos: [] },
|
||
onLoad: function (query) {
|
||
this.alive = true; this.visible = true; this.photos = []; this.busy = false; this.seq = 0;
|
||
this.debugMode = !!(query && query.debug === 'camera' && development.cameraDebugEnabled());
|
||
this.setData({ debugMode: this.debugMode });
|
||
if (this.debugMode) return;
|
||
if (!api.user() || !getApp().globalData.profile) my.redirectTo({ url: '/pages/splash/splash' });
|
||
},
|
||
onReady: function () {
|
||
this.pageReady = true;
|
||
if (this.cameraAuthorized) this.mountCamera();
|
||
},
|
||
onShow: function () {
|
||
this.visible = true;
|
||
if (!this.data.previewUrl && !this.data.analyzing && !this.data.debugComplete) this.renderStep();
|
||
var that = this;
|
||
permissions.settings().then(function (current) {
|
||
if (!that.alive || !that.visible) return;
|
||
if (permissions.state(current, 'scope.camera') === true) { that.cameraAuthorized = true; that.mountCamera(); }
|
||
else if (permissions.state(current, 'scope.camera') === false) that.setData({ cameraEnabled: false, permissionMessage: '相机权限未开启,可前往授权设置或使用相册。' });
|
||
});
|
||
},
|
||
mountCamera: function () {
|
||
if (!this.alive || !this.visible || !this.pageReady || this.data.cameraEnabled || this.data.debugComplete) return;
|
||
var that = this;
|
||
this.setData({ cameraEnabled: true, camError: false }, function () {
|
||
if (!that.alive || !that.visible) return;
|
||
try {
|
||
that.cameraCtx = my.createCameraContext('skin-camera');
|
||
if (!that.cameraCtx || !that.cameraCtx.takePhoto) that.onCameraError();
|
||
} catch (_) { that.onCameraError(); }
|
||
});
|
||
},
|
||
onEnableCamera: function () {
|
||
if (this.authorizing) return;
|
||
var that = this; this.authorizing = true;
|
||
permissions.ensure('scope.camera').then(function () { that.cameraAuthorized = true; that.mountCamera(); }).catch(function (err) {
|
||
if (that.alive && that.visible) that.setData({ permissionMessage: err.message });
|
||
}).then(function () { that.authorizing = false; });
|
||
},
|
||
onOpenSettings: function () {
|
||
var that = this;
|
||
permissions.open().then(function (current) {
|
||
if (permissions.state(current, 'scope.camera') === true) { that.cameraAuthorized = true; that.mountCamera(); }
|
||
}).catch(function (err) { if (that.alive) my.showToast({ content: err.message, type: 'none' }); });
|
||
},
|
||
onHide: function () { this.pause(); },
|
||
onUnload: function () { this.pause(); this.alive = false; this.photos = []; this.debugResults = []; },
|
||
pause: function () {
|
||
this.visible = false; this.seq++; this.busy = false;
|
||
clearTimeout(this.nextTimer);
|
||
if (this.signal) this.signal.canceled = true;
|
||
voice.stop(true);
|
||
this.cameraCtx = null;
|
||
if (this.alive) this.setData({ cameraEnabled: false });
|
||
if (this.alive && this.data.analyzing) this.setData({ analyzing: false, doneTip: '' });
|
||
},
|
||
renderStep: function () {
|
||
var cfg = STEPS[this.data.stepIndex];
|
||
this.setData({ stepText: (this.data.stepIndex + 1) + '/3', stepTip: cfg.tip,
|
||
guides: [cfg.turn, '取下眼镜', '光线充足'], maskStep: cfg.mask, doneTip: '' });
|
||
if (!this.debugMode) voice.play(voice.VOICE.step[this.data.stepIndex]);
|
||
},
|
||
onCameraError: function () {
|
||
this.setData({ camError: true, cameraEnabled: false, permissionMessage: '相机暂不可用,请检查淘宝的系统相机权限,或使用系统相机/相册。' });
|
||
my.showToast({ content: '相机不可用,请使用系统相机或相册', type: 'none' });
|
||
},
|
||
onCameraStop: function () { if (this.alive && this.visible) this.onCameraError(); },
|
||
onShutter: function () {
|
||
if (this.busy || this.data.previewUrl || this.data.analyzing) return;
|
||
if (this.data.camError || !this.cameraCtx) return this.choose(['camera']);
|
||
var that = this, seq = this.seq;
|
||
this.busy = true;
|
||
this.cameraCtx.takePhoto({ quality: 'high', success: function (res) {
|
||
if (!that.alive || !that.visible || seq !== that.seq) return;
|
||
var file = res.tempImagePath || res.tempFilePath || res.apFilePath;
|
||
if (file) that.setData({ previewUrl: file });
|
||
else my.showToast({ content: '拍摄失败,请重试', type: 'none' });
|
||
}, fail: function () {
|
||
if (that.alive && that.visible) my.showToast({ content: '拍摄失败,请重试', type: 'none' });
|
||
}, complete: function () { if (seq === that.seq) that.busy = false; } });
|
||
},
|
||
choose: function (sourceType) {
|
||
if (this.busy || this.data.previewUrl || this.data.analyzing) return;
|
||
var that = this;
|
||
this.busy = true;
|
||
// chooseImage has its own album scope; native capture also needs camera.
|
||
permissions.ensure('scope.album').then(function () {
|
||
if (sourceType[0] === 'camera') return permissions.ensure('scope.camera');
|
||
}).then(function () {
|
||
if (!that.alive || !that.visible) { that.busy = false; return; }
|
||
my.chooseImage({ count: 1, sourceType: sourceType,
|
||
success: function (res) {
|
||
var paths = res.apFilePaths || res.tempFilePaths || [];
|
||
if (that.alive && paths[0]) that.setData({ previewUrl: paths[0] });
|
||
}, fail: function () { if (that.alive && that.visible) my.showToast({ content: '未选取照片,可重试或检查系统权限', type: 'none' }); },
|
||
complete: function () { that.busy = false; } });
|
||
}).catch(function (err) {
|
||
that.busy = false;
|
||
if (that.alive && that.visible) { that.setData({ permissionMessage: err.message }); my.showToast({ content: err.message, type: 'none' }); }
|
||
});
|
||
},
|
||
onSystemCamera: function () { this.choose(['camera']); },
|
||
onAlbum: function () { this.choose(['album']); },
|
||
onFlip: function () {
|
||
if (this.busy || this.data.previewUrl || this.data.analyzing) return;
|
||
this.setData({ devicePosition: this.data.devicePosition === 'front' ? 'back' : 'front' });
|
||
},
|
||
onRetake: function () { if (!this.busy) this.setData({ previewUrl: '' }); },
|
||
onConfirm: function () {
|
||
if (this.busy || !this.data.previewUrl || this.data.analyzing) return;
|
||
var that = this, step = this.data.stepIndex;
|
||
this.busy = true; this.photos[step] = this.data.previewUrl;
|
||
if (!this.debugMode) voice.play(voice.VOICE.shot, { cut: true });
|
||
if (step === 2) { this.setData({ previewUrl: '' }); this.submitPhotos(); return; }
|
||
this.setData({ stepIndex: step + 1, previewUrl: '', doneTip: '拍摄完成。下一张:' + STEPS[step + 1].tip + ',' + STEPS[step + 1].turn });
|
||
this.nextTimer = setTimeout(function () {
|
||
if (!that.alive || !that.visible) return;
|
||
that.busy = false; that.renderStep();
|
||
}, 2000);
|
||
},
|
||
submitPhotos: function () {
|
||
if (!this.photos[0] || !this.photos[1] || !this.photos[2]) return;
|
||
var that = this, seq = ++this.seq, dataUrls = [];
|
||
var debugResults = [];
|
||
this.signal = { canceled: false }; var signal = this.signal;
|
||
this.busy = true; this.setData({ analyzing: true, doneTip: '' });
|
||
if (!this.debugMode) voice.play(voice.VOICE.analyzing);
|
||
// One shared native canvas: draw the photos sequentially.
|
||
this.photos.reduce(function (chain, photo) {
|
||
return chain.then(function () {
|
||
if (signal.canceled) throw { canceled: true };
|
||
return image.compressToBase64(photo, that);
|
||
}).then(function (value) {
|
||
dataUrls.push(value);
|
||
if (that.debugMode) debugResults.push({ key: STEPS[debugResults.length].target,
|
||
title: STEPS[debugResults.length].tip.replace(/^请/, ''), photo: photo,
|
||
width: that.data.compressWidth, height: that.data.compressHeight,
|
||
kilobytes: Math.round(value.replace(/^data:[^,]+,/, '').length * 3 / 4 / 1024) });
|
||
});
|
||
}, Promise.resolve()).then(function () {
|
||
if (signal.canceled) throw { canceled: true };
|
||
if (that.debugMode) {
|
||
if (!that.alive || !that.visible || seq !== that.seq) return;
|
||
that.busy = false; that.cameraCtx = null;
|
||
dataUrls = []; // Do not keep base64, upload, create an account or touch report state.
|
||
that.setData({ analyzing: false, cameraEnabled: false, debugComplete: true, debugPhotos: debugResults });
|
||
return;
|
||
}
|
||
var ordered = [dataUrls[0], dataUrls[2], dataUrls[1]];
|
||
return api.analyze(ordered, getApp().globalData.profile, signal).then(function (payload) {
|
||
if (!that.alive || !that.visible || seq !== that.seq) return;
|
||
getApp().globalData.photos = ordered; getApp().globalData.payload = payload;
|
||
voice.stop(true); my.redirectTo({ url: '/pages/report/report' });
|
||
});
|
||
}).catch(function (err) {
|
||
if (err.canceled || !that.alive || !that.visible || seq !== that.seq) return;
|
||
that.busy = false; that.setData({ analyzing: false });
|
||
if (!that.debugMode) voice.play(voice.VOICE.fail, { cut: true });
|
||
my.showToast({ content: err.message || '照片处理失败,请重新拍摄', type: 'none', duration: 3000 });
|
||
});
|
||
},
|
||
onDebugRestart: function () {
|
||
if (!this.debugMode || !development.cameraDebugEnabled()) return;
|
||
this.photos = []; this.busy = false; this.seq++;
|
||
this.setData({ debugComplete: false, debugPhotos: [], stepIndex: 0, previewUrl: '', analyzing: false });
|
||
this.renderStep(); this.onEnableCamera();
|
||
},
|
||
onCancel: function () {
|
||
if (this.debugMode) { this.photos = []; this.setData({ debugPhotos: [], previewUrl: '' }); }
|
||
this.pause(); my.navigateBack();
|
||
},
|
||
noop: function () {}
|
||
});
|