// QR Scanner Module - Self-contained QR scanning page // Adapted from existing camera implementation // Utility functions (copied from utils.js for self-contained module) var camera_rules = null; function get_system_info() { return wx.getSystemInfoSync(); } function get_phone_model() { var ret = get_system_info().model; console.log("phone model", ret); return ret; } function match_camera_rules(model, rules, default_zoom) { console.log(model, "apply zoom rules:", rules); var best_match = null; for (var rule of rules) { if (model.toLowerCase().startsWith(rule.model.toLowerCase())) { if (!best_match || rule.model.length > best_match.model.length) { best_match = rule; } } } if (best_match) { console.log("found best match", best_match); return best_match; } var ret = { zoom: default_zoom, web_view: false }; console.log("using default", ret); return ret; } function get_camera_rule(max_zoom, cb) { var default_zoom = 4; if (max_zoom && max_zoom >= 60) { /* * 2024.06.01: in some Huawei/Honor models, the scale is different, use 40 * in this case so we don't need to set up rules for each specific model */ console.log(`max zoom is ${max_zoom}, default zoom will be 40`); default_zoom = 40; } if (camera_rules) { let rule = match_camera_rules(get_phone_model(), camera_rules, default_zoom); cb(rule); } else { var url = 'https://themblem.com/api/v1/camera-rules/'; wx.request({ url, complete: (res) => { var rules = res.data; camera_rules = rules; let rule = match_camera_rules(get_phone_model(), rules, default_zoom); cb(rule); } }); } } function make_query(zoom, return_page) { var gd = getApp().globalData; var ret = "zoom=" + zoom; var ui = wx.getStorageSync('userinfo') || {}; ret += "&phonemodel=" + encodeURIComponent(get_phone_model()); ret += "&realip=" + (gd.real_ip || ""); ret += "&emblem_id=" + (ui.emblem_id || ""); ret += "&nick_name=" + encodeURIComponent(ui.nickName || ""); ret += "&tenant=" + (gd.tenant_id || ""); ret += "&tk=" + Date.now(); if (return_page) { ret += "&return_page=" + encodeURIComponent(return_page); } console.log("Web-view query:", ret); return ret; } Page({ /** * Page initial data */ data: { hint_text: '查找二维码', enable_debug: false, camera_flash: 'off', phone_model: 'unknown', zoom: -1, max_zoom: 1, use_worker: false, show_tip: false, show_modal: '', busy: true, should_check_auto_torch: true, done_checking_auto_torch: false, camera_sensitivity: 1, frame_uploaded: 0, frame_upload_time_cost: 0, qrarc_class: 'sm', qrmarkers_class: 'hidden', frame_upload_interval_ms: 2000, return_page: '', // Page to navigate to after successful scan server_url: 'https://themblem.com', // Default server URL debug_msgs: [], debug_image_data_url: '', rule_zoom: -1, camera_rule: null, use_web_view: false, emblem_camera_url: null }, /** * Lifecycle function--Called when page load */ onLoad(options) { console.log('QR Scanner module loaded', options); // Store return page from query parameters if (options.return_page) { this.setData({ return_page: options.return_page }); } // Initialize image data storage this.image_data_urls = []; // Handle debug mode options = options || {}; if (options.debug || options.scene == 'debug') { getApp().globalData.debug = true; } const enable_debug = getApp().globalData.debug || false; // Get system information this.initializeSystem(enable_debug); // Load camera rules this.loadCameraRules(); }, /** * Initialize system information and device detection */ initializeSystem(enable_debug) { const systemInfo = get_system_info(); const phone_model = systemInfo.model; const use_worker = phone_model.toLowerCase().includes('iphone'); this.setData({ enable_debug, phone_model, window_width: systemInfo.windowWidth, window_height: systemInfo.windowHeight, use_worker }); console.log(`Phone model: ${phone_model}, Use worker: ${use_worker}`); // Store phone model in global data getApp().globalData.phone_model = phone_model; // Initialize worker for iPhone devices if (use_worker) { this.initializeWorker(); } }, /** * Initialize WebAssembly worker for QR processing */ initializeWorker() { // TODO: Initialize worker - requires qrtool.wx.js and worker setup console.log('Worker initialization would happen here'); }, /** * Load camera rules from API */ loadCameraRules() { get_camera_rule(null, (rule) => { console.log('Camera rule loaded:', rule); const use_web_view = rule.web_view || false; let emblem_camera_url = null; // Set up web-view URL if needed if (use_web_view) { emblem_camera_url = "https://themblem.com/camera-5.0/?" + make_query(rule.zoom, this.data.return_page); this.addDebugMessage(`Using web-view camera: ${emblem_camera_url}`); } else { this.addDebugMessage('Using native WeChat camera'); } this.setData({ camera_rule: rule, zoom: rule.zoom, rule_zoom: rule.zoom, camera_sensitivity: rule.sensitivity || 1, use_web_view: use_web_view, emblem_camera_url: emblem_camera_url, busy: false }); // Add rule info to debug messages this.addDebugMessage(`Camera rule: zoom=${rule.zoom}, web_view=${rule.web_view}`); }); }, /** * Camera initialization callback */ setup_camera(e) { console.log('Camera setup', e); this.camera_context = wx.createCameraContext(); // Set up camera frame listener this.camera_context.onCameraFrame((frame) => { this.processFrame(frame); }); this.setData({ busy: false, hint_text: '查找二维码' }); }, /** * Process camera frame for QR detection */ processFrame(frame) { if (this.data.busy) return; // TODO: Implement QR detection logic console.log('Processing frame', frame.width, frame.height); }, /** * Handle successful QR verification */ onVerificationSuccess(qrCode) { console.log('QR verification successful:', qrCode); if (this.data.return_page) { wx.navigateTo({ url: `${this.data.return_page}?qr_code=${encodeURIComponent(qrCode)}`, success: () => { console.log(`Navigated to return page: ${this.data.return_page}`); }, fail: (err) => { console.error('Failed to navigate to return page:', err); this.restart_camera(); } }); } else { console.warn('No return page specified'); this.restart_camera(); } }, /** * Toggle torch/flash */ toggle_torch() { const newFlash = this.data.camera_flash === 'torch' ? 'off' : 'torch'; this.setData({ camera_flash: newFlash }); console.log('Torch toggled to:', newFlash); }, /** * Show scan guide */ show_scanguide() { this.setData({ show_modal: 'scanguide', show_tip: false }); }, /** * Show service modal */ show_service() { this.setData({ show_modal: 'service' }); }, /** * Close modal and restart camera */ restart_camera() { this.setData({ show_modal: '', hint_text: '查找二维码', busy: false }); }, /** * Close any modal */ close_modal() { this.setData({ show_modal: '' }); }, /** * Debug tap handler */ debug_tap() { const count = (this.debug_tap_count || 0) + 1; this.debug_tap_count = count; if (count >= 5) { this.setData({ enable_debug: !this.data.enable_debug }); this.debug_tap_count = 0; } // Clear count after 3 seconds setTimeout(() => { this.debug_tap_count = 0; }, 3000); }, /** * Generate hint text based on QR detection result */ make_hint_text(result) { if (result && result.qrcode && result.qrcode.length > 0) { const err = result.err || ''; if (err.includes('margin too small')) { return '对齐定位点'; } else if (err.includes('energy check failed') || err.includes('cannot detect angle')) { return '移近一点'; } return '对齐定位点'; } return '查找二维码'; }, /** * Add debug message to debug overlay */ addDebugMessage(message) { if (!this.data.enable_debug) return; const timestamp = new Date().toLocaleTimeString(); const debugMsg = `${timestamp}: ${message}`; this.setData({ debug_msgs: [...this.data.debug_msgs, debugMsg].slice(-10) // Keep last 10 messages }); }, /** * Log function for debugging */ log(...args) { console.log(...args); this.addDebugMessage(args.join(' ')); }, /** * Handle messages from web-view camera */ on_webview_message(e) { console.log('Web-view message received:', e); this.addDebugMessage(`Web-view message: ${JSON.stringify(e.detail)}`); // Handle QR code results from web-view if (e.detail && e.detail.data && e.detail.data.length > 0) { const messageData = e.detail.data[0]; if (messageData.qr_code) { this.onVerificationSuccess(messageData.qr_code); } else if (messageData.error) { this.addDebugMessage(`Web-view error: ${messageData.error}`); this.setData({ show_modal: 'verifyfailed', hint_text: '识别失败' }); } } } });