diff --git a/scanner/QR_SCANNER_MODULE_PROGRESS.md b/scanner/QR_SCANNER_MODULE_PROGRESS.md
new file mode 100644
index 0000000..e5bb1e0
--- /dev/null
+++ b/scanner/QR_SCANNER_MODULE_PROGRESS.md
@@ -0,0 +1,129 @@
+# QR Scanner Module Development Progress
+
+## Project Goal
+
+Create a self-contained, portable QR scanning page module that can be easily integrated into other WeChat Mini Programs. The module should include all scanning functionality in a single directory and accept an entry query parameter to redirect to different pages upon successful scan and verification.
+
+## Feature Checklist - Extracted from Existing Code
+
+### ✅ Core QR Processing
+- [ ] **WebAssembly QR Tool Integration** - `qrtool.wx.js` library for QR code processing (TODO: Add library)
+- [ ] **Dual Processing Modes**:
+ - [ ] Worker-based processing (iPhone devices) - `worker/index.js` (TODO: Implement)
+ - [ ] Synchronous processing (Android devices) - `precheck.js` (TODO: Implement)
+- [ ] **QR Pattern Validation** - Emblem-specific QR code pattern matching (TODO: Add validation)
+- [ ] **Frame Pre-checking** - Validate QR frames before full processing (TODO: Implement)
+- [ ] **Angle Detection** - QR code angle detection and correction (TODO: Implement)
+
+### ✅ Camera System
+- [x] **Native Camera Interface** - WeChat camera component integration ✅
+- [ ] **Web-view Camera Fallback** - `pages/camwebview/` for problematic devices (TODO: Add fallback)
+- [ ] **Device-specific Camera Rules** - API-driven zoom and camera settings per phone model (TODO: Implement API integration)
+- [ ] **Dynamic Zoom Control** - Initial zoom + QR-found zoom adjustment (TODO: Implement)
+- [x] **Torch/Flash Control** - Manual and automatic torch management ✅
+- [ ] **Auto-torch Detection** - API-based torch recommendation system (TODO: Implement API integration)
+
+### ✅ UI Components & Visual Feedback
+- [x] **QR Targeting Overlay** - Animated corner arcs for QR positioning ✅
+- [x] **Visual State Indicators** - Progress states (searching, found, verifying) ✅
+- [x] **Hint Text System** - Dynamic user guidance messages ✅
+- [x] **Debug Overlay** - Development/diagnostic information display ✅
+- [x] **Modal System**: ✅
+ - [x] Verification spinner (`verifyspin`) ✅
+ - [x] Failed verification (`verifyfailed`) ✅
+ - [x] Scan guide tutorial (`scanguide`) ✅
+ - [x] Service modal (`servicemodal`) ✅
+ - [x] Tooltip component (`tooltip`) ✅
+
+### ✅ API Integration & Data Flow
+- [ ] **Image Upload System** - Multi-frame submission to backend
+- [ ] **Verification Pipeline** - QR code verification with themblem.com API
+- [ ] **Camera Rules API** - Device-specific camera configuration fetching
+- [ ] **Auto-torch API** - Torch recommendation based on QR code
+- [ ] **Event Tracking** - Frame upload and analytics events
+- [ ] **Session Management** - Unique session ID generation and tracking
+
+### ✅ Phone Model & Runtime Configuration
+- [ ] **Device Detection** - Phone model identification and global data storage
+- [ ] **Processing Mode Selection** - Worker vs synchronous based on device type
+- [ ] **Camera Sensitivity Adjustment** - Device-specific camera sensitivity settings
+- [ ] **Performance Optimization** - Frame upload throttling and batch processing
+
+### ✅ Navigation & Integration
+- [x] **Entry Point Configuration** - Support for return page query parameter ✅
+- [x] **Return Page Routing** - Navigate to specified page after successful scan and verification ✅
+
+### ✅ Error Handling & Recovery
+- [ ] **Verification Failure Flow** - Retry and service contact options
+- [ ] **Upload Failure Recovery** - Network error handling
+- [ ] **Worker Failure Fallback** - Graceful degradation to sync mode
+- [ ] **Camera Permission Handling** - User permission flow management
+
+### ✅ Assets & Resources
+- [ ] **Static Image Assets**:
+ - [ ] QR targeting arcs (`/static/arc.png`)
+ - [ ] QR positioning markers (`/static/qrmarkers.png`)
+ - [ ] UI action icons (`/assets/play-button.png`, `/assets/flash-button.png`)
+- [ ] **WebAssembly Binary** - `qrtool.wx.js` QR processing library
+- [ ] **Animation Assets** - Lottie animations for loading states
+
+## Module Structure Design
+
+```
+/qr-scanner-module/
+├── qr-scanner.js # Main page logic
+├── qr-scanner.wxml # UI template
+├── qr-scanner.wxss # Styling
+├── qr-scanner.json # Page configuration
+├── lib/
+│ ├── qrtool.wx.js # WebAssembly QR library
+│ ├── precheck.js # QR validation logic
+│ ├── upload.js # API communication
+│ └── utils.js # Utility functions
+├── components/ # UI components
+│ ├── tooltip/
+│ ├── verifyspin/
+│ ├── verifyfailed/
+│ ├── scanguide/
+│ └── servicemodal/
+├── worker/
+│ └── index.js # WebAssembly worker
+└── assets/ # Static resources
+ ├── images/
+ └── animations/
+```
+
+## Integration Requirements
+
+### Required Query Parameters
+- `return_page` - Target page to navigate to after successful verification
+
+### Required Global Dependencies
+- WeChat Mini Program camera API
+- WebAssembly support
+- Worker thread support (iOS)
+- Canvas 2D context for image processing
+
+### External API Dependencies
+- `{server_url}/api/v1/camera-rules/` - Camera configuration
+- `{server_url}/api/v1/check-auto-torch/` - Torch recommendations
+- Verification endpoint for QR code validation
+- Event tracking endpoints
+
+## Success Criteria
+
+1. **Self-contained Module** - All functionality within single directory
+2. **Platform Portability** - Easy integration into other mini programs
+3. **Device Compatibility** - Support for iOS/Android with appropriate fallbacks
+4. **Performance Optimization** - Efficient WebAssembly + Worker threading
+5. **Robust Error Handling** - Graceful failure modes and user feedback
+6. **Configurable Integration** - Flexible redirect and mode parameters
+7. **Asset Independence** - Bundled resources with minimal external dependencies
+
+## Current Status: PLANNING PHASE ⏳
+
+Next steps:
+1. Create module directory structure
+2. Extract and consolidate existing code
+3. Implement portable integration interface
+4. Test cross-platform compatibility
\ No newline at end of file
diff --git a/scanner/app.json b/scanner/app.json
index c05a043..543d2c5 100644
--- a/scanner/app.json
+++ b/scanner/app.json
@@ -2,6 +2,7 @@
"pages": [
"pages/index/index",
"pages/camera/camera",
+ "pages/emblemscanner/emblemscanner",
"pages/debugentry/debugentry",
"pages/debuguploaded/debuguploaded",
"pages/camwebview/camwebview",
diff --git a/scanner/pages/emblemscanner/assets/README.md b/scanner/pages/emblemscanner/assets/README.md
new file mode 100644
index 0000000..4947db2
--- /dev/null
+++ b/scanner/pages/emblemscanner/assets/README.md
@@ -0,0 +1,10 @@
+# Required Assets
+
+This directory should contain the following image files:
+
+- `arc.png` - Corner arc image for QR targeting overlay
+- `qrmarkers.png` - QR marker positioning image
+- `play-button.png` - Play/guide button icon
+- `flash-button.png` - Flash/torch button icon
+
+These assets should be copied from the main project's static/ and assets/ directories when available.
\ No newline at end of file
diff --git a/scanner/pages/emblemscanner/assets/arc.png b/scanner/pages/emblemscanner/assets/arc.png
new file mode 100644
index 0000000..bc3a238
Binary files /dev/null and b/scanner/pages/emblemscanner/assets/arc.png differ
diff --git a/scanner/pages/emblemscanner/assets/flash-button.png b/scanner/pages/emblemscanner/assets/flash-button.png
new file mode 100644
index 0000000..b18cdc3
Binary files /dev/null and b/scanner/pages/emblemscanner/assets/flash-button.png differ
diff --git a/scanner/pages/emblemscanner/assets/play-button.png b/scanner/pages/emblemscanner/assets/play-button.png
new file mode 100644
index 0000000..aff31b1
Binary files /dev/null and b/scanner/pages/emblemscanner/assets/play-button.png differ
diff --git a/scanner/pages/emblemscanner/assets/qrmarkers.png b/scanner/pages/emblemscanner/assets/qrmarkers.png
new file mode 100644
index 0000000..2f47bce
Binary files /dev/null and b/scanner/pages/emblemscanner/assets/qrmarkers.png differ
diff --git a/scanner/pages/emblemscanner/emblemscanner.js b/scanner/pages/emblemscanner/emblemscanner.js
new file mode 100644
index 0000000..606981e
--- /dev/null
+++ b/scanner/pages/emblemscanner/emblemscanner.js
@@ -0,0 +1,258 @@
+// QR Scanner Module - Self-contained QR scanning page
+// Adapted from existing camera implementation
+
+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: ''
+ },
+
+ /**
+ * 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 = [];
+
+ // Get system information
+ this.initializeSystem();
+
+ // Load camera rules
+ this.loadCameraRules();
+ },
+
+ /**
+ * Initialize system information and device detection
+ */
+ initializeSystem() {
+ const systemInfo = wx.getSystemInfoSync();
+ const phone_model = systemInfo.model;
+ const use_worker = phone_model.toLowerCase().includes('iphone');
+
+ this.setData({
+ phone_model,
+ window_width: systemInfo.windowWidth,
+ window_height: systemInfo.windowHeight,
+ use_worker
+ });
+
+ console.log(`Phone model: ${phone_model}, Use worker: ${use_worker}`);
+
+ // 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() {
+ // TODO: Implement camera rules loading from API
+ console.log('Camera rules loading would happen here');
+
+ // For now, set default values
+ this.setData({
+ zoom: 1,
+ camera_sensitivity: 1,
+ busy: false
+ });
+ },
+
+ /**
+ * 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);
+
+ // For demo purposes, simulate QR detection
+ if (Math.random() < 0.01) { // 1% chance to simulate QR found
+ this.simulateQRFound();
+ }
+ },
+
+ /**
+ * Simulate QR code found (for testing)
+ */
+ simulateQRFound() {
+ this.setData({
+ hint_text: '识别成功',
+ show_modal: 'verifying'
+ });
+
+ // Simulate verification process
+ setTimeout(() => {
+ this.onVerificationSuccess('test-qr-code');
+ }, 2000);
+ },
+
+ /**
+ * 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 '查找二维码';
+ }
+});
\ No newline at end of file
diff --git a/scanner/pages/emblemscanner/emblemscanner.json b/scanner/pages/emblemscanner/emblemscanner.json
new file mode 100644
index 0000000..2fb78ae
--- /dev/null
+++ b/scanner/pages/emblemscanner/emblemscanner.json
@@ -0,0 +1,3 @@
+{
+ "navigationBarTitleText": "QR Scanner"
+}
\ No newline at end of file
diff --git a/scanner/pages/emblemscanner/emblemscanner.wxml b/scanner/pages/emblemscanner/emblemscanner.wxml
new file mode 100644
index 0000000..cd09593
--- /dev/null
+++ b/scanner/pages/emblemscanner/emblemscanner.wxml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ hint_text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item }}
+ model: {{ phone_model }}
+ zoom: {{ zoom }}
+ sensitivity: {{ camera_sensitivity }}
+ frame uploaded: {{ frame_uploaded }} (upload cost {{ frame_upload_time_cost }}ms)
+ max zoom: {{ max_zoom }}
+ result: {{ result }}
+
+
+
+
+
+
+
+
+
+
+ 扫描指南
+
+
+
+
+
+
+
+ 开启补光
+
+
+
+
+
+
+
+
+
+ 将QR码置于框内进行扫描
+
+
+
+
+
+
+ 服务联系信息
+
+
+
+
+
+
+
+
+ 正在验证中...
+
+
+
+
+
+
+ 验证失败
+
+
+
+
+
+
+
+
+ 扫描指南
+ 1. 将QR码置于框内
+ 2. 保持稳定
+ 3. 确保光线充足
+
+
+
+
\ No newline at end of file
diff --git a/scanner/pages/emblemscanner/emblemscanner.wxss b/scanner/pages/emblemscanner/emblemscanner.wxss
new file mode 100644
index 0000000..c67bc50
--- /dev/null
+++ b/scanner/pages/emblemscanner/emblemscanner.wxss
@@ -0,0 +1,246 @@
+/* Main container */
+view.wrapper {
+ width: 100%;
+ height: 100%;
+ background-size: cover;
+ position: relative;
+ overflow: hidden;
+}
+
+/* WeChat camera */
+camera.camera {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ z-index: -3;
+}
+
+/* Canvas for image processing */
+canvas#output {
+ width: 10px;
+ height: 10px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: -10;
+}
+
+/* QR targeting arcs overlay */
+view.qrarc.sm {
+ width: 350rpx;
+ height: 350rpx;
+ margin: 360rpx 200rpx;
+ position: absolute;
+ animation: qrarc-anime 1.2s ease-in-out infinite;
+}
+
+view.qrarc.lg {
+ width: 550rpx;
+ height: 550rpx;
+ margin: 260rpx 100rpx;
+ position: absolute;
+ animation: qrarc-anime 1.2s ease-in-out infinite;
+}
+
+view.qrarc image.arc {
+ position: absolute;
+ width: 15%;
+ height: 15%;
+ opacity: 0.9;
+}
+
+view.qrarc image.arc.topright {
+ right: 0;
+ transform: rotate(90deg);
+}
+
+view.qrarc image.arc.bottomleft {
+ bottom: 0;
+ transform: rotate(-90deg);
+}
+
+view.qrarc image.arc.bottomright {
+ bottom: 0;
+ right: 0;
+ transform: rotate(180deg);
+}
+
+@keyframes qrarc-anime {
+ 0% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.05);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+
+/* QR markers overlay */
+view.qrmarkers {
+ opacity: 70%;
+ margin: 100rpx 0;
+}
+
+image.square {
+ width: 750rpx;
+ height: 750rpx;
+}
+
+/* On-screen display for hints */
+view.osd {
+ position: fixed;
+ width: 100%;
+ top: 0;
+ color: #fff;
+ text-align: center;
+}
+
+view.osd .upper {
+ border-radius: 20rpx;
+ background-color: rgba(0, 0, 0, 0.6);
+ font-size: 1.1rem;
+ display: inline-block;
+ margin: 130rpx auto 630rpx auto;
+ padding: 0.8rem 2rem;
+}
+
+/* Bottom action controls */
+view.bottomfixed {
+ position: absolute;
+ width: 100%;
+ bottom: 0;
+ height: 200rpx;
+ background-color: #171616;
+ text-align: center;
+ border-top: 2px solid rgba(239, 72, 35, 0.7);
+ color: #707070;
+}
+
+.actions {
+ font-size: 30rpx;
+}
+
+.actions .icon image {
+ height: 30rpx;
+ width: 80rpx;
+}
+
+view.half {
+ position: relative;
+ width: 50%;
+ display: inline-block;
+}
+
+view.icon {
+ font-size: 20rpx;
+ margin: 30rpx 0 20rpx;
+}
+
+view.text {
+ display: block;
+}
+
+view.brighter view.text {
+ color: #eee;
+}
+
+view.brighter {
+ color: #eee;
+}
+
+/* Debug overlay */
+view.debug {
+ position: absolute;
+ width: 80%;
+ bottom: 240rpx;
+ left: 10px;
+ padding: 0.3rem;
+ border: 1px solid yellow;
+ border-radius: 3px;
+ color: yellow;
+ background-color: rgba(100, 100, 100, 0.8);
+ z-index: 1000;
+ font-size: 13px;
+ word-break: break-all;
+}
+
+view.debug image {
+ position: fixed;
+ right: 10px;
+ top: 10px;
+ width: 64px;
+ height: 64px;
+ border: 1px solid green;
+}
+
+/* Tooltip */
+view.tooltip {
+ position: fixed;
+ bottom: 310rpx;
+ left: 75rpx;
+}
+
+.tooltip-content {
+ background-color: rgba(0, 0, 0, 0.8);
+ color: white;
+ padding: 20rpx;
+ border-radius: 10rpx;
+ font-size: 28rpx;
+}
+
+/* Modal styles */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.modal-content {
+ background-color: white;
+ padding: 40rpx;
+ border-radius: 20rpx;
+ text-align: center;
+ max-width: 600rpx;
+ margin: 40rpx;
+}
+
+.modal-content.verifying {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20rpx;
+}
+
+.spinner {
+ width: 60rpx;
+ height: 60rpx;
+ border: 4rpx solid #f3f3f3;
+ border-top: 4rpx solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.modal-content button {
+ margin: 20rpx 10rpx;
+ padding: 20rpx 40rpx;
+ border: none;
+ border-radius: 10rpx;
+ background-color: #ef4823;
+ color: white;
+}
\ No newline at end of file