simplify camera-5.1
This commit is contained in:
parent
5093b2b40b
commit
b87693a717
@ -36,7 +36,8 @@ const {
|
||||
|
||||
// Import upload functionality for verification (self-contained)
|
||||
const {
|
||||
upload_image_data_urls
|
||||
upload_image_data_urls,
|
||||
check_auto_torch
|
||||
} = require('./upload.js');
|
||||
|
||||
// Import precheck utilities for image processing
|
||||
@ -65,8 +66,6 @@ Page({
|
||||
show_tip: false,
|
||||
show_modal: '',
|
||||
busy: true,
|
||||
should_check_auto_torch: true,
|
||||
done_checking_auto_torch: false,
|
||||
camera_sensitivity: 1,
|
||||
qrarc_class: 'sm',
|
||||
qrmarkers_class: 'hidden',
|
||||
@ -92,9 +91,8 @@ Page({
|
||||
use_web_view: false,
|
||||
use_worker: false,
|
||||
emblem_camera_url: null,
|
||||
first_qr_found: false, // Track if first QR has been detected to trigger zoom-in
|
||||
// State machine: loading_rules -> loading_qrtool -> init_camera -> scanning -> verifying -> result
|
||||
app_state: 'loading_rules', // 'loading_rules', 'loading_qrtool', 'init_camera', 'scanning', 'webview_scanning', 'verifying', 'result'
|
||||
// State machine: loading_rules -> loading_qrtool -> init_camera -> qr_detecting -> check_auto_torch -> final_scanning -> verifying -> result
|
||||
app_state: 'loading_rules', // 'loading_rules', 'loading_qrtool', 'init_camera', 'qr_detecting', 'webview_scanning', 'check_auto_torch', 'final_scanning', 'verifying', 'result'
|
||||
scan_mode: 'unknown', // 'camera', 'webview'
|
||||
no_web_view: false, // Override web-view rule, force native camera
|
||||
worker_processing: false, // Track if worker is currently processing a frame
|
||||
@ -104,26 +102,15 @@ Page({
|
||||
onLoad(options) {
|
||||
console.log('QR Scanner module loaded', options);
|
||||
|
||||
// Store page options for use in onShow
|
||||
this.pageOptions = options || {};
|
||||
|
||||
const no_web_view = options.no_web_view === '1' || options.no_web_view === 'true';
|
||||
|
||||
this.setData({
|
||||
return_page: options.return_page || '/pages/test_result_page/test_result_page',
|
||||
no_web_view: no_web_view
|
||||
});
|
||||
|
||||
// Initialize image collection for verification
|
||||
this.image_data_urls = [];
|
||||
|
||||
options = options || {};
|
||||
const enable_debug = options.debug || options.scene == 'debug' || false;
|
||||
|
||||
// Step 1: Initialize system and get phone model
|
||||
this.initializeSystem(enable_debug);
|
||||
this.fetchRealIP();
|
||||
this.fetchTenantID();
|
||||
|
||||
// Step 2: Load camera rules based on phone model
|
||||
this.loadCameraRules();
|
||||
},
|
||||
|
||||
fetchRealIP() {
|
||||
@ -169,10 +156,6 @@ Page({
|
||||
|
||||
console.log(`Phone model: ${phone_model}, Use worker: ${use_worker}`);
|
||||
|
||||
// Set up worker if needed (like camera.js)
|
||||
if (use_worker) {
|
||||
this.setupWorker();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -180,11 +163,11 @@ Page({
|
||||
*/
|
||||
setupWorker() {
|
||||
// Create worker with local worker file
|
||||
this.worker = wx.createWorker('/pages/emblemscanner/worker/index.js', {
|
||||
var worker = wx.createWorker('/pages/emblemscanner/worker/index.js', {
|
||||
useExperimentalWorker: true,
|
||||
});
|
||||
|
||||
this.worker.onMessage((msg) => {
|
||||
worker.onMessage((msg) => {
|
||||
console.log('Worker message:', msg.type);
|
||||
|
||||
if (msg.type === "result") {
|
||||
@ -216,14 +199,21 @@ Page({
|
||||
if (result.qrcode && is_emblem_qr_pattern(result.qrcode)) {
|
||||
this.addDebugMessage(`Worker QR detected: ${result.qrcode}`);
|
||||
|
||||
// Check auto-torch on first valid QR detection (only when in QR detecting state)
|
||||
if (this.data.app_state === 'qr_detecting') {
|
||||
this.startCheckingAutoTorch(result.qrcode);
|
||||
|
||||
// Trigger zoom-in if function is set up
|
||||
if (this.on_first_qr_found && !this.data.first_qr_found) {
|
||||
if (this.on_first_qr_found) {
|
||||
this.on_first_qr_found();
|
||||
}
|
||||
|
||||
if (result.ok) {
|
||||
return; // Exit early, auto-torch check will return to final_scanning
|
||||
}
|
||||
|
||||
if (this.data.app_state === 'final_scanning' && result.ok) {
|
||||
// Request worker to submit image data
|
||||
this.worker.postMessage({ type: "ready_to_submit" });
|
||||
this.get_worker().postMessage({ type: "ready_to_submit" });
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,6 +250,7 @@ Page({
|
||||
});
|
||||
|
||||
this.addDebugMessage('Worker set up for iPhone processing');
|
||||
return worker;
|
||||
},
|
||||
|
||||
loadCameraRules(max_zoom = null) {
|
||||
@ -328,10 +319,8 @@ Page({
|
||||
onCameraError(e) {
|
||||
console.error('Camera error', e);
|
||||
this.addDebugMessage(`Camera error: ${JSON.stringify(e.detail)}`);
|
||||
this.setData({
|
||||
show_modal: 'verifyfailed',
|
||||
hint_text: '相机初始化失败'
|
||||
});
|
||||
// Redirect with failure instead of showing modal
|
||||
this.goToResult(null, null, false);
|
||||
},
|
||||
|
||||
toggle_torch() {
|
||||
@ -342,6 +331,7 @@ Page({
|
||||
console.log('Torch toggled to:', newFlash);
|
||||
},
|
||||
|
||||
|
||||
show_scanguide() {
|
||||
this.setData({
|
||||
show_modal: 'scanguide',
|
||||
@ -405,7 +395,6 @@ Page({
|
||||
|
||||
// Set up zoom-in behavior when QR is found
|
||||
this.on_first_qr_found = () => {
|
||||
this.setData({ first_qr_found: true });
|
||||
this.addDebugMessage(`First QR found, zoom to ${zoom}x`);
|
||||
this.camera_context.setZoom({ zoom: zoom });
|
||||
this.setData({
|
||||
@ -441,11 +430,11 @@ Page({
|
||||
},
|
||||
|
||||
/**
|
||||
* State: Init Camera -> Scanning (Camera)
|
||||
* State: Init Camera -> QR Detecting (Camera)
|
||||
* Also starts frame processing since all conditions are implied to be ready
|
||||
*/
|
||||
startCameraScanning() {
|
||||
this.transitionToState('scanning', 'camera');
|
||||
this.transitionToState('qr_detecting', 'camera');
|
||||
this.setData({
|
||||
hint_text: '查找二维码',
|
||||
busy: false
|
||||
@ -461,21 +450,10 @@ Page({
|
||||
});
|
||||
|
||||
// Start the listener with worker if using worker mode
|
||||
if (this.data.use_worker) {
|
||||
if (!this.worker) {
|
||||
this.addDebugMessage('Worker not found, cannot start listener');
|
||||
this.setData({
|
||||
show_modal: 'verifyfailed',
|
||||
hint_text: '工作线程初始化失败'
|
||||
});
|
||||
return;
|
||||
}
|
||||
var worker = this.get_worker();
|
||||
this.listener.start({
|
||||
worker: this.worker
|
||||
worker,
|
||||
});
|
||||
} else {
|
||||
this.listener.start();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -483,8 +461,8 @@ Page({
|
||||
* Camera frame callback - receives live camera frames
|
||||
*/
|
||||
onCameraFrame(frame) {
|
||||
// Only process frames when in camera scanning state
|
||||
if (this.data.app_state !== 'scanning') {
|
||||
// Only process frames when in QR detecting or final scanning states
|
||||
if (this.data.app_state !== 'qr_detecting' && this.data.app_state !== 'final_scanning') {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -499,7 +477,7 @@ Page({
|
||||
this.lastFrameTime = now;
|
||||
|
||||
// Use worker for iPhone, direct processing for other devices
|
||||
if (this.data.use_worker && this.worker) {
|
||||
if (this.data.use_worker) {
|
||||
// Skip if worker is already processing a frame
|
||||
if (this.data.worker_processing) {
|
||||
this.setData({
|
||||
@ -524,7 +502,7 @@ Page({
|
||||
// Set processing flag before sending message
|
||||
this.setData({ worker_processing: true });
|
||||
|
||||
this.worker.postMessage({
|
||||
this.get_worker().postMessage({
|
||||
type: 'frame',
|
||||
width: frame.width,
|
||||
height: frame.height,
|
||||
@ -624,12 +602,19 @@ Page({
|
||||
if (result.qrcode && is_emblem_qr_pattern(result.qrcode)) {
|
||||
this.addDebugMessage(`QR detected: ${result.qrcode} ok: ${result.ok}: err ${result.err}`);
|
||||
|
||||
// Check auto-torch on first valid QR detection (only when in QR detecting state)
|
||||
if (this.data.app_state === 'qr_detecting') {
|
||||
this.startCheckingAutoTorch(result.qrcode);
|
||||
|
||||
// Trigger zoom-in if function is set up
|
||||
if (!this.data.first_qr_found && this.on_first_qr_found) {
|
||||
if (this.on_first_qr_found) {
|
||||
this.on_first_qr_found();
|
||||
}
|
||||
|
||||
if (result.ok) {
|
||||
return; // Exit early, auto-torch check will return to final_scanning state
|
||||
}
|
||||
|
||||
if (this.data.app_state === 'final_scanning' && result.ok) {
|
||||
this.onQRCodeDetected(result.qrcode, frame, copiedFrameData); // Pass the copied frame data
|
||||
}
|
||||
} else {
|
||||
@ -699,22 +684,28 @@ Page({
|
||||
getApp().globalData.verify_resp = resp;
|
||||
|
||||
if (resp.serial_code) {
|
||||
// Let verification animation run for a bit, then show result
|
||||
// Let verification animation run for a bit, then redirect with success
|
||||
const delay = 3000 - (Date.now() - begin);
|
||||
setTimeout(() => {
|
||||
this.goToResult(qrCode, resp.serial_code);
|
||||
this.goToResult(qrCode, resp.serial_code, true);
|
||||
}, delay > 0 ? delay : 0);
|
||||
} else {
|
||||
this.showVerifyFailed();
|
||||
// Redirect with failure instead of showing modal
|
||||
const delay = 3000 - (Date.now() - begin);
|
||||
setTimeout(() => {
|
||||
this.goToResult(qrCode, null, false);
|
||||
}, delay > 0 ? delay : 0);
|
||||
}
|
||||
} else {
|
||||
this.showVerifyFailed();
|
||||
// Redirect with failure instead of showing modal
|
||||
this.goToResult(qrCode, null, false);
|
||||
}
|
||||
};
|
||||
|
||||
const fail = (e) => {
|
||||
this.addDebugMessage(`Upload failed: ${JSON.stringify(e)}`);
|
||||
this.showVerifyFailed();
|
||||
// Redirect with failure instead of showing modal
|
||||
this.goToResult(qrCode, null, false);
|
||||
};
|
||||
|
||||
// Store global data like camera.js
|
||||
@ -725,15 +716,6 @@ Page({
|
||||
upload_image_data_urls(dataUrls, success, fail, "emblemscanner verification");
|
||||
},
|
||||
|
||||
/**
|
||||
* Show verification failed modal
|
||||
*/
|
||||
showVerifyFailed() {
|
||||
this.setData({
|
||||
show_modal: 'verifyfailed',
|
||||
hint_text: '验证失败'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* State: Loading Rules -> Webview Scanning
|
||||
@ -747,7 +729,40 @@ Page({
|
||||
},
|
||||
|
||||
/**
|
||||
* State: Scanning -> Verifying (only for camera mode)
|
||||
* State: QR Detecting -> Check Auto-torch
|
||||
*/
|
||||
startCheckingAutoTorch(qrcode) {
|
||||
this.transitionToState('check_auto_torch');
|
||||
this.setData({
|
||||
hint_text: '检查补光设置...'
|
||||
});
|
||||
|
||||
check_auto_torch(qrcode, (auto_torch, camera_sensitivity) => {
|
||||
this.addDebugMessage(`Auto-torch check: ${auto_torch}, sensitivity: ${camera_sensitivity}`);
|
||||
|
||||
if (auto_torch) {
|
||||
this.setData({
|
||||
camera_flash: 'torch'
|
||||
});
|
||||
this.addDebugMessage('Auto-torch enabled');
|
||||
}
|
||||
|
||||
// Update camera sensitivity and continue scanning
|
||||
this.setData({
|
||||
camera_sensitivity: camera_sensitivity || this.data.camera_sensitivity
|
||||
});
|
||||
|
||||
// Transition to final_scanning state (ready to collect frames for verification)
|
||||
this.transitionToState('final_scanning');
|
||||
this.setData({
|
||||
hint_text: '查找二维码'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* State: Final Scanning -> Verifying (show spinner while waiting for backend)
|
||||
*/
|
||||
startVerifying() {
|
||||
this.transitionToState('verifying');
|
||||
@ -768,12 +783,21 @@ Page({
|
||||
/**
|
||||
* State: Any -> Result (jump to return page)
|
||||
*/
|
||||
goToResult(qrCode, serialCode) {
|
||||
goToResult(qrCode, serialCode, success = true) {
|
||||
this.transitionToState('result');
|
||||
|
||||
// Pass both qr_code and serial_code parameters like camera-5.1 does
|
||||
// serialCode comes from server verification response
|
||||
const url = `${this.data.return_page}?qr_code=${encodeURIComponent(qrCode)}&serial_code=${encodeURIComponent(serialCode)}`;
|
||||
// Always redirect to return_page, pass ok=1 for success, ok=0 for failure
|
||||
// Only pass qr_code and serial_code if ok=1 (success)
|
||||
let url;
|
||||
if (success && qrCode) {
|
||||
if (serialCode) {
|
||||
url = `${this.data.return_page}?ok=1&qr_code=${encodeURIComponent(qrCode)}&serial_code=${encodeURIComponent(serialCode)}`;
|
||||
} else {
|
||||
url = `${this.data.return_page}?ok=1&qr_code=${encodeURIComponent(qrCode)}`;
|
||||
}
|
||||
} else {
|
||||
url = `${this.data.return_page}?ok=0`;
|
||||
}
|
||||
|
||||
wx.redirectTo({
|
||||
url: url,
|
||||
@ -806,8 +830,7 @@ Page({
|
||||
ok_frames: 0,
|
||||
total_processing_time: 0,
|
||||
avg_processing_time_ms: 0,
|
||||
last_frame_time_ms: 0,
|
||||
first_qr_found: false
|
||||
last_frame_time_ms: 0
|
||||
});
|
||||
|
||||
// Reset frame timing
|
||||
@ -865,12 +888,11 @@ Page({
|
||||
const messageData = e.detail.data[0];
|
||||
if (messageData.qr_code) {
|
||||
// Web-view results go directly to result (no verification step)
|
||||
this.goToResult(messageData.qr_code);
|
||||
this.goToResult(messageData.qr_code, messageData.serial_code, true);
|
||||
} else if (messageData.error) {
|
||||
this.addDebugMessage(`Web-view error: ${messageData.error}`);
|
||||
this.setData({
|
||||
hint_text: '识别失败'
|
||||
});
|
||||
// Redirect with failure instead of showing hint
|
||||
this.goToResult(null, null, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -882,10 +904,49 @@ Page({
|
||||
return is_emblem_qr_pattern(qrCode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Lifecycle - page show
|
||||
*/
|
||||
onShow() {
|
||||
console.log('EmblemScanner page shown - initializing');
|
||||
|
||||
// Reset state machine to initial state
|
||||
this.setData({
|
||||
app_state: 'loading_rules',
|
||||
scan_mode: 'unknown',
|
||||
busy: true,
|
||||
hint_text: '查找二维码',
|
||||
show_modal: '',
|
||||
worker_processing: false,
|
||||
verifying_stage: 0,
|
||||
// Reset frame processing statistics
|
||||
frames_processed: 0,
|
||||
frames_skipped: 0,
|
||||
ok_frames: 0,
|
||||
total_processing_time: 0,
|
||||
avg_processing_time_ms: 0,
|
||||
last_frame_time_ms: 0
|
||||
});
|
||||
|
||||
// Initialize image collection for verification
|
||||
this.image_data_urls = [];
|
||||
|
||||
const enable_debug = this.pageOptions.debug || this.pageOptions.scene == 'debug' || false;
|
||||
|
||||
// Step 1: Initialize system and get phone model
|
||||
this.initializeSystem(enable_debug);
|
||||
this.fetchRealIP();
|
||||
this.fetchTenantID();
|
||||
|
||||
// Step 2: Load camera rules based on phone model
|
||||
this.loadCameraRules();
|
||||
},
|
||||
|
||||
/**
|
||||
* Lifecycle - page hide
|
||||
*/
|
||||
onHide() {
|
||||
console.log('EmblemScanner page hidden - cleaning up');
|
||||
this.cleanupListener();
|
||||
},
|
||||
|
||||
@ -905,6 +966,14 @@ Page({
|
||||
this.listener = null;
|
||||
this.addDebugMessage('Camera frame listener stopped');
|
||||
}
|
||||
},
|
||||
|
||||
get_worker() {
|
||||
var gd = getApp().globalData;
|
||||
if (!gd.emblemscanner_worker) {
|
||||
gd.emblemscanner_worker = this.setupWorker();
|
||||
}
|
||||
return gd.emblemscanner_worker;
|
||||
}
|
||||
|
||||
});
|
||||
@ -30,8 +30,8 @@
|
||||
</camera>
|
||||
</block>
|
||||
|
||||
<!-- STATE: SCANNING -->
|
||||
<block wx:if="{{ app_state == 'scanning' }}">
|
||||
<!-- STATE: QR_DETECTING, CHECK_AUTO_TORCH, or FINAL_SCANNING -->
|
||||
<block wx:if="{{ app_state == 'qr_detecting' || app_state == 'check_auto_torch' || app_state == 'final_scanning' }}">
|
||||
<!-- QR targeting arcs overlay -->
|
||||
<view class="qrarc {{ qrarc_class }}">
|
||||
<image class="topleft arc" src="./assets/arc.png"></image>
|
||||
@ -145,8 +145,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Bottom action controls (for camera init and scanning) -->
|
||||
<view wx:if="{{ app_state == 'init_camera' || app_state == 'scanning' }}" class="bottomfixed">
|
||||
<!-- Bottom action controls (for camera init and QR detection/scanning) -->
|
||||
<view wx:if="{{ app_state == 'init_camera' || app_state == 'qr_detecting' || app_state == 'check_auto_torch' || app_state == 'final_scanning' }}" class="bottomfixed">
|
||||
<view class="actions">
|
||||
<view class="half {{ show_tip ? 'brighter' : '' }}" bindtap="show_scanguide">
|
||||
<view class="icon">
|
||||
@ -183,7 +183,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Verification Spinner - copied from verifyspin component -->
|
||||
<!-- Verification Spinner - shows during backend submission -->
|
||||
<view wx:if="{{ show_modal == 'verifying' }}" class="verification-container">
|
||||
<view class="verification-spinner {{ verifying_stage == 0 ? 'spin-and-shrink' : 'spin-only' }}">
|
||||
<image class="square" src="./assets/spinner.png"></image>
|
||||
@ -193,15 +193,6 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Verification Failed -->
|
||||
<view wx:if="{{ show_modal == 'verifyfailed' }}" class="modal">
|
||||
<view class="modal-content">
|
||||
<text>验证失败</text>
|
||||
<button bindtap="restart_camera">重新扫描</button>
|
||||
<button bindtap="show_service">联系客服</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Scan Guide -->
|
||||
<view wx:if="{{ show_modal == 'scanguide' }}" class="modal">
|
||||
<view class="modal-content">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user