From c2d93f09a94b8045a5e138892532cdc5fc7a812a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AC=B2=E7=88=86=E7=88=86?= Date: Sat, 21 Jun 2025 14:53:27 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 34 + DEPLOYMENT.md | 125 +++ LICENSE | 4 +- README.md | 187 ++++ app.js | 205 ++++ app.json | 34 + app.wxss | 14 + assets/icons/album.png | Bin 0 -> 6209 bytes assets/icons/back.png | Bin 0 -> 4590 bytes assets/icons/bar-code.png | Bin 0 -> 3236 bytes assets/icons/bar_code_b.png | Bin 0 -> 4333 bytes assets/icons/camera.png | Bin 0 -> 5175 bytes assets/icons/camera_switch.png | Bin 0 -> 5376 bytes assets/icons/cloud-offline.png | Bin 0 -> 5913 bytes assets/icons/contact.png | Bin 0 -> 7941 bytes assets/icons/email.png | Bin 0 -> 4228 bytes assets/icons/flash_off.png | Bin 0 -> 5475 bytes assets/icons/flash_on.png | Bin 0 -> 4477 bytes assets/icons/history.png | Bin 0 -> 8983 bytes assets/icons/history_list.png | Bin 0 -> 6250 bytes assets/icons/icons.wxss | 61 + assets/icons/link.png | Bin 0 -> 7353 bytes assets/icons/location.png | Bin 0 -> 8370 bytes assets/icons/micro_qrcode.png | Bin 0 -> 5614 bytes assets/icons/phone.png | Bin 0 -> 7869 bytes assets/icons/qrcode.png | Bin 0 -> 5645 bytes assets/icons/qrcode12.png | Bin 0 -> 6100 bytes assets/icons/qrcode_b.png | Bin 0 -> 10910 bytes assets/icons/qrcode_logo.png | Bin 0 -> 132109 bytes assets/icons/scan.png | Bin 0 -> 4312 bytes assets/icons/setting.png | Bin 0 -> 7664 bytes assets/icons/text.png | Bin 0 -> 3391 bytes assets/icons/tick_b.png | Bin 0 -> 4072 bytes assets/icons/tick_w.png | Bin 0 -> 3646 bytes assets/icons/wifi.png | Bin 0 -> 5488 bytes assets/js/weapp.qrcode.esm.js | 5 + config/api-config.js | 88 ++ config/config-manager.js | 108 ++ images/README.md | 31 + pages/custom-scan/custom-scan.js | 562 ++++++++++ pages/custom-scan/custom-scan.json | 6 + pages/custom-scan/custom-scan.wxml | 159 +++ pages/custom-scan/custom-scan.wxss | 580 ++++++++++ pages/generate-barcode/generate-barcode.js | 373 +++++++ pages/generate-barcode/generate-barcode.json | 5 + pages/generate-barcode/generate-barcode.wxml | 190 ++++ pages/generate-barcode/generate-barcode.wxss | 524 +++++++++ pages/generate-qr/generate-qr.js | 357 ++++++ pages/generate-qr/generate-qr.json | 5 + pages/generate-qr/generate-qr.wxml | 188 ++++ pages/generate-qr/generate-qr.wxss | 574 ++++++++++ pages/generate-result/generate-result.js | 1037 +++++++++++++++++ pages/generate-result/generate-result.json | 3 + pages/generate-result/generate-result.wxml | 90 ++ pages/generate-result/generate-result.wxss | 262 +++++ pages/history/history.js | 784 +++++++++++++ pages/history/history.json | 5 + pages/history/history.wxml | 124 +++ pages/history/history.wxss | 477 ++++++++ pages/index/index.js | 903 +++++++++++++++ pages/index/index.json | 4 + pages/index/index.wxml | 236 ++++ pages/index/index.wxss | 1050 ++++++++++++++++++ pages/logs/logs.js | 18 + pages/logs/logs.json | 4 + pages/logs/logs.wxml | 6 + pages/logs/logs.wxss | 16 + project.config.json | 38 + sitemap.json | 7 + theme.json | 14 + utils/api-service.js | 481 ++++++++ utils/barcode-simple.js | 1 + utils/history-manager.js | 254 +++++ utils/jsbarcode-util.js | 238 ++++ utils/message-util.js | 139 +++ utils/qrcode.js | 8 + utils/util.js | 19 + 77 files changed, 10635 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 DEPLOYMENT.md create mode 100644 README.md create mode 100644 app.js create mode 100644 app.json create mode 100644 app.wxss create mode 100644 assets/icons/album.png create mode 100644 assets/icons/back.png create mode 100644 assets/icons/bar-code.png create mode 100644 assets/icons/bar_code_b.png create mode 100644 assets/icons/camera.png create mode 100644 assets/icons/camera_switch.png create mode 100644 assets/icons/cloud-offline.png create mode 100644 assets/icons/contact.png create mode 100644 assets/icons/email.png create mode 100644 assets/icons/flash_off.png create mode 100644 assets/icons/flash_on.png create mode 100644 assets/icons/history.png create mode 100644 assets/icons/history_list.png create mode 100644 assets/icons/icons.wxss create mode 100644 assets/icons/link.png create mode 100644 assets/icons/location.png create mode 100644 assets/icons/micro_qrcode.png create mode 100644 assets/icons/phone.png create mode 100644 assets/icons/qrcode.png create mode 100644 assets/icons/qrcode12.png create mode 100644 assets/icons/qrcode_b.png create mode 100644 assets/icons/qrcode_logo.png create mode 100644 assets/icons/scan.png create mode 100644 assets/icons/setting.png create mode 100644 assets/icons/text.png create mode 100644 assets/icons/tick_b.png create mode 100644 assets/icons/tick_w.png create mode 100644 assets/icons/wifi.png create mode 100644 assets/js/weapp.qrcode.esm.js create mode 100644 config/api-config.js create mode 100644 config/config-manager.js create mode 100644 images/README.md create mode 100644 pages/custom-scan/custom-scan.js create mode 100644 pages/custom-scan/custom-scan.json create mode 100644 pages/custom-scan/custom-scan.wxml create mode 100644 pages/custom-scan/custom-scan.wxss create mode 100644 pages/generate-barcode/generate-barcode.js create mode 100644 pages/generate-barcode/generate-barcode.json create mode 100644 pages/generate-barcode/generate-barcode.wxml create mode 100644 pages/generate-barcode/generate-barcode.wxss create mode 100644 pages/generate-qr/generate-qr.js create mode 100644 pages/generate-qr/generate-qr.json create mode 100644 pages/generate-qr/generate-qr.wxml create mode 100644 pages/generate-qr/generate-qr.wxss create mode 100644 pages/generate-result/generate-result.js create mode 100644 pages/generate-result/generate-result.json create mode 100644 pages/generate-result/generate-result.wxml create mode 100644 pages/generate-result/generate-result.wxss create mode 100644 pages/history/history.js create mode 100644 pages/history/history.json create mode 100644 pages/history/history.wxml create mode 100644 pages/history/history.wxss create mode 100644 pages/index/index.js create mode 100644 pages/index/index.json create mode 100644 pages/index/index.wxml create mode 100644 pages/index/index.wxss create mode 100644 pages/logs/logs.js create mode 100644 pages/logs/logs.json create mode 100644 pages/logs/logs.wxml create mode 100644 pages/logs/logs.wxss create mode 100644 project.config.json create mode 100644 sitemap.json create mode 100644 theme.json create mode 100644 utils/api-service.js create mode 100644 utils/barcode-simple.js create mode 100644 utils/history-manager.js create mode 100644 utils/jsbarcode-util.js create mode 100644 utils/message-util.js create mode 100644 utils/qrcode.js create mode 100644 utils/util.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8105f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# 依赖和包管理 +node_modules/ +package-lock.json +miniprogram_npm/ + +# 微信小程序私有配置文件 +project.private.config.json + +# 系统文件 +.DS_Store +*.log +.vscode/ +.idea/ + +# 敏感配置文件 +config/api-config.local.js +config/secrets.js +*.env +.env* + +# 临时文件 +*.tmp +*.temp +.cache/ + +# 开发和调试文件 +debug.log +error.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# 微信开发者工具生成的文件 +.tea/ \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..a79c408 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,125 @@ +# 部署指南 + +## 🚀 快速部署 + +### 1. 配置小程序AppID + +在 `project.config.json` 文件中,将 `appid` 字段替换为您自己的小程序AppID: + +```json +{ + "appid": "您的小程序AppID" +} +``` + +### 2. 配置API服务器 + +1. 复制 `config/api-config.example.js` 为 `config/api-config.js` +2. 修改其中的配置信息: + +```javascript +const ApiConfig = { + server: { + baseUrl: 'https://您的API服务器地址.com', + // 其他配置... + }, + auth: { + corpCode: '您的企业代码', + storageKeys: { + // ⚠️ 重要:以下键名必须根据您的服务器API规范进行调整 + accessToken: 'your_server_access_token_key', + userCode: 'your_server_user_code_key', + userInfo: 'user_info', + isLoggedIn: 'is_logged_in' + } + // 其他配置... + } +}; +``` + +> **重要说明**:`storageKeys` 中的键名是根据不同服务器的API规范来设定的。不同的后端服务可能使用不同的命名约定,请根据您的服务器API文档进行相应调整。 + +### 3. 环境变量配置(可选) + +如果您使用环境变量管理配置,可以创建 `.env` 文件: + +```env +API_BASE_URL=https://your-api-server.com +CORP_CODE=your_corp_code +WECHAT_APP_ID=your_wechat_app_id +``` + +### 4. 私有配置 + +`project.private.config.json` 文件包含开发者工具的私有配置,请根据需要调整: + +- 删除或修改其中的个人配置 +- 该文件已被 `.gitignore` 忽略,不会被提交到版本控制 + +## ⚙️ 详细配置说明 + +### StorageKeys 配置 + +`storageKeys` 配置是与您的后端服务密切相关的,不同的服务器可能使用完全不同的命名规范: + +**示例对比:** + +```javascript +// 某些服务器使用带前缀的格式 +storageKeys: { + accessToken: 'S-Access-Token', + userCode: 'S-Code' +} + +// 另一些服务器使用标准格式 +storageKeys: { + accessToken: 'access_token', + userCode: 'user_id' +} + +// 还有些服务器使用驼峰命名 +storageKeys: { + accessToken: 'accessToken', + userCode: 'userCode' +} +``` + +**如何确定正确的键名:** +1. 查看您的服务器API文档 +2. 检查登录接口返回的Header字段名 +3. 咨询后端开发人员确认存储键的命名规范 + +## 📋 部署检查清单 + +在发布到生产环境前,请确认: + +- [ ] 已替换所有示例配置为真实配置 +- [ ] **已根据服务器API规范调整storageKeys配置** +- [ ] 移除或替换测试数据 +- [ ] 检查所有API端点是否正确 +- [ ] 验证小程序AppID是否正确 +- [ ] 测试登录和认证功能是否正常 +- [ ] 测试所有主要功能是否正常 +- [ ] 确认权限配置是否完整 + +## 🔒 安全建议 + +1. **永远不要**将真实的AppID、密钥等敏感信息提交到公开仓库 +2. 使用环境变量或私有配置文件管理敏感信息 +3. 定期检查和更新访问令牌 +4. 在生产环境中禁用测试和调试功能 + +## 🛠️ 开发环境 + +如果您需要在开发环境中使用不同的配置,可以: + +1. 创建 `config/api-config.local.js` 文件(已被gitignore忽略) +2. 在代码中根据环境加载不同的配置文件 + +```javascript +// 示例:根据环境加载配置 +const isDev = wx.getSystemInfoSync().platform === 'devtools'; +const config = isDev ? + require('./api-config.local.js') : + require('./api-config.js'); +``` \ No newline at end of file diff --git a/LICENSE b/LICENSE index b257232..211bd9a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 嬲爆爆 +Copyright (c) 2024 小籽二维码 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e52546 --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ +# 小籽二维码 - 微信小程序 + +
+ 小籽二维码 +

扫码生成 · 简单便捷

+

一款功能完整的微信小程序二维码工具,支持扫码识别、生成二维码和条形码等功能

+
+ +## ✨ 主要功能 + +### 🔍 扫码识别 +- **智能识别**:支持二维码、条形码等多种格式 +- **相册扫码**:可从相册选择图片进行识别 +- **多种模式**:自定义摄像头和微信API两种扫码模式 +- **结果处理**:自动识别URL、WiFi、联系人、电话等信息类型 + +### 📱 二维码生成 +- **多类型支持**:文本、链接、WiFi、联系人信息 +- **自定义样式**:支持多种样式和颜色设置 +- **高清输出**:生成高质量二维码图片 +- **保存分享**:可保存到相册或分享给好友 + +### 📊 条形码生成 +- **多格式支持**:Code128、EAN13、Code39等标准格式 +- **商品条码**:支持商品标准条形码生成 +- **自定义配置**:可调整尺寸、颜色等参数 + +### 📝 扫码记录 +- **历史记录**:自动保存所有扫码和生成记录 +- **分类管理**:按类型和时间分类显示 +- **快速操作**:支持复制、分享、删除等操作 +- **数据统计**:显示使用统计和记录数量 + +## 🛠️ 技术特点 + +- **iOS风格界面**:采用现代化iOS设计风格,用户体验友好 +- **响应式布局**:适配不同屏幕尺寸的设备 +- **离线支持**:核心功能支持离线使用 +- **性能优化**:采用懒加载和组件化开发 +- **数据持久化**:使用本地存储保存用户数据 + +## 📁 项目结构 + +``` +QrCode/ +├── app.js # 应用入口文件 +├── app.json # 应用配置文件 +├── app.wxss # 全局样式文件 +├── assets/ # 静态资源目录 +│ ├── icons/ # 图标文件 +│ └── js/ # 第三方JS库 +├── config/ # 配置文件目录 +│ └── api-config.js # API配置 +├── pages/ # 页面目录 +│ ├── index/ # 首页 +│ ├── custom-scan/ # 自定义扫码页面 +│ ├── generate-qr/ # 二维码生成页面 +│ ├── generate-barcode/ # 条形码生成页面 +│ ├── generate-result/ # 生成结果页面 +│ └── history/ # 历史记录页面 +└── utils/ # 工具类目录 + ├── api-service.js # API服务 + ├── history-manager.js # 历史记录管理 + ├── qrcode.js # 二维码工具 + └── util.js # 通用工具函数 +``` + +## 🚀 快速开始 + +### 环境要求 +- 微信开发者工具 +- Node.js (可选,用于开发工具) + +### 安装步骤 + +1. **克隆项目** + ```bash + git clone [项目地址] + cd QrCode + ``` + +2. **配置项目** + - 复制 `config/api-config.js` 为 `config/api-config.local.js` + - 在 `config/api-config.local.js` 中填入您的API服务器地址和配置 + - 在 `project.config.json` 中填入您的小程序AppID + + > 💡 **配置说明**:项目会优先使用 `api-config.local.js`(不会上传到Git),如果不存在则使用默认的 `api-config.js` + +3. **微信开发者工具导入** + - 打开微信开发者工具 + - 选择"导入项目" + - 选择项目目录 + - 确认AppID配置正确 + +4. **预览和调试** + - 点击"编译"按钮 + - 使用"真机调试"测试摄像头功能 + - 可使用"预览"功能在手机上体验 + +> ⚠️ **重要提醒**:首次使用前请仔细阅读 [DEPLOYMENT.md](DEPLOYMENT.md) 文件了解如何正确配置敏感信息。 + +## 📋 功能详情 + +### 扫码功能 +- 支持QR码、Data Matrix、PDF417等二维码格式 +- 支持EAN、UPC、Code128等条形码格式 +- 自动识别内容类型(URL、文本、WiFi配置等) +- 提供历史记录和快速操作 + +### 生成功能 +- **二维码生成**:支持文本、URL、WiFi、联系人信息 +- **条形码生成**:支持商品码、自定义文本编码 +- **样式自定义**:颜色、尺寸、边距等参数调整 +- **批量操作**:支持批量生成和导出 + +### 历史管理 +- 自动保存所有操作记录 +- 按日期、类型分类展示 +- 支持搜索和筛选 +- 数据导出和备份功能 + +## 🎨 界面预览 + +- **首页**:功能导航和快速操作 +- **扫码页**:实时摄像头预览和识别 +- **生成页**:参数配置和实时预览 +- **历史页**:记录列表和详情查看 + +## 🔧 自定义配置 + +### API配置 +修改 `config/api-config.js` 文件来配置后端服务地址: + +```javascript +module.exports = { + baseUrl: 'https://your-api-domain.com', + timeout: 10000, + // 其他配置... +} +``` + +### 功能开关 +在 `app.js` 中可以配置功能开关: + +```javascript +globalData: { + enableOfflineMode: true, // 离线模式 + enableHistory: true, // 历史记录 + enableShare: true, // 分享功能 +} +``` + +## 📱 兼容性 + +- **微信版本**:要求微信7.0.0及以上版本 +- **系统支持**:iOS 10.0+、Android 5.0+ +- **功能权限**:需要摄像头、相册访问权限 + +## 🤝 贡献指南 + +欢迎提交Issue和Pull Request来帮助改进项目! + +1. Fork 项目 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 打开 Pull Request + +## 📄 开源协议 + +本项目采用 MIT 协议开源 - 查看 [LICENSE](LICENSE) 文件了解详情。 + +## 👨‍💻 作者 + +- 如有疑问或建议,欢迎提交Issue + +## 🙏 致谢 + +- 感谢微信小程序平台提供的开发框架 +- 感谢开源社区提供的各种工具库和组件 +- 感谢所有贡献者和用户的支持 + +--- + +
+

⭐ 如果这个项目对你有帮助,请给它一个星标!

+
\ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..4fad964 --- /dev/null +++ b/app.js @@ -0,0 +1,205 @@ +// app.js +const apiService = require('./utils/api-service.js'); +const MessageUtil = require('./utils/message-util.js'); + +App({ + onLaunch() { + console.log('小程序启动'); + + // 展示本地存储能力 + const logs = wx.getStorageSync('logs') || [] + logs.unshift(Date.now()) + wx.setStorageSync('logs', logs) + + // 设置初始化状态 + this.globalData.isInitializing = true; + this.globalData.initializationError = null; + + // 静默登录 + this.performSilentLogin(); + }, + + // 执行静默登录 + async performSilentLogin() { + try { + console.log('开始静默登录...'); + + // 检查是否已经登录 + if (apiService.isUserLoggedIn()) { + console.log('用户已登录,跳过静默登录'); + this.globalData.userInfo = apiService.userInfo; + this.globalData.isLoggedIn = true; + this.globalData.isOfflineMode = false; + this.finishInitialization(true); + return; + } + + // 执行静默登录 + const result = await apiService.silentLogin(); + + if (result.success) { + console.log('静默登录成功:', result.userInfo); + this.globalData.userInfo = result.userInfo; + this.globalData.isLoggedIn = true; + + // 可以在这里触发其他需要登录状态的操作 + this.onLoginSuccess(); + this.finishInitialization(true); + } + } catch (error) { + console.error('静默登录失败:', error); + this.globalData.isLoggedIn = false; + this.globalData.initializationError = error; + + // 可以在这里处理登录失败的逻辑 + this.onLoginFailed(error); + this.finishInitialization(false); + } + }, + + // 完成初始化 + finishInitialization(success) { + console.log('初始化完成,成功:', success); + this.globalData.isInitializing = false; + + // 通知所有页面初始化完成 + this.notifyPagesInitComplete(success); + }, + + // 通知页面初始化完成 + notifyPagesInitComplete(success) { + // 获取当前页面栈 + const pages = getCurrentPages(); + if (pages.length > 0) { + const currentPage = pages[pages.length - 1]; + + // 如果当前页面有初始化完成的回调,则调用 + if (typeof currentPage.onAppInitComplete === 'function') { + currentPage.onAppInitComplete(success); + } + } + }, + + // 检查是否正在初始化 + isInitializing() { + return this.globalData.isInitializing; + }, + + // 获取初始化错误 + getInitializationError() { + return this.globalData.initializationError; + }, + + // 登录成功回调 + onLoginSuccess() { + console.log('登录成功,可以进行后续操作'); + + // 检查是否为测试模式,如果是测试模式则不清除手动设置 + const isTestMode = this.isTestMode(); + if (isTestMode) { + console.log('🧪 测试模式:保持当前手动设置的离线状态'); + return; + } + + // 清除离线模式状态 + this.globalData.isOfflineMode = false; + + // 清除离线状态存储 + try { + wx.removeStorageSync('app_offline_mode'); + } catch (e) { + console.error('清除离线状态失败:', e); + } + }, + + // 登录失败回调 + onLoginFailed(error) { + console.log('登录失败,进入离线模式'); + + // 检查是否为测试模式,如果是测试模式则不覆盖手动设置 + const isTestMode = this.isTestMode(); + if (isTestMode) { + console.log('🧪 测试模式:保持当前手动设置的离线状态'); + return; + } + + // 设置离线模式状态 + this.globalData.isOfflineMode = true; + + // 保存离线状态到本地存储 + try { + wx.setStorageSync('app_offline_mode', true); + } catch (e) { + console.error('保存离线状态失败:', e); + } + }, + + // 获取登录状态 + isLoggedIn() { + return this.globalData.isLoggedIn && apiService.isUserLoggedIn(); + }, + + // 检查是否为离线模式 + isOfflineMode() { + // 先检查内存状态 + if (this.globalData.isOfflineMode !== undefined) { + return this.globalData.isOfflineMode; + } + + // 从本地存储检查 + try { + const offlineMode = wx.getStorageSync('app_offline_mode'); + const testMode = wx.getStorageSync('test_offline_mode'); + + // 如果是测试模式,显示额外日志 + if (testMode) { + console.log('🧪 测试模式:手动设置的离线状态 =', !!offlineMode); + } + + this.globalData.isOfflineMode = !!offlineMode; + return this.globalData.isOfflineMode; + } catch (e) { + console.error('读取离线状态失败:', e); + return false; + } + }, + + // 检查是否为测试模式 + isTestMode() { + try { + return !!wx.getStorageSync('test_offline_mode'); + } catch (e) { + return false; + } + }, + + // 获取用户信息 + getUserInfo() { + return this.globalData.userInfo; + }, + + // 手动重新登录 + async reLogin() { + try { + const result = await apiService.silentLogin(); + if (result.success) { + this.globalData.userInfo = result.userInfo; + this.globalData.isLoggedIn = true; + this.onLoginSuccess(); + return true; + } + } catch (error) { + console.error('重新登录失败:', error); + MessageUtil.showServiceError(); + } + return false; + }, + + globalData: { + userInfo: null, + isLoggedIn: false, + isOfflineMode: false, + isInitializing: false, + initializationError: null + } +}) diff --git a/app.json b/app.json new file mode 100644 index 0000000..6a2a19b --- /dev/null +++ b/app.json @@ -0,0 +1,34 @@ +{ + "pages": [ + "pages/index/index", + "pages/custom-scan/custom-scan", + "pages/generate-qr/generate-qr", + "pages/generate-barcode/generate-barcode", + "pages/generate-result/generate-result", + "pages/history/history", + "pages/logs/logs" + ], + "window": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "小籽二维码", + "navigationBarBackgroundColor": "#ffffff", + "backgroundColor": "#f8f9fa", + "backgroundTextStyle": "dark", + "enablePullDownRefresh": true + }, + "permission": { + "scope.camera": { + "desc": "用于扫描二维码和条形码,以及摄像头功能测试" + }, + "scope.writePhotosAlbum": { + "desc": "用于保存生成的二维码和拍摄的照片到相册" + } + }, + "darkmode": true, + "themeLocation": "theme.json", + "requiredBackgroundModes": ["audio"], + "style": "v2", + "componentFramework": "glass-easel", + "sitemapLocation": "sitemap.json", + "lazyCodeLoading": "requiredComponents" +} diff --git a/app.wxss b/app.wxss new file mode 100644 index 0000000..0221432 --- /dev/null +++ b/app.wxss @@ -0,0 +1,14 @@ +/**app.wxss**/ + +/* 引入全局图标样式 */ +@import "./assets/icons/icons.wxss"; + +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 200rpx 0, 0, 0; + box-sizing: border-box; +} diff --git a/assets/icons/album.png b/assets/icons/album.png new file mode 100644 index 0000000000000000000000000000000000000000..88e212df79117b0417c7c22d0f1910f6f243a82e GIT binary patch literal 6209 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 literal 0 HcmV?d00001 diff --git a/assets/icons/back.png b/assets/icons/back.png new file mode 100644 index 0000000000000000000000000000000000000000..c4714b19151ca8b8dae796f168fc40329b091c16 GIT binary patch literal 4590 zcma)Ac|4SF_a4*8pfJ`6X$-}e$Xb>x!(gavWz8DKJ{e?5wo&$NWI|cWl7y5cYba~e zM6zWWGpF49hI*%2_*p<8&}n@P+7x(p zAAg`Hf$wt5H@U!r!Ov7r3-qQ(a1jK8z0yacZUx!VbHd-+xCD1ix~|$GKfkKaKcT27 z@=uAOU=u)YU`aLPnabPK(@P0N1qdiDVfrZ!H{3qV*S;U9s{cLKx zZhwjYOR1$(?{7Xgv-Vg3y-?&)?Jta*TM2%8#qX_K-S$eZ+J4jg^6o_V2Ge|K%j$k` zPmEu{s#>pb)Hx1NWH2+wj|dQyVgw>gErTK%p_&}oLI@Dbhb=-=1zblcoQ^g*FAPE% zn7Vn%pJjliB@5`19)ckM=)ZXP)gOYusNmpcoM4VZ(MO$^`(Y7?#X84Z`cTc(G;aQ} z>w2L7_X|X$w1z!uZc38Kg|c%Ok=A->ql9j4yx>v)&$gIz)W*Qo7pLyXJYoWKID;0p z?jL3w6jk0pzn7Bv8V0LxG%Xo-DG$X)D4p8U1Z+OEs~Em}Azx#`Y3F8V=A_eH$R0dO zTa7CM5z6Z@_TGD%iB0f_dt!ZM?~5*Wjez|vb5DA4e_{tb6u1fdoV>jJ7dD}*3p1~_ zWS1Xm&(F=7(?@GOClP)QWo!)4?;`Wqs@=-^`uek-nO{N#E5A90cTh60GVCwma=`M5 zDQs2n==gYcqp!_)+fTK#TIh3?LlZyxKPq(#FhDDzd)TUaIii#86EkFf=+LN{RZm=f zr5Fgs>oDr3U>?{=%|ZL){wyTHi2ViP_wT^{oO--~kOnJV=1&^#z%S!U?eb19ak;{X zi1>Ur%T1?=kIb5>d=BvFp91LiP((O=Dt0~(g~n;jb^UR~I;?yIPUlVWMgO!8oJHjK zGif*OxWPu?I(@kvDebUkV0E6z-nNE>;cLw#6<9dC*2w{lhlRSla^HcJGS?!YVH5Ca zz$}r?CAG+6F#7jtPx|PsC|L|ivxJeEFy-NtxKDXOMxig{i|^eXG!VJS!!XDtGRP96APyyz zcV_mUcHavwJ}V$_JMidmTjNzuLX$tl;2#VJj^!--tFU{{Bk#(9i#SE0;O+80y_4EQM+>;x#)kgIs}o0<#I+mG z6$lWN&tus+Ht#7aE>Neqf20C1(SYtw!@t`)-qoGeb1ZBZA+Zr<2ekUy&S zy+Dh;?l1#kCbTMIL%yWeAO85g^Tp=IXoHo=dCUj9Nfi3FKkR8t*2vauM=z8U&WswW zu(!|2%&fcOUE9X6w0931HVvAVRhWmcTGH7oNKHq^kvjae`?V(*KUa@ArX09t1^cCR(cTic#0wE` z#(wq{=ut`2V3)snu5D9eYG2c^K>eYKu*5bAI- zZ|Vu2f66{5#ZGva)8`dEpMKGP0*)MKP4idvw@9{-Slcmyz^D%8hnd(5l^c49VS*@? zjcQSUX&9}CzFRdkQIr)|_JFgy9Ff~_wjsj{S2GkQoHzUFbv5&hX{yYr6UTLbO(Bw_ zZ*XYzk_Q$kISKI<)LbY`BEAA69&TH1dhRTA~!o~lBp*PS`)=o&F299$1;cdBjs za=U!UV#8jd>>E?04U|?o_Cj{7w>G_yTa4=i^ffD_O0V_)+1@gky(ZY>c;C(~!7jcj+Zo2I@_v=h zJgB9uJ=eGe3)1#M1r@jb$*_1ck6a%sAPm;#Yrk3B7G7pvnnCI5QCHQf`DA{T1OD_l zy&JP%Rz>ooH=3J==mD30`KufiWGB?YL}j>9m%T@;{U1CyP>1!c1ndlo=p}o#H!AC{ zUEiuMqA=pAY#A4=Xrmi$fvV$b%F4STg8QoSc+IbxpLYpe*%f z?MlpEZzn})VhPucj9SQ_g^{nRY&tWnCfwdqsPeZu><~Fvp?PTz(MGwaZ~O!-w9`7I z97r@-$H4*hvbcEomIP1W?RzifRI0&dC+#}xA|ZeY*Oa8Bl0a2%qLHqNzwH^;XtiIQ z7%5CapOQwhQ{Ze+&0JdCg2<3H-9pt92eVQ}rWzdQB}w+2!`w5!cJ^y$@ZeBZ&gh>- zNZK30mCz{<4dljzcs%YVE1CleciQ7D>~r(F4q#%Leo8E^YN**lp34d`1^s>Tf0=uh zxf>dY6#G4SZ& z_-^VbYx^&z>Nt_R>flWl+Pkrzij)WJ1}DM6h&947UPa039!}V!-|Lz@4D_4%NonOz z=2Ai(1D1bFYX4kRpr0F;p4}x#*!ANZt>cpI3?4Z>;MgG|nw!Z<({)3*+Q8i(SLIL+ zvfwU8<~AQv>_L$%cJR&#xr5ahTLt-W^aoTZZ_vJx~Qu3ABF?x#$1)c_`d@WO?z{AF({`ztU}L4iX%2eg-j2PHd1HvzXz5eViz}I2OqQF@>#dC1`DX zfVUI04tUkfMr1Gv5xp9UyN*cx+n!b343S~XK}Sv<=(LeAzT|0?W|XG%-*H zNvB0lK=y=FpE12mlnokqpGW>_$pa1!nfXIE;XZsv?ty{9Y8;uh?%MF^yL?2ACCB9J z>@ec50`a||myhqrabWZj8Q$UOnNIO|^>kk&_(^aU%(5D91CN$RZtPMxL$aZVK_QSw zZP5k}CMcVRiuu8~1%NJWpCW-SFacEUw+3n`vp@CJu6(|E6_sfD^1nc^+dG9)kYUZUSRnOvt{f#_v;mYdrw6I?}? zBneNnQHI4`k#E`|d(DKaMroT=0i4c^ze|{E=57W_9%f+*E@8ws0eE7Co|&-N;dcqE zqBL0nhR=)2q#{1MrYU|V&1{hmR47-=v*YU0U~tRS8G{Y^PWW)ny3LI+|BQd_`gGr54WIk&;Oudzoaco)?BFELf{u)4uhA}B8O2^X!^vp-`_#t;JvQ!Wv z$0J%#way*K*}jZRjTPbt2!g7r>ewJfXUL7K+lC{?v;XCdYo&v)8`B2T*VTL%zP-4z zLT%hm^_8aSrneeH@MZtDNS`#D63U9K6Ga6ZP+{514n2i-a3fr;eT{&QSIxC3e3RHe zchC;>xXJ2}?UmyFbMY|5+7TpLIyQlY(E4F$SBgB9C!Bx9%CqIaxQKj?#318Bh7P>R z%guaU>pJv+{XCK%>t zR%))J;xt|X5KG`P<`X5NH*4-x+uc^4B zM*-3s@%pXL5J1#*ohVGzeJ~TZ^1%nQQGa-e88C(lf~ZB>FP)i!GxXwlceL|0Zk-jr ztQ`h`_y++t+OP5HIZ-NPH(l{z5v>m(zQP#fXGP?OJKZ!6y2s$DeaYAk2ROg}y`vAl zX^1s%`6~G>*mSgL&L9HCC(W5<|4)Twp_4(8G)7*e8E@=C0HT(rtZ!xz>ONI-*LR#!<4d7?%vH^ zlf4}+mNXlE8$La;IFB(C0-!9mrhQR+sty&h|~=|dc@bxBfdym5OtT}Tei=%+Mh*{ zZ(j@?$3Qo1nAPs#4+61a_v(!b`lyE(W*V6r^3iqtF@bzOBs#0c-tBc$hi_+6HX=?` zy+KL+3#B-HQ-Pmqn^Fk;V%MN#XlU5%zyF86zK|<7J37+Ml7^ubBuw`KDrUAZfW9~B zn`2N=KSt#JZ&7r4)Z8a8FE3XW1dpb>T8LbVl`N`|PX3o8+voPX{|>Nb0iqd~A)P$v zac>aV+uWymkNmFSJ2VaB+xE_NeD$~Ss3PS+ioHHoFjcbzqK~U>^2e9mlDO+uV|^s! z@Il5VOLGw@u6lFyiaC}}lXjS^2=3N#HUJlK1Ap>SkHlRY16)ZA!1Fh)%ZIc}#9B4Y zz(oK6qy;U~l&)NjcjlH66D>QLr?YYep6}OF*2m2oZ@~uu0kZyBRfncT7JC2tv@T zZoxYhfR;2Wrb{Q|1uML2$BaC<`Oho@%1ozD@!40BpPye=AfB7pRUmGi^TrEaqyr-S z)@%T{_7hB+%UGY0%;w!Kjuh>yoDm4MPm`4{U)llPT(v~ok;2;bJ1MYjVt@k-;aDto zQ)3JB+x+cHBs^{Zhi2M=qZ*|E5{Ltz)cS|;`Z($rLr zoiGK+-P=jB#al`pZ6t%)*FZa_3#D{9Dg*O^lj2$&5DiIWEoUD^t3S3|%7mLLtH~XG z9|=Oqf$U2MdtRBBx}I^mpwoW1Z;QMynmnyH-a}p0s;b3q$xxM zK^{dwEV&XTbcmwTBS^6j9x4`i2k-j@@7*=)oH=XHH+$Be`My2-q}v^`78g+vK_C#~ zHe{j$+_N_y^cMJi*QK@??gYXdtSu3>y*uU+2r*9^B1Db!{7}?UtNItV>yDl`(q)Is zGi1DqEhVMy0%RPDj1%1M-Y~V5Gp&B~)bnR?g7?#vmG?^8?iWelM|c)`)CK9?@MXR& ztZ}aRd@uE2M3IHLey8%-(tzto*=Kxt_FCXC3h`t0 zLWiTf>2F*b`znd&ij7(xzGhs%-o}hR zl=+NfMc8-lI?(`GC-d`<%$Mp$Kc~A?tu)~wDQpz@q*NlDz>r9qK0WV9iyg#8ycWrF zhz=AGUDjm)L-z|{BndOm!7)ySHtd69Uov?w;h%3Df!?Zp3wEFbcP?ZQ@B?5@*j&FiUQ7LNYV8uy=z( zi=WHx0G+HDofp}n66I@PuMX8vhqsnUKqECO+YuYTPV2rNTx(*kvn5T>wx=)&-bUd^rJ=$Ow0H)vyGWqS>tffM z%f}wrhkkO>fPycwt)Eq_S28EGi;QOok1}9D3CLHpTGyccdUYpkw=;ziljQzz;M7v{ ztFe-5KsnwGRcLg?Gy83hICN;cS)&mFK{r6iORbO)bdJ%v$Z>=KdJy4zfFmb>)|+@W z6(xuQ$bt-AT`!nBF+Qnt;xI?V3C~zD*aTqZvSRT+;NU0?C9n1d!Tq6DUZiiO+QpE zUqc1F_RXr~tfWW;47%KE@A8U}WSI`+EgSO~VYS_nJg$jr$fTBuK7e7-Kl6=IqshD(G^)CFb+ z`yY6o`sPIa@p=yHwbV8)YxZ7NjDZ(;)JBO7w2OcY2FicZF?DpIa-a%~ZexY>2KzWjg zrO-0ZX1Ba1T1EwY2@mXwKD!Ah#-C8#jGV|*kWoRBu1dfF);0CJzEuE~k>*kU+a&}8 zBKZ|LVlo#*Br^l4479YlDGb0IE^f>!g--$uy(}V`SY|B@1E!x}bJ8<+!YoexunVfW z&1pY;C#tX^$WL+SdHZO*#d$xi68y@GCM|Q%_%hxDW%4kqUl56f^CDV$pxy=Myth?1 z3xqk3-BspO58MAr$xSB#2ne(zc6q}Z$Pm|-%78^gyqR|8FR(uwcpA_mE2<_Hn3TXA zKj~vrVF-;+wA**g=kT2f-nZqEq=_PpRS9}F*wqpS=iT09{JDTc1S+4Dcbe}3FA#>i zI_3&5;Ez=+#LFQuJP)={ge*MZdSR=2U-F}}t%FO*zSc756Gk5{^|^mu+Zen1?bl2y z=ak=-Q&a9;_j`}z`&b06eo~rH>io6QkH0ei>g)20jf@UEmwddKaN;c5pu_G}%(-o- zL<%f!)dVrGKjDjFblLA*4!jHnA^$K44!mKedr1=xFbAO*GYZ@Nx~`AXzL)ZFl4za64>w_8KYI16Hf*%U?J@VSZfa-@4w&Qgt>%|8l`-)S0<4*e1fck~x(>jm`jQSvuCVWl4&SeZ3e{=oX_0p{9NGc# zIuVEQm6M)x9XVj(RDv&k4mv{ zY}CE|#7&_t=%hSts%rMYRjX*rkm zdk=UYiQ}nGcyh+trA(ggk!^EX^1jU2LWcuKLK5JkJzO{~3GLl9*Jl{kAC4H$H?d!| zKXZ-MyPDgrwCU#W2FHmv8-o1}c()c@6}D`2X>U9RC#FvAkd9GbUFX4&gF?p{6S0z? z`ro)Ubv1YG*_fyi^;EiVM70(DSpzPbeaX%1_PNnlaU*Q|x%0Ii*T-JESKUT&>rIa2 z-+woh7^PTPUU|E0(PaxnDP!k_D^D(&0_+Dvz8&{dfyK+_`Vp|Wb^y2rPnhJ;iroYbwj7U6XWt9!P zkxz9-L7a9OxGw0-V1E@mf4F*~%Q4pUi`Z&aarR(2$WK1D6o_rDW|kZX7m31r1{n^~ z4#d#ueZH4kOPOEWqnkrXsOL%M6DzWrU#Y99jzyL`QSIo8>~%G;&y8Pv{fpwDy%nRG z$Rl;@Vxt_1aplYsGheKLfE>JL7m@e!#-Ulit^_mY_bjWA1!Qq3P7{njtI+d-8&BfJ zA6lzrQCG`$r_u}MWt*Yb12Q9x=|elDadU1%RLpKR0Cj`;qBC=p20}9NzxBi#U`#Ijb%LZ@*}lI)fvp zLb@K`skD{V%muG)(3Y&o=D0hGAysfKgED(WUC#Z8dsDhOL+;~Rh(?<`Mg<7mUd3*s zH3!P_4x;rKDj@0Fr tr6W-Ds*>LZRFRm9?t7Eee**ARX)?hu*8ETNT==&GVM97ZthGE!{|`eZk;wo6 literal 0 HcmV?d00001 diff --git a/assets/icons/bar_code_b.png b/assets/icons/bar_code_b.png new file mode 100644 index 0000000000000000000000000000000000000000..f26045e53ce840798b1d6d4e9a94c3dcb4f460d9 GIT binary patch literal 4333 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 literal 0 HcmV?d00001 diff --git a/assets/icons/camera.png b/assets/icons/camera.png new file mode 100644 index 0000000000000000000000000000000000000000..fd3e0ed4470d8c78706a2c13c6cfaabf00f4e870 GIT binary patch literal 5175 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 E0hNorqW}N^ literal 0 HcmV?d00001 diff --git a/assets/icons/camera_switch.png b/assets/icons/camera_switch.png new file mode 100644 index 0000000000000000000000000000000000000000..2344a9a98b5bb82bdbf4cb2678ad69cf71253713 GIT binary patch literal 5376 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“`!+0JfidWpRqzjv>G#xaW)pJe3XO|!Ll<0V#$-aoTTOOEar>v7R(H%S zJvw95O%pXdIiP$Qvm!3n*~F~73$d@ zj}1`i>Zy!ZQL{n;CezyYXt%aTpQYqf%uRqw^B+h;*T1!XrVFxqaa2*|# zv~dSND`CM;iMGcl&_vx6U~j?^1<^wMBMa4n1~x;nzLFmL&$$Z3dSLacHC0&djBKa1 z_FIemwjFJj4$AD9gu|o<^btZ<+)A?RplCgH^yRFP`XWv=}g<%2kbJ0Flqqc%N$X3~nub&noZSzIvr@n`Pbc3WKdu3a)0%<52# zsW{ozF~j31lWqRI8;G}0%HwAY_tA!iOL$d4(}h17J7-xCsa3P?NF6@ul6PpM=w|m1?2i&c_nO4h+AL_Yr`_Fr^HErFCD%|kS@FSm+}#-cl0Sw zy1A!F?(yziVC#SAP^VD1@n`%Un|d^jAa>ZrX{QU%MH5Cm=pN(xmX2%*f_9_ zoADw_KD`uAZY&%rpSGm-94~IMAty(*(HZVx!gDm+hdP>s+f7_B`ex3zFy}kC;?%S$ ziqtw_{d#H^>n#0s8C1$_>;J@}Vfznmzh!Zup%EV-9&GusOfD)D;xDRMsA`S;^DSR= zRgOiLgS7_>^8{!4sus0gNfJ>mhRgrxfB+L1O~LQGG{^jbL}i8&{-p6JVyIa~z>mQ1#v5+pgnlt0*j*zx=Y)tH(1<-f(r;e&_Kt}eD2QH`06!^Xg9Y93CShrez?srn@# zbf(FWs!F1P#=|#MB(rAAPe(DTelY|bLno-nNFnC~IS#6R5sQ%1@uDKz;;TYZqx2XN z;o3u%B^7+(ta&mRohlQsR#U!0plDLn&@I6%-EFGbL@OZX6ARv9fm#mRsY_iMk@uL_P~AB<3|be z^LOVE^84I`o;(rtxfsEf%8=jr%O=%}QT>YGw2<*lD5w$+=Q(cijD~bz4Y#7?1K$YJ z7?ZQ#JCJ;}5t*>Rbv~dJ_8Zq&Pd}dnNul1>k-{3jEuUzo#Ory3#z>RGBNS;>u`UqW zT(Jh*@Y*LVUhh1|do}BCXCZ)R(Y*{bA^JO!(s5~#Rp?=lnJ);#F&HaVS7cv}Y#A86V1?^`ap;xS_6*PEJ zahyy~Eh+Cs$8L*%MOS|N(}tHCvAy|F+yF`4r_ruPXIrYAbbT_E%*;dGS_|lUxEs}4 jBhnk$DE_}E`74rtWiBME)uW1PX#!}ZxnZULP5l1=gksri literal 0 HcmV?d00001 diff --git a/assets/icons/cloud-offline.png b/assets/icons/cloud-offline.png new file mode 100644 index 0000000000000000000000000000000000000000..85ca54545af29687743c463c40fea450db2f2498 GIT binary patch literal 5913 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 bcMQ4X>A93)beGY(i`R0B~9mOzckB z^M3(jImwkoTK)+j!FJ|&fYvYa6#x(h7A81{aQE+*A_g};3H)&@9Blo|RI7B4@4Q6k zd0W4sqI21Pj@c-b?Y}Sn(KMCx%fYz#D9RnwSI7q2XBFBuJRDLZ2)UG^XdCaI9Nb>s zQ}!_&*wBCKvt%6^(!Zm(ymM@@aiBO5(Jw-XjzVOk$U5z2Fc_JNMBsMU5YQ1cnzFKt z0ZkCUSfY(Ypg$;`v!(8NC)E&?FQ+;e1S!d-wYLgFkk1|%k+N^X;?Xx7sTXBVmVWBs4!r>c8TFla>8z+ zqi$#y9Y5QJS)rosOYZ|MyukAdC``IAITxZR^zd)#hB?)HC9oRtC>toT_2K-m0@#fS z(vLC*CbNYzQD755r!|RY8ekY?x@}XNv<|#RGfE1`-%v$pAfY7)#zpVT%8N~~cIxO< z(~}G_OnN+h2GG;8eHZz98Qr4}Ip(EX>WE};VbbB`B;Z>{xdJOrS+vw?qC~goH@*OX zJ_R<9cv`8T4l|wAUgUxY=7IqE0HUq`*^CX>cyRoc*s&@D%46xZjY(I&b)gozfN;W2 zhCuPE1Sl^-=gHLS8c&vrerbV+M(v@szFEmYZAsNDYyhR4S;V+9hg?#n40lw05K+pArLPmDGPm&*cBUD zFl{&BT~|Vb<3|HSC7aZQ@Sz+yPhs;^+xOL{&m-L3zZinQdg^VoGfX_P66z*gwYoDr zQh60P^u}(yQ^WgUwE;B|aGV)T5)BA0pU&ZUYCApB0zrCKm& zWxO<1(p8FU{Q1wSNvG;DczIg8^+dpWeUOLq<>QMww~}qrKi-@?tp;MccSJ3w?ID|S zQ_ay`X;QPWmWfKOrPwge^_!mu?|V1izz?j$)HHwF=oEnxfjH|CX(2au57u`p3qM)k05$d4R#vpcoUT;eFsoOuW1@ZX)UrrS`-31~6K zO8_9>{>OY`RNAN6`&~gsCe=(~H!@XWwv+XuRUeZwF7x?36-x=nHr|(b-<|*6{_#!P zhZ>ExbGBY(8)mcsJBO|v`pwOcM#|m6Qy-ByD3l(y(l77`BQmJbD0i?Du^m`hW3^(G zP40WJCG>^u1AUSE@tDZ11Q?1enbdQ0nHsxk)jpMP%j4xx!4Mk@JXYPAbFoI^utR!- zC1u_bQz0zk)}g}8X=XoMUQg5lBHWB9w%8oikL>;)D&AJ01(3BY)w1X`Piw!reqk#M zj3k9j(M5lM3ir(n3rq#+ahI$TU>Wg>e_Yh65;ul3`T8q$7Mq{d6%u}(ZIW|b<#iO_ zX;JFs;WbFJwKGXSx1u(b%KX8S{a{z8;kmw5J9v(k=~Twx8PhnP{hWO(5iQ8ndr3yR zqwZIo3} zdg1=QOIy5{s)QxrI*+8NTh?7y0=KO1Xe=-Vs{T%9>(+;X@ADvO2Gy~;0HpBPs7AD3el`QhFTeahTb)pAm{t>C_Eev20J*Ou5wo=kR5zR|6 zms%G%)SW70JW^UMg+aO(TZ?bE#!%Tw%V|4B>38q` zw(R5VH{x_vETwlHW(?o&ec=JV=kQey)jL(Tb?B=c`Ftce-!(aHZiE|4Nbw*%OKH87 zXz3kxYD(I5BRlBB2prjYs?6!*;fgG&uZCw;7G8TJq9a4cw^$L|6K|3hd?@5|N{8Vo z#{2&5tI@I~m>M4GeVZ+etjJ!=M2!C_)@RLfC`|#Qt`^EE6uVAIUxhz&iFrfn!h_V9 z5VNxtuR}J41~QKtKei*E*nQGn+1--6D2$Zk&*0qM6e;eOFdX4b38fNJD};v6{=6gy=9Y7Jm^?{=u;7ou0_|pK2rvbtf??)3*cjDO(9Iuoly)&w=aJp zc)L6D>K=DZ$aDBvp28BWcG<_3Bif>4(2dhXH~NhVc~eBiKNg#7zF`e#ab15xufBz6 zT=BCmktnbN1BSB$Cg?4g=g+Q z^k}pc*x#mM30yoWYmE`IKaN+8#5Y3yl=u=Gn49xoHNi4?tzY-z3GHkWEtH8OVx0zP ziXb87RtD}|x*31IwEAt0QHw&pO8||5D6indl^ah_GmbGD3!DI7k8FfVzLTirrXZ2YVE9&!5l zxaKL5pTJ>O8Vx}SU}ngRm*ijPVhW=8xN58klZ_!FPmz>Lh}-vKm1y)Dr16uvpHVL2 zC&d3#NEW7AaVPvUYfx@lf4aCYO{qTN@9zM^PTc2H=~=LzwYrgB)|w`Shv5x3M3w?c zd_jji4Uo;}(B_g`y9gpLz(35&7xYURARVo8O2O<+_jeq{#@EAuY_m-gV5KGsV{R^3 z;zTk$_F@3dkgo(1|MudCbcf6fly7Vd3O?1w!K!EyWK3J`dzW%TcOU`Z;%z(bPXTT# zB-w{(l_0}$-g}&7d5F`|^iu$#hxP6~HYwPiJBO!?2a~@H$bkuhN97qGCtzUQhsx}# zMfG?(3_1Z}kc%5S5z)N^!gi*A9n=@a;tTYl%S_`EqbWp602|}Sg)9Clpb)lNFiIHz z{T(X$FU!EA*i_^1G4v1^c>R0o#fSLx%qdFV!C6T>T^`zE*dB8vp~_W?h~5v+4mMA| zyoZElB8aR2cVV$)Ci+bI9)V|f)d4~-3pJ<7eGb>xVbgV>TS@EUquMN{Mb2f2c%-Yc z?u(SVElWle_v6vr&^r)qt+?_hV4-1^aO(B|v>L~?{LB~zVuSk9*gP@v#WTG9bi$Xvd<+pq>4s)sSuSV9c4e#`&` zYX>HkP*-zj`)V@M?ENLcYetN%%Llub=E((<uvsU`*BThW>4kqKi(us3+DN>nOI^l;n n0$06>5$KojCLs5}FyN4z?5MqOK=V#Kc`E=Grq(7jyeH*9DZXoy literal 0 HcmV?d00001 diff --git a/assets/icons/flash_off.png b/assets/icons/flash_off.png new file mode 100644 index 0000000000000000000000000000000000000000..629569209d5240f538e22d0b4cedd68a95bbf187 GIT binary patch literal 5475 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

FgB{muzvPC_iFWv5?V==~ z9t_>Xx6_7Y*e%@Zkt+A-{Vu4}){0qkk+ty7=2I-hYvuldpzLqKKR>N_{~U`yDkAE4 zC-n%~+~|9wLf!E)qs@^`rt*38Q)1L*z&NF_l#%}Q6hO;!f=3wWS+UNGIyIMqR$Gu; zjqR@oo5|7{g;Y~uPbn2?wJ0%TN>>TKarGWRHT~#?4Fi4rcDlVlL7}lU)rixh`wv?O zzB1vrQkl_P-p~u!_%sN4+jGdEklsc^wU4Jg-GT|^sZ06OaI^qfxTX{65=b9qxV%yN zLub+)T7Z;G-^lx3N@=UC;Y$)_ysX%eu;{Qaw_cldz(e|76XND2m}hn7N9>lQM~;(Y qrCWU3u6-oU{P$XD#zgx$RmNRJZfnCA{fn9{Ku1$wqx_Lw#D4+eVDjYv literal 0 HcmV?d00001 diff --git a/assets/icons/phone.png b/assets/icons/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..20fc9349dd39e93317159bde1eafef7879e303f2 GIT binary patch literal 7869 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 C@`!c- literal 0 HcmV?d00001 diff --git a/assets/icons/qrcode.png b/assets/icons/qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..6f9c78a8604adb4c95723d9757504a6e6e0c61fc GIT binary patch literal 5645 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 ItIqiU0Zt1|9RL6T literal 0 HcmV?d00001 diff --git a/assets/icons/qrcode12.png b/assets/icons/qrcode12.png new file mode 100644 index 0000000000000000000000000000000000000000..58970436ff1b6e68f16f2be19da22db30dc35be7 GIT binary patch literal 6100 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 RaZ;HcprdJ^QLW|}@qd0HL2Lj3 literal 0 HcmV?d00001 diff --git a/assets/icons/qrcode_b.png b/assets/icons/qrcode_b.png new file mode 100644 index 0000000000000000000000000000000000000000..3d6bb3a7488418f3674c8a6835c71f09cf48b75d GIT binary patch literal 10910 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ⅆ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≠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 literal 0 HcmV?d00001 diff --git a/assets/icons/scan.png b/assets/icons/scan.png new file mode 100644 index 0000000000000000000000000000000000000000..a90ca78e59a28a19f65dda6287515b84c182bdf7 GIT binary patch literal 4312 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

It7Ej}FG==QkeR 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 Bw0{5q literal 0 HcmV?d00001 diff --git a/assets/icons/text.png b/assets/icons/text.png new file mode 100644 index 0000000000000000000000000000000000000000..e32e8f6bab568b9b20543e481bef09fa7c764ea9 GIT binary patch literal 3391 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 NJ}V3kU3cDt@*fD7a?1b! literal 0 HcmV?d00001 diff --git a/assets/icons/tick_b.png b/assets/icons/tick_b.png new file mode 100644 index 0000000000000000000000000000000000000000..23e6cfd3e4362a1de4b3190a06371c1bbeddebca GIT binary patch literal 4072 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 IYU=XfKgX+?}^bsYn3;umz99 zdV#fL^O1moXSHw5ZLmP%y`1fVn)hlvkhFxyVh$$<@{5RE9Y=*Xg1A>YHE~xWj~~Kf zA#$E_p}AY@B$Y&u!N)Ha%E@_pZpUW6x_d_srF|Yj5Pc(e@6;g?Ih{Rsuy^HzN+yN4 z%PR7h+gtH&_OY$YD?Ul19|sb5FB_-3j#<`|g+)gNEwh#9ALLRbs&GIsQbhZaF+>?# zDkvSRXeHaP0b&{$+IR&oq@A&_1zO&5L$&juYA}_`t z-Q3BIHhx;Xq)AD~PNA_bFlBp>#~aR+$GY#_AE+^nMLJ3C+aGpDQ1j05#2Rqn@(zkLK6tsvGW7)ga1dAZ@#>QKX^w>!8s_$I>w%%@EV7fp z9;U3$TeCtnhJ(2A#Ho5{Y-i4&E4|m?#!_$3S!y-jy6@!_^>M!@aa^7uA!&8kc88S$%*CEa)b&Z}KJMRH=N-Pt zvQ5NRyMOw@I4=j?w{rL(JbC`!`V}T$bi7;4b)=8IO79{Sc3^1v-` zU*yA7Wt8;DS|-j$w@b}mQG-wGo%!mq=KfzzoOyZeMxR29+v@bLK_FVThr^(QZMw{I zCegjz$8PLhN#b+WKv9&WGD>xHO$2Xa(xuidt2Al@v?|^~<3RmcF*Ywy#RUYax`rmKPzKEd-R=󌩰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 literal 0 HcmV?d00001 diff --git a/assets/icons/wifi.png b/assets/icons/wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7d76800bad5fa5d9e51f1685019b6f01ebc7ae GIT binary patch literal 5488 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 literal 0 HcmV?d00001 diff --git a/assets/js/weapp.qrcode.esm.js b/assets/js/weapp.qrcode.esm.js new file mode 100644 index 0000000..7231270 --- /dev/null +++ b/assets/js/weapp.qrcode.esm.js @@ -0,0 +1,5 @@ +/** + * weapp.qrcode.js v1.0.0 (https://github.com/yingye/weapp-qrcode#readme) + */ + +var hasOwn=Object.prototype.hasOwnProperty,toStr=Object.prototype.toString,defineProperty=Object.defineProperty,gOPD=Object.getOwnPropertyDescriptor,isArray=function(t){return"function"==typeof Array.isArray?Array.isArray(t):"[object Array]"===toStr.call(t)},isPlainObject=function(t){if(!t||"[object Object]"!==toStr.call(t))return!1;var e,r=hasOwn.call(t,"constructor"),o=t.constructor&&t.constructor.prototype&&hasOwn.call(t.constructor.prototype,"isPrototypeOf");if(t.constructor&&!r&&!o)return!1;for(e in t);return void 0===e||hasOwn.call(t,e)},setProperty=function(t,e){defineProperty&&"__proto__"===e.name?defineProperty(t,e.name,{enumerable:!0,configurable:!0,value:e.newValue,writable:!0}):t[e.name]=e.newValue},getProperty=function(t,e){if("__proto__"===e){if(!hasOwn.call(t,e))return;if(gOPD)return gOPD(t,e).value}return t[e]},extend=function t(){var e,r,o,n,i,a,s=arguments[0],u=1,l=arguments.length,h=!1;for("boolean"==typeof s&&(h=s,s=arguments[1]||{},u=2),(null==s||"object"!=typeof s&&"function"!=typeof s)&&(s={});u=7&&this.setupTypeNumber(t),null==this.dataCache&&(this.dataCache=QRCode.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,e)},setupPositionProbePattern:function(t,e){for(var r=-1;r<=7;r++)if(!(t+r<=-1||this.moduleCount<=t+r))for(var o=-1;o<=7;o++)e+o<=-1||this.moduleCount<=e+o||(this.modules[t+r][e+o]=0<=r&&r<=6&&(0==o||6==o)||0<=o&&o<=6&&(0==r||6==r)||2<=r&&r<=4&&2<=o&&o<=4)},getBestMaskPattern:function(){for(var t=0,e=0,r=0;r<8;r++){this.makeImpl(!0,r);var o=QRUtil.getLostPoint(this);(0==r||t>o)&&(t=o,e=r)}return e},createMovieClip:function(t,e,r){var o=t.createEmptyMovieClip(e,r);this.make();for(var n=0;n>r&1);this.modules[Math.floor(r/3)][r%3+this.moduleCount-8-3]=o}for(r=0;r<18;r++){o=!t&&1==(e>>r&1);this.modules[r%3+this.moduleCount-8-3][Math.floor(r/3)]=o}},setupTypeInfo:function(t,e){for(var r=this.errorCorrectLevel<<3|e,o=QRUtil.getBCHTypeInfo(r),n=0;n<15;n++){var i=!t&&1==(o>>n&1);n<6?this.modules[n][8]=i:n<8?this.modules[n+1][8]=i:this.modules[this.moduleCount-15+n][8]=i}for(n=0;n<15;n++){i=!t&&1==(o>>n&1);n<8?this.modules[8][this.moduleCount-n-1]=i:n<9?this.modules[8][15-n-1+1]=i:this.modules[8][15-n-1]=i}this.modules[this.moduleCount-8][8]=!t},mapData:function(t,e){for(var r=-1,o=this.moduleCount-1,n=7,i=0,a=this.moduleCount-1;a>0;a-=2)for(6==a&&a--;;){for(var s=0;s<2;s++)if(null==this.modules[o][a-s]){var u=!1;i>>n&1)),QRUtil.getMask(e,o,a-s)&&(u=!u),this.modules[o][a-s]=u,-1==--n&&(i++,n=7)}if((o+=r)<0||this.moduleCount<=o){o-=r,r=-r;break}}}},QRCode.PAD0=236,QRCode.PAD1=17,QRCode.createData=function(t,e,r){for(var o=QRRSBlock.getRSBlocks(t,e),n=new QRBitBuffer,i=0;i8*s)throw new Error("code length overflow. ("+n.getLengthInBits()+">"+8*s+")");for(n.getLengthInBits()+4<=8*s&&n.put(0,4);n.getLengthInBits()%8!=0;)n.putBit(!1);for(;!(n.getLengthInBits()>=8*s||(n.put(QRCode.PAD0,8),n.getLengthInBits()>=8*s));)n.put(QRCode.PAD1,8);return QRCode.createBytes(n,o)},QRCode.createBytes=function(t,e){for(var r=0,o=0,n=0,i=new Array(e.length),a=new Array(e.length),s=0;s=0?g.get(c):0}}var d=0;for(h=0;h=0;)e^=QRUtil.G15<=0;)e^=QRUtil.G18<>>=1;return e},getPatternPosition:function(t){return QRUtil.PATTERN_POSITION_TABLE[t-1]},getMask:function(t,e,r){switch(t){case QRMaskPattern.PATTERN000:return(e+r)%2==0;case QRMaskPattern.PATTERN001:return e%2==0;case QRMaskPattern.PATTERN010:return r%3==0;case QRMaskPattern.PATTERN011:return(e+r)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(e/2)+Math.floor(r/3))%2==0;case QRMaskPattern.PATTERN101:return e*r%2+e*r%3==0;case QRMaskPattern.PATTERN110:return(e*r%2+e*r%3)%2==0;case QRMaskPattern.PATTERN111:return(e*r%3+(e+r)%2)%2==0;default:throw new Error("bad maskPattern:"+t)}},getErrorCorrectPolynomial:function(t){for(var e=new QRPolynomial([1],0),r=0;r5&&(r+=3+i-5)}for(o=0;o=256;)t-=255;return QRMath.EXP_TABLE[t]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},i=0;i<8;i++)QRMath.EXP_TABLE[i]=1<=1&&n<=127?e+=t.charAt(r):n>2047?(e+=String.fromCharCode(224|n>>12&15),e+=String.fromCharCode(128|n>>6&63),e+=String.fromCharCode(128|n>>0&63)):(e+=String.fromCharCode(192|n>>6&31),e+=String.fromCharCode(128|n>>0&63));return e}function drawQrcode(t){t=t||{},(t=extend(!0,{width:256,height:256,x:0,y:0,typeNumber:-1,correctLevel:QRErrorCorrectLevel.H,background:"#ffffff",foreground:"#000000",image:{imageResource:"",dx:0,dy:0,dWidth:100,dHeight:100}},t)).canvasId||t.ctx?function(){var e,r=new QRCode(t.typeNumber,t.correctLevel);r.addData(utf16to8(t.text)),r.make(),e=t.ctx?t.ctx:t._this?wx.createCanvasContext&&wx.createCanvasContext(t.canvasId,t._this):wx.createCanvasContext&&wx.createCanvasContext(t.canvasId);for(var o=t.width/r.getModuleCount(),n=t.height/r.getModuleCount(),i=0;i>>7-t%8&1)},put:function(t,e){for(var r=0;r>>e-r-1&1))},getLengthInBits:function(){return this.length},putBit:function(t){var e=Math.floor(this.length/8);this.buffer.length<=e&&this.buffer.push(0),t&&(this.buffer[e]|=128>>>this.length%8),this.length++}};export default drawQrcode; \ No newline at end of file diff --git a/config/api-config.js b/config/api-config.js new file mode 100644 index 0000000..497e5f7 --- /dev/null +++ b/config/api-config.js @@ -0,0 +1,88 @@ +// API配置文件 +// 如需自定义配置,请复制此文件为 api-config.local.js 并修改配置信息 +// 项目会优先使用 api-config.local.js,如不存在则使用此默认配置 + +const ApiConfig = { + // 服务器配置 + server: { + baseUrl: 'https://your-api-server.com', // 替换为您的API服务器地址 + timeout: 30000, // 30秒超时 + retryCount: 3, // 重试次数 + }, + + // API路径配置 + endpoints: { + // 微信登录接口 + wxLogin: '/wx/login', + // 二维码/条形码生成接口 + codeGeneration: '/api/code/gen', + }, + + // 历史记录配置 + history: { + maxRecords: 20, // 历史记录最大条数 + storageKeys: { + scanHistory: 'scanHistory', // 扫码历史 + generateHistory: 'generateHistory', // 生成历史 + barcodeHistory: 'barcodeHistory' // 条形码历史 + }, + // 记录类型 + recordTypes: { + scan: 'scan', // 扫码记录 + qrGenerate: 'qr', // 二维码生成记录 + barcodeGenerate: 'barcode' // 条形码生成记录 + } + }, + + // 频率控制配置 + rateLimit: { + // 本地频率控制 (毫秒) + singleGenInterval: 2000, // 单个生成间隔 2秒 + batchGenInterval: 10000, // 批量生成间隔 10秒 + maxRequestsPerMinute: 20, // 每分钟最多请求数 + maxBatchSize: 50, // 批量生成最大数量 + + // 频率控制存储key + storageKeys: { + lastSingleGen: 'last_single_gen_time', + lastBatchGen: 'last_batch_gen_time', + requestHistory: 'request_history_minute' + } + }, + + // 用户标识配置 + userIdentity: { + storageKey: 'user_unique_id', + prefix: 'wx_user_' + }, + + // 登录配置 + auth: { + corpCode: 'your_corp_code', // 替换为您的企业代码 + storageKeys: { + // 以下键名需要根据您的服务器API规范进行调整 + accessToken: 'access_token', // 访问令牌存储键 + userCode: 'user_code', // 用户代码存储键 + userInfo: 'user_info', // 用户信息存储键 + isLoggedIn: 'is_logged_in' // 登录状态存储键 + }, + tokenExpiryTime: 30 * 60 * 1000, // 30分钟,毫秒 + }, + + // 二维码/条形码类型配置 + codeTypes: { + qrcode: { + standard: 'qr_standard', + micro: 'qr_micro' + }, + barcode: { + 'Code 128': 'barcode_128', + 'Code 39': 'barcode_39', + 'EAN-13': 'barcode_ean13', + 'EAN-8': 'barcode_ean8', + 'ITF': 'barcode_itf' + } + } +}; + +module.exports = ApiConfig; \ No newline at end of file diff --git a/config/config-manager.js b/config/config-manager.js new file mode 100644 index 0000000..92b11f0 --- /dev/null +++ b/config/config-manager.js @@ -0,0 +1,108 @@ +// 配置管理器 +// 自动处理配置文件的加载,支持开发和生产环境 + +const ConfigManager = { + + /** + * 获取API配置 + * 优先级:api-config.local.js > api-config.js + */ + getApiConfig() { + let config = null; + let configSource = ''; + + try { + // 首先尝试加载本地配置文件 + config = require('./api-config.local.js'); + configSource = 'api-config.local.js'; + console.log('✅ 已加载本地配置 api-config.local.js'); + } catch (error) { + try { + // 如果本地配置不存在,加载默认配置 + config = require('./api-config.js'); + configSource = 'api-config.js'; + console.log('📋 已加载默认配置 api-config.js'); + + // 在开发工具中显示提示 + if (wx.getSystemInfoSync().platform === 'devtools') { + this.showConfigTip(); + } + } catch (defaultError) { + console.error('❌ 配置文件加载失败:', defaultError); + throw new Error('无法加载配置文件,请检查 config 目录'); + } + } + + // 验证必要的配置项 + this.validateConfig(config, configSource); + + return config; + }, + + /** + * 验证配置文件的必要字段 + */ + validateConfig(config, configSource = '') { + const requiredFields = [ + 'server.baseUrl', + 'auth.corpCode', + 'auth.storageKeys.accessToken' + ]; + + for (const field of requiredFields) { + const value = this.getNestedValue(config, field); + if (!value || value.includes('your-') || value.includes('example')) { + if (configSource === 'api-config.js') { + console.warn(`⚠️ 配置项 ${field} 是示例值,建议创建 api-config.local.js 文件并配置真实参数`); + } else { + console.warn(`⚠️ 配置项 ${field} 似乎还是示例值,请检查配置`); + } + } + } + }, + + /** + * 获取嵌套对象的值 + */ + getNestedValue(obj, path) { + return path.split('.').reduce((current, key) => current?.[key], obj); + }, + + /** + * 显示配置提示 + */ + showConfigTip() { + // 延迟显示,避免影响启动 + setTimeout(() => { + wx.showModal({ + title: '配置提示', + content: '当前使用默认配置文件。\n\n如需自定义配置,请复制 api-config.js 为 api-config.local.js 并修改您的配置参数。', + showCancel: false, + confirmText: '我知道了' + }); + }, 2000); + }, + + /** + * 检查是否为开发环境 + */ + isDevelopment() { + return wx.getSystemInfoSync().platform === 'devtools'; + }, + + /** + * 获取环境特定的配置 + */ + getEnvironmentConfig() { + const baseConfig = this.getApiConfig(); + + if (this.isDevelopment()) { + // 开发环境的特殊处理 + console.log('🔧 当前为开发环境'); + } + + return baseConfig; + } +}; + +module.exports = ConfigManager; \ No newline at end of file diff --git a/images/README.md b/images/README.md new file mode 100644 index 0000000..b3488d2 --- /dev/null +++ b/images/README.md @@ -0,0 +1,31 @@ +# 图标资源说明 + +## 所需图标列表 + +### 主要功能图标 +- `setting.png` - 设置图标 (36rpx × 36rpx) +- `scan.png` - 扫码图标 (48rpx × 48rpx) +- `qr-code.png` - 二维码图标 (48rpx × 48rpx) +- `barcode.png` - 条形码图标 (48rpx × 48rpx) + +### 箭头图标 +- `arrow-down.png` - 下拉箭头 (24rpx × 24rpx) +- `arrow-up.png` - 上拉箭头 (24rpx × 24rpx) + +### 广告相关 +- `ad-close.png` - 广告关闭按钮 (32rpx × 32rpx) + +### 历史记录图标 +- `scan-record.png` - 扫码记录图标 (48rpx × 48rpx) + +### 分享相关 +- `share-cover.jpg` - 分享封面图 (520px × 416px) + +## 图标设计要求 +- 采用线性图标风格 +- 主色调:#4A90E2 +- 背景透明PNG格式 +- 支持高清屏幕显示 + +## 临时占位 +在开发阶段,可以使用微信小程序默认图标或者在线图标库的图标进行占位。 \ No newline at end of file diff --git a/pages/custom-scan/custom-scan.js b/pages/custom-scan/custom-scan.js new file mode 100644 index 0000000..ed41199 --- /dev/null +++ b/pages/custom-scan/custom-scan.js @@ -0,0 +1,562 @@ +// custom-scan.js - iOS风格版本 +Page({ + data: { + showCamera: false, + devicePosition: 'back', + flash: 'off', + useCustomScan: true, + loading: false, + loadingText: '处理中...', + showResult: false, + scanResult: '', + // 按钮按下状态 + testPressed: false, + customSwitchPressed: false, + wechatSwitchPressed: false, + takePhotoPressed: false, + flashPressed: false, + switchCameraPressed: false, + albumPressed: false, + backPressed: false, + closePressed: false, + copyPressed: false, + handlePressed: false + }, + + onLoad: function (options) { + console.log('扫码页面加载'); + + // 设置页面标题和样式 + wx.setNavigationBarTitle({ + title: '扫一扫' + }); + wx.setNavigationBarColor({ + frontColor: '#ffffff', + backgroundColor: '#000000' + }); + + // 延迟一点启动摄像头,确保页面完全加载 + setTimeout(() => { + this.initCamera(); + }, 500); + }, + + onShow: function () { + console.log('扫码页面显示 - 当前摄像头状态:', this.data.showCamera); + + // 重置页面状态 + this.setData({ + loading: false, + showResult: false, + useCustomScan: true + }); + + // 当页面重新显示时,检查摄像头状态 + // 如果摄像头没有显示,重新初始化 + if (!this.data.showCamera) { + console.log('摄像头未显示,重新初始化'); + setTimeout(() => { + this.initCamera(); + }, 300); + } else { + console.log('摄像头已显示,状态正常'); + } + }, + + onHide: function () { + console.log('扫码页面隐藏'); + + // 隐藏加载状态和结果弹窗 + this.setData({ + loading: false, + showResult: false + }); + }, + + onUnload: function () { + console.log('扫码页面卸载'); + }, + + // 测试按钮 - 验证点击功能(虽然界面隐藏但功能保留) + testButton: function () { + console.log('✅ 按钮功能正常 - 摄像头状态:', this.data.showCamera); + + // iOS风格提示 + wx.showToast({ + title: '功能测试正常', + icon: 'success', + duration: 1500 + }); + }, + + // 初始化摄像头 + initCamera: function () { + console.log('初始化摄像头'); + + // 检查权限 + wx.getSetting({ + success: (res) => { + if (res.authSetting['scope.camera'] === false) { + // 用户拒绝过摄像头权限 + wx.showModal({ + title: '需要摄像头权限', + content: '请在设置中开启摄像头权限来使用扫码功能', + confirmText: '去设置', + cancelText: '返回', + success: (modalRes) => { + if (modalRes.confirm) { + wx.openSetting(); + } else { + this.goBack(); + } + } + }); + } else if (res.authSetting['scope.camera'] === undefined) { + // 还没有请求过权限 + wx.authorize({ + scope: 'scope.camera', + success: () => { + this.setData({ + showCamera: true + }); + console.log('摄像头启动成功'); + }, + fail: () => { + wx.showToast({ + title: '需要摄像头权限才能扫码', + icon: 'none', + duration: 2000 + }); + this.goBack(); + } + }); + } else { + // 启动摄像头 + this.setData({ + showCamera: true + }); + console.log('摄像头启动成功'); + } + }, + fail: (err) => { + console.error('获取设置失败:', err); + wx.showToast({ + title: '权限检查失败', + icon: 'none' + }); + } + }); + }, + + // 切换到自定义扫码 + switchToCustom: function () { + console.log('切换到自定义扫码'); + if (!this.data.useCustomScan) { + this.setData({ + useCustomScan: true + }); + wx.showToast({ + title: '已切换到自定义扫码', + icon: 'none' + }); + } + }, + + // 切换到微信API扫码 + switchToWechat: function () { + console.log('切换到微信API扫码'); + if (this.data.useCustomScan) { + this.setData({ + useCustomScan: false + }); + // 直接调用微信API + this.callWechatScanAPI(); + } + }, + + // 调用微信扫码API + callWechatScanAPI: function () { + console.log('调用微信扫码API'); + wx.scanCode({ + onlyFromCamera: false, // 支持摄像头拍照和从相册选择 + scanType: ['qrCode', 'barCode', 'datamatrix', 'pdf417'], + success: (res) => { + console.log('微信API扫码成功:', res); + this.handleScanSuccess(res.result); + }, + fail: (err) => { + console.error('微信API扫码失败:', err); + + // 无论什么原因失败,都要恢复到自定义扫码模式 + this.setData({ + useCustomScan: true + }); + + if (err.errMsg && err.errMsg.includes('cancel')) { + console.log('用户取消微信扫码,返回自定义模式'); + } else { + wx.showModal({ + title: '扫码失败', + content: '识别失败,请确保图片清晰且包含可识别的二维码或条形码。\n\n提示:可以选择拍照或从相册选择图片进行识别。', + confirmText: '知道了', + showCancel: false + }); + } + + // 确保摄像头状态正确 + if (!this.data.showCamera) { + setTimeout(() => { + this.initCamera(); + }, 300); + } + } + }); + }, + + // 拍照扫码 + takePhotoToScan: function () { + console.log('开始拍照识别'); + + if (!this.data.showCamera) { + wx.showToast({ + title: '摄像头未就绪,请稍候', + icon: 'none' + }); + return; + } + + if (!this.data.useCustomScan) { + // 如果不是自定义模式,调用微信API + this.callWechatScanAPI(); + return; + } + + // 显示加载状态 + this.setData({ + loading: true, + loadingText: '正在识别...' + }); + + // 创建摄像头上下文 + const ctx = wx.createCameraContext(); + + ctx.takePhoto({ + quality: 'high', + success: (res) => { + console.log('拍照成功,开始识别'); + this.recognizeQRCode(res.tempImagePath); + }, + fail: (err) => { + console.error('拍照失败:', err); + this.setData({ + loading: false + }); + + wx.showToast({ + title: '拍照失败,请重试', + icon: 'none' + }); + } + }); + }, + + // 识别二维码 + recognizeQRCode: function (imagePath) { + // 这里使用模拟识别,实际项目中需要接入真实的识别服务 + + this.setData({ + loadingText: '识别中...' + }); + + // 模拟识别过程 + setTimeout(() => { + // 模拟识别结果 + const mockResults = [ + 'https://www.apple.com', + 'https://developer.apple.com', + '这是一个测试二维码内容 - iOS风格界面', + 'WIFI:T:WPA;S:iPhone的WiFi;P:password123;H:;', + 'tel:10086', + 'mailto:example@icloud.com', + null // 表示未识别到 + ]; + + const randomResult = mockResults[Math.floor(Math.random() * mockResults.length)]; + + if (randomResult) { + // 识别成功 + console.log('识别成功:', randomResult); + this.handleScanSuccess(randomResult); + } else { + // 未识别到内容 + this.setData({ + loading: false + }); + wx.showToast({ + title: '未识别到二维码,请重试', + icon: 'none' + }); + } + + }, 1200); // 稍微缩短识别时间 + }, + + // 处理扫码成功 + handleScanSuccess: function (result) { + console.log('扫码成功:', result); + + // 保存扫码记录 + this.saveScanRecord(result); + + this.setData({ + loading: false, + showResult: true, + scanResult: result + }); + }, + + // 保存扫码记录 + saveScanRecord: function(content) { + const historyManager = require('../../utils/history-manager.js'); + const success = historyManager.saveScanRecord(content); + + if (success) { + console.log('扫码记录已保存'); + } + }, + + // 切换闪光灯 + toggleFlash: function () { + console.log('切换闪光灯'); + + const newFlash = this.data.flash === 'torch' ? 'off' : 'torch'; + this.setData({ + flash: newFlash + }); + + wx.showToast({ + title: newFlash === 'torch' ? '闪光灯已开启' : '闪光灯已关闭', + icon: 'none', + duration: 1500 + }); + }, + + // 切换摄像头 + switchCamera: function () { + console.log('切换摄像头'); + const newPosition = this.data.devicePosition === 'back' ? 'front' : 'back'; + this.setData({ + devicePosition: newPosition + }); + + wx.showToast({ + title: `已切换到${newPosition === 'back' ? '后置' : '前置'}摄像头`, + icon: 'none', + duration: 1500 + }); + }, + + // 从相册选择图片识别 + chooseFromAlbum: function () { + console.log('从相册选择图片'); + wx.chooseImage({ + count: 1, + sizeType: ['original'], + sourceType: ['album'], + success: (res) => { + const imagePath = res.tempFilePaths[0]; + this.setData({ + loading: true, + loadingText: '正在识别相册图片...' + }); + this.recognizeQRCode(imagePath); + }, + fail: (err) => { + console.error('选择图片失败:', err); + } + }); + }, + + // 返回按钮 + goBack: function () { + console.log('返回上级页面'); + wx.navigateBack(); + }, + + // 隐藏结果弹窗 + hideResult: function () { + console.log('关闭结果弹窗'); + this.setData({ + showResult: false, + scanResult: '' + }); + }, + + // 复制结果 + copyResult: function () { + wx.setClipboardData({ + data: this.data.scanResult, + success: () => { + wx.showToast({ + title: '已复制到剪贴板', + icon: 'success' + }); + } + }); + }, + + // 处理结果(返回首页处理) + handleResult: function () { + const result = this.data.scanResult; + + // 隐藏结果弹窗 + this.hideResult(); + + // 返回到首页并处理结果 + wx.navigateBack({ + success: () => { + // 获取首页实例并调用处理方法 + const pages = getCurrentPages(); + if (pages.length > 1) { + const prevPage = pages[pages.length - 2]; + if (prevPage.handleScanResult) { + // 直接传递字符串,与首页的handleScanResult方法兼容 + prevPage.handleScanResult(result); + } + } + } + }); + }, + + // 停止事件冒泡 + stopPropagation: function () { + // 阻止事件冒泡 + }, + + // 摄像头准备就绪 + onCameraReady: function (e) { + console.log('摄像头已准备就绪'); + }, + + // 摄像头错误 + onCameraError: function (e) { + console.error('摄像头错误:', e.detail); + + let errorMsg = '摄像头启动失败'; + if (e.detail.errMsg) { + if (e.detail.errMsg.includes('user deny')) { + errorMsg = '用户拒绝了摄像头权限'; + } else if (e.detail.errMsg.includes('not available')) { + errorMsg = '摄像头设备不可用'; + } else { + errorMsg = '摄像头出现错误: ' + e.detail.errMsg; + } + } + + wx.showModal({ + title: '摄像头错误', + content: errorMsg, + showCancel: false, + confirmText: '返回', + success: () => { + this.goBack(); + } + }); + }, + + // 摄像头停止 + onCameraStop: function (e) { + console.log('摄像头已停止'); + this.setData({ + showCamera: false + }); + }, + + // 测试按钮触摸事件 + onTestTouchStart: function() { + this.setData({ testPressed: true }); + }, + onTestTouchEnd: function() { + this.setData({ testPressed: false }); + }, + + // 自定义切换按钮触摸事件 + onCustomSwitchTouchStart: function() { + this.setData({ customSwitchPressed: true }); + }, + onCustomSwitchTouchEnd: function() { + this.setData({ customSwitchPressed: false }); + }, + + // 微信切换按钮触摸事件 + onWechatSwitchTouchStart: function() { + this.setData({ wechatSwitchPressed: true }); + }, + onWechatSwitchTouchEnd: function() { + this.setData({ wechatSwitchPressed: false }); + }, + + // 拍照按钮触摸事件 + onTakePhotoTouchStart: function() { + this.setData({ takePhotoPressed: true }); + }, + onTakePhotoTouchEnd: function() { + this.setData({ takePhotoPressed: false }); + }, + + // 闪光灯按钮触摸事件 + onFlashTouchStart: function() { + this.setData({ flashPressed: true }); + }, + onFlashTouchEnd: function() { + this.setData({ flashPressed: false }); + }, + + // 切换摄像头按钮触摸事件 + onSwitchCameraTouchStart: function() { + this.setData({ switchCameraPressed: true }); + }, + onSwitchCameraTouchEnd: function() { + this.setData({ switchCameraPressed: false }); + }, + + // 相册按钮触摸事件 + onAlbumTouchStart: function() { + this.setData({ albumPressed: true }); + }, + onAlbumTouchEnd: function() { + this.setData({ albumPressed: false }); + }, + + // 返回按钮触摸事件 + onBackTouchStart: function() { + this.setData({ backPressed: true }); + }, + onBackTouchEnd: function() { + this.setData({ backPressed: false }); + }, + + // 关闭按钮触摸事件 + onCloseTouchStart: function() { + this.setData({ closePressed: true }); + }, + onCloseTouchEnd: function() { + this.setData({ closePressed: false }); + }, + + // 复制按钮触摸事件 + onCopyTouchStart: function() { + this.setData({ copyPressed: true }); + }, + onCopyTouchEnd: function() { + this.setData({ copyPressed: false }); + }, + + // 处理按钮触摸事件 + onHandleTouchStart: function() { + this.setData({ handlePressed: true }); + }, + onHandleTouchEnd: function() { + this.setData({ handlePressed: false }); + }, +}); \ No newline at end of file diff --git a/pages/custom-scan/custom-scan.json b/pages/custom-scan/custom-scan.json new file mode 100644 index 0000000..469bbe1 --- /dev/null +++ b/pages/custom-scan/custom-scan.json @@ -0,0 +1,6 @@ +{ + "navigationStyle": "custom", + "backgroundColor": "#000000", + "backgroundTextStyle": "light", + "enablePullDownRefresh": false +} \ No newline at end of file diff --git a/pages/custom-scan/custom-scan.wxml b/pages/custom-scan/custom-scan.wxml new file mode 100644 index 0000000..75275b5 --- /dev/null +++ b/pages/custom-scan/custom-scan.wxml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + 测试按钮 (验证点击) + + + + + + + 自定义 + + + 微信摄像头 + + + + + + 自定义兼容强,微信性能高,标准码建议使用微信摄像头 + + + + + + + + 拍照 + + + + + + {{flash === 'torch' ? '关闭' : '闪光'}} + + + + + + 翻转 + + + + + + 相册 + + + + + + + 返回 + + + + + 摄像头状态: {{showCamera ? '已启动' : '未启动'}} + 当前模式: {{useCustomScan ? '自定义' : '微信API'}} + + + + + + + + 正在启动摄像头... + + + + + + + + + + + + + {{loadingText}} + + + + + + + + 扫码结果 + × + + + + + 扫码方式: + {{useCustomScan ? '自定义摄像头' : '微信摄像头'}} + + + 识别内容: + {{scanResult}} + + + + + + + + + + \ No newline at end of file diff --git a/pages/custom-scan/custom-scan.wxss b/pages/custom-scan/custom-scan.wxss new file mode 100644 index 0000000..cd33f6a --- /dev/null +++ b/pages/custom-scan/custom-scan.wxss @@ -0,0 +1,580 @@ +/* custom-scan.wxss - iOS风格设计 */ + +.container { + width: 100vw; + height: 100vh; + background: #000; + position: relative; + overflow: hidden; +} + + + +/* 摄像头区域 - 全屏占满,避开状态栏 */ +.camera-container { + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: #000; + padding-top: env(safe-area-inset-top); +} + +.camera-preview { + width: 100%; + height: 100%; + object-fit: cover; +} + +.camera-placeholder { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #000; + color: #ffffff; +} + +.placeholder-icon { + width: 80rpx; + height: 80rpx; + margin-bottom: 20rpx; + opacity: 0.6; +} + +.placeholder-text { + font-size: 32rpx; + color: #ffffff; + opacity: 0.8; + font-weight: 500; +} + + + +/* 扫描线容器 - 全屏范围,与摄像头平级 */ +.scan-line-container { + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 800; /* 高于摄像头但低于控制栏 */ + overflow: hidden; + pointer-events: none; /* 确保不会拦截点击事件 */ +} + +/* 扫描线 - 带拖尾效果 */ +.scan-line { + position: absolute; + left: 5%; + right: 5%; + height: 8rpx; + background: linear-gradient(90deg, + transparent 0%, + rgba(0, 255, 136, 0.4) 5%, + rgba(0, 255, 136, 0.8) 20%, + #00ff88 40%, + #ffffff 50%, + #00ff88 60%, + rgba(0, 255, 136, 0.8) 80%, + rgba(0, 255, 136, 0.4) 95%, + transparent 100% + ); + border-radius: 4rpx; + box-shadow: + 0 0 40rpx #00ff88, + 0 0 80rpx rgba(0, 255, 136, 0.7), + 0 0 120rpx rgba(0, 255, 136, 0.4); + animation: scanLineMove 3s ease-in-out infinite; +} + +/* 扫描线移动动画 - 从15%位置开始到控制栏顶部 */ +@keyframes scanLineMove { + 0% { + top: 15%; /* 从上面15%位置开始 */ + opacity: 0; + transform: scaleY(0.5); + } + 5% { + opacity: 1; + transform: scaleY(1); + } + 85% { + opacity: 1; + transform: scaleY(1); + } + 95% { + opacity: 0.3; + transform: scaleY(0.8); + } + 100% { + top: 85%; /* 到控制栏顶部附近消失 */ + opacity: 0; + transform: scaleY(0.5); + } +} + +/* 底部控制栏 - 全屏覆盖层 */ +.control-bar { + position: absolute; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + padding: 20rpx 30rpx; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(20rpx); +} + +/* 隐藏测试区域 - 保持功能但不显示 */ +.test-section { + display: none; /* 隐藏测试按钮,保持iOS简洁 */ +} + +.test-btn { + width: 100%; + font-size: 24rpx; + background: linear-gradient(135deg, #ff4444, #cc0000); + color: #ffffff; + border-radius: 25rpx; + padding: 15rpx; + text-align: center; + border: 2rpx solid rgba(255, 255, 255, 0.2); +} + +.test-btn.pressed { + background: linear-gradient(135deg, #cc0000, #990000); + border-color: rgba(255, 255, 255, 0.4); +} + +/* iOS风格模式切换 */ +.mode-switch { + display: flex; + background: rgba(118, 118, 128, 0.12); + border-radius: 20rpx; + padding: 4rpx; + margin-bottom: 24rpx; + backdrop-filter: blur(20rpx); +} + +.switch-item { + flex: 1; + text-align: center; + padding: 16rpx; + border-radius: 16rpx; + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + cursor: pointer; +} + +.switch-item.active { + background: rgba(255, 255, 255, 0.9); + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15); +} + +.switch-item.active .switch-text { + color: #000000; +} + +.switch-item.pressed { + background: rgba(118, 118, 128, 0.4); +} + +.switch-item.active.pressed { + background: rgba(255, 255, 255, 0.7); +} + +.switch-text { + font-size: 28rpx; + font-weight: 600; + color: #ffffff; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +/* 模式说明文字 */ +.mode-description { + margin-bottom: 30rpx; + text-align: center; +} + +.desc-text { + font-size: 22rpx; + color: rgba(255, 255, 255, 0.7); + background: rgba(0, 0, 0, 0.3); + padding: 8rpx 16rpx; + border-radius: 15rpx; + backdrop-filter: blur(10rpx); + border: 0.5rpx solid rgba(255, 255, 255, 0.1); + line-height: 1.4; +} + +/* iOS风格功能按钮组 */ +.action-buttons { + display: flex; + justify-content: space-around; + align-items: center; + margin-bottom: 24rpx; + gap: 16rpx; +} + +.action-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 120rpx; + height: 120rpx; + background: rgba(118, 118, 128, 0.12); + border-radius: 30rpx; + backdrop-filter: blur(20rpx); + border: 0.5rpx solid rgba(255, 255, 255, 0.1); + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + cursor: pointer; +} + +.action-btn:active { + transform: scale(0.9); + background: rgba(118, 118, 128, 0.3); + opacity: 0.8; +} + +.action-btn.pressed { + background: rgba(118, 118, 128, 0.4); + border-color: rgba(255, 255, 255, 0.3); +} + +.action-btn.primary { + width: 120rpx; /* 统一大小 */ + height: 120rpx; + background: #00C853; + border: none; + box-shadow: 0 4rpx 20rpx rgba(0, 200, 83, 0.3); +} + +.action-btn.primary:active { + background: #00A043; + transform: scale(0.9); + opacity: 0.8; +} + +.action-btn.primary.pressed { + background: #00A043; + box-shadow: 0 2rpx 10rpx rgba(0, 200, 83, 0.4); +} + +.btn-icon { + font-size: 32rpx; + margin-bottom: 8rpx; + color: #ffffff; + opacity: 0.9; +} + +/* 按钮图标图片 */ +.btn-icon-img { + width: 32rpx; + height: 32rpx; + margin-bottom: 8rpx; + opacity: 0.9; +} + +.btn-text { + font-size: 20rpx; + color: #ffffff; + font-weight: 500; + opacity: 0.8; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +/* iOS风格返回按钮 */ +.back-btn { + display: flex; + align-items: center; + justify-content: center; + background: rgba(118, 118, 128, 0.12); + border-radius: 24rpx; + padding: 16rpx 24rpx; + backdrop-filter: blur(20rpx); + cursor: pointer; + margin-bottom: 16rpx; + border: 0.5rpx solid rgba(255, 255, 255, 0.1); + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); +} + +.back-btn:active { + background: rgba(118, 118, 128, 0.2); + transform: scale(0.98); +} + +.back-btn.pressed { + background: rgba(118, 118, 128, 0.4); + border-color: rgba(255, 255, 255, 0.3); +} + +.back-icon { + font-size: 28rpx; + color: #ffffff; + margin-right: 8rpx; + opacity: 0.9; +} + +.back-text { + font-size: 28rpx; + color: #ffffff; + font-weight: 500; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +/* 隐藏调试信息 - 保持功能但不显示 */ +.debug-info { + display: none; /* 隐藏调试信息,保持iOS简洁 */ +} + +.debug-text { + display: block; + font-size: 20rpx; + color: #ffff00; + margin-bottom: 6rpx; + line-height: 1.3; +} + +.debug-text:last-child { + margin-bottom: 0; +} + +/* iOS风格加载提示 - 解决遮挡问题 */ +.loading-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 8000; /* 降低层级,避免遮挡关闭按钮 */ + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(20rpx); +} + +.loading-content { + display: flex; + flex-direction: column; + align-items: center; + background: rgba(58, 58, 60, 0.95); + padding: 40rpx; + border-radius: 24rpx; + backdrop-filter: blur(40rpx); + border: 0.5rpx solid rgba(255, 255, 255, 0.1); + min-width: 200rpx; +} + +.loading-spinner { + width: 40rpx; + height: 40rpx; + border: 3rpx solid rgba(255, 255, 255, 0.2); + border-top: 3rpx solid #ffffff; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20rpx; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.loading-text { + font-size: 28rpx; + color: #ffffff; + text-align: center; + font-weight: 500; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +/* iOS风格结果弹窗 - 浅色主题 */ +.result-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 9000; /* 高于加载层级,确保可以关闭 */ + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx; + backdrop-filter: blur(20rpx); +} + +.result-content { + width: 100%; + max-width: 640rpx; + background: rgba(255, 255, 255, 0.98); + border-radius: 28rpx; + backdrop-filter: blur(40rpx); + overflow: hidden; + border: 0.5rpx solid rgba(0, 0, 0, 0.1); + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3); +} + +.result-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + background: rgba(245, 245, 247, 0.8); + border-bottom: 0.5rpx solid rgba(0, 0, 0, 0.1); +} + +.result-title { + font-size: 34rpx; + font-weight: 600; + color: #1d1d1f; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +.result-close { + font-size: 32rpx; + color: #666666; + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 28rpx; + background: rgba(0, 0, 0, 0.05); + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + cursor: pointer; +} + +.result-close:active { + background: rgba(0, 0, 0, 0.1); + transform: scale(0.95); +} + +.result-close.pressed { + background: rgba(0, 0, 0, 0.2); +} + +.result-body { + padding: 32rpx; + background: rgba(255, 255, 255, 0.95); +} + +.result-item { + margin-bottom: 24rpx; +} + +.result-item:last-child { + margin-bottom: 0; +} + +.result-label { + display: block; + font-size: 24rpx; + color: #666666; + margin-bottom: 8rpx; + font-weight: 500; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +.result-value { + display: block; + font-size: 32rpx; + color: #1d1d1f; + background: rgba(245, 245, 247, 0.8); + padding: 16rpx; + border-radius: 16rpx; + word-break: break-all; + line-height: 1.4; + font-family: 'SF Mono', Monaco, monospace; + border: 1rpx solid rgba(0, 0, 0, 0.05); +} + +.result-actions { + display: flex; + gap: 16rpx; + padding: 0 32rpx 32rpx; + background: rgba(255, 255, 255, 0.95); +} + +.result-btn { + flex: 1; + height: 88rpx; + border-radius: 22rpx; + font-size: 32rpx; + font-weight: 600; + border: none; + transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + cursor: pointer; + font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display'; +} + +.result-btn:active { + transform: scale(0.98); +} + +.result-btn.pressed { + /* 移除transform动画 */ +} + +.result-btn.secondary { + background: rgba(0, 0, 0, 0.05); + color: #1d1d1f; + border: 1rpx solid rgba(0, 0, 0, 0.1); +} + +.result-btn.secondary:active { + background: rgba(0, 0, 0, 0.1); +} + +.result-btn.secondary.pressed { + background: rgba(0, 0, 0, 0.15); + border-color: rgba(0, 0, 0, 0.2); +} + +.result-btn.primary { + background: #00C853; + color: #ffffff; +} + +.result-btn.primary:active { + background: #00A043; +} + +.result-btn.primary.pressed { + background: #00A043; +} + +/* 响应式适配 */ +@media (max-width: 750rpx) { + .scan-box { + width: 400rpx; + height: 400rpx; + } + + .action-buttons { + gap: 12rpx; + } + + .action-btn { + width: 100rpx; + height: 100rpx; + } + + .action-btn.primary { + width: 100rpx; /* 响应式模式下也统一大小 */ + height: 100rpx; + } +} + +/* 深色模式优化 */ +@media (prefers-color-scheme: dark) { + .container { + background: #000000; + } +} \ No newline at end of file diff --git a/pages/generate-barcode/generate-barcode.js b/pages/generate-barcode/generate-barcode.js new file mode 100644 index 0000000..3db9153 --- /dev/null +++ b/pages/generate-barcode/generate-barcode.js @@ -0,0 +1,373 @@ +// 生成条形码页面 - iOS风格 - 混合生成版本(服务器端+本地生成) +const apiService = require('../../utils/api-service.js'); +const MessageUtil = require('../../utils/message-util.js'); + +Page({ + data: { + // 基础数据 + inputContent: '', + batchContent: '', + batchMode: false, + batchLines: 0, + + // 条形码类型 + barcodeType: 'Code 128', + + // 限制设置 + maxLength: 100, + maxBatchLength: 2000, + + // 美化选项 + foregroundColor: '#000000', + backgroundColor: '#ffffff', + barcodeWidth: 300, + + // 状态控制 + generating: false, + + // 离线模式状态 + isOfflineMode: false + }, + + onLoad(options) { + // 从参数中获取类型信息 + if (options.type) { + const type = decodeURIComponent(options.type); + this.setData({ barcodeType: type }); + this.adjustLimits(); + } + + // 检查离线模式 + this.checkOfflineMode(); + }, + + onShow() { + // 页面显示时重新检查离线状态 + this.checkOfflineMode(); + }, + + // 检查离线模式状态 + checkOfflineMode() { + try { + const app = getApp(); + const isOffline = app.isOfflineMode(); + + this.setData({ + isOfflineMode: isOffline + }); + + console.log('条形码生成页面 - 离线模式状态:', isOffline); + } catch (error) { + console.error('检查离线模式失败:', error); + this.setData({ + isOfflineMode: false + }); + } + }, + + // 切换批量模式 + toggleBatchMode() { + const newBatchMode = !this.data.batchMode; + this.setData({ + batchMode: newBatchMode, + inputContent: '', + batchContent: '', + batchLines: 0 + }); + }, + + // 单个输入变化 + onInputChange(e) { + const value = e.detail.value; + this.setData({ inputContent: value }); + this.validateInput(value); + }, + + // 批量输入变化 + onBatchInputChange(e) { + const value = e.detail.value; + const lines = value.split('\n').filter(line => line.trim()).length; + + // 限制行数 + if (lines > 50) { + const limitedLines = value.split('\n').slice(0, 50).join('\n'); + this.setData({ + batchContent: limitedLines, + batchLines: 50 + }); + wx.showToast({ + title: '最多支持50行', + icon: 'none' + }); + return; + } + + this.setData({ + batchContent: value, + batchLines: lines + }); + }, + + // 切换条形码类型 + switchType(e) { + const type = e.currentTarget.dataset.type; + this.setData({ barcodeType: type }); + this.adjustLimits(); + + // 重新验证当前输入 + if (!this.data.batchMode && this.data.inputContent) { + this.validateInput(this.data.inputContent); + } + }, + + // 根据类型调整输入限制 + adjustLimits() { + let maxLength = 100; + + switch (this.data.barcodeType) { + case 'EAN-13': + maxLength = 13; + break; + case 'EAN-8': + maxLength = 8; + break; + case 'Code 39': + maxLength = 80; + break; + case 'ITF': + maxLength = 50; + break; + case 'Code 128': + default: + maxLength = 100; + break; + } + + this.setData({ maxLength }); + }, + + // 输入验证 + validateInput(value) { + const type = this.data.barcodeType; + + // 数字格式验证 + if (type === 'EAN-13' || type === 'EAN-8' || type === 'ITF') { + const numericOnly = value.replace(/[^0-9]/g, ''); + if (numericOnly !== value) { + this.setData({ inputContent: numericOnly }); + } + } + + // Code 39 字符限制 + if (type === 'Code 39') { + const validChars = value.replace(/[^A-Z0-9\-. $\/+%]/g, ''); + if (validChars !== value) { + this.setData({ inputContent: validChars.toUpperCase() }); + } + } + }, + + // 选择颜色 + selectColor(e) { + const { type, color } = e.currentTarget.dataset; + if (type === 'foreground') { + this.setData({ foregroundColor: color }); + } else if (type === 'background') { + this.setData({ backgroundColor: color }); + } + }, + + // 选择尺寸 + selectSize(e) { + const size = parseInt(e.currentTarget.dataset.size); + this.setData({ barcodeWidth: size }); + }, + + // 生成条形码 + async generateBarcode() { + // 防止重复点击 + if (this.data.generating) return; + + // 验证输入 + if (!this.validateBeforeGenerate()) return; + + // 开始生成动画 + this.setData({ generating: true }); + + try { + await this.performGeneration(); + + } catch (error) { + console.error('生成条形码失败:', error); + MessageUtil.showNetworkError(error); + } finally { + // 结束生成动画 + this.setData({ generating: false }); + } + }, + + // 生成前验证 + validateBeforeGenerate() { + if (this.data.batchMode) { + if (!this.data.batchContent.trim()) { + wx.showToast({ + title: '请输入批量内容', + icon: 'none' + }); + return false; + } + + const lines = this.data.batchContent.split('\n').filter(line => line.trim()); + if (lines.length === 0) { + wx.showToast({ + title: '请输入有效内容', + icon: 'none' + }); + return false; + } + + // 验证每行内容 + for (let i = 0; i < lines.length; i++) { + if (!this.validateSingleContent(lines[i].trim())) { + wx.showToast({ + title: `第${i + 1}行内容格式不正确`, + icon: 'none' + }); + return false; + } + } + } else { + if (!this.data.inputContent.trim()) { + wx.showToast({ + title: '请输入条形码内容', + icon: 'none' + }); + return false; + } + + if (!this.validateSingleContent(this.data.inputContent)) { + wx.showToast({ + title: '内容格式不正确', + icon: 'none' + }); + return false; + } + } + + return true; + }, + + // 验证单个内容 + validateSingleContent(content) { + const type = this.data.barcodeType; + + switch (type) { + case 'EAN-13': + return /^\d{13}$/.test(content); + case 'EAN-8': + return /^\d{8}$/.test(content); + case 'ITF': + return /^\d+$/.test(content) && content.length % 2 === 0; + case 'Code 39': + return /^[A-Z0-9\-. $\/+%]+$/.test(content); + case 'Code 128': + default: + return content.length > 0 && content.length <= this.data.maxLength; + } + }, + + // 执行生成 + async performGeneration() { + // 统一跳转到结果页面,由结果页面根据离线状态选择生成方式 + const generateData = { + content: this.data.batchMode ? + this.data.batchContent.split('\n').filter(line => line.trim()) : + this.data.inputContent, + barcodeType: this.data.barcodeType, + foregroundColor: this.data.foregroundColor, + backgroundColor: this.data.backgroundColor, + barcodeWidth: this.data.barcodeWidth, + batchMode: this.data.batchMode + }; + + // 保存到历史记录 + this.saveToHistory(); + + // 跳转到结果页面 + const dataParam = encodeURIComponent(JSON.stringify(generateData)); + wx.navigateTo({ + url: '/pages/generate-result/generate-result?data=' + dataParam + }); + }, + + + + // 服务器生成条形码 + async generateWithServer() { + try { + const content = this.data.batchMode ? + this.data.batchContent.split('\n').filter(line => line.trim()) : + this.data.inputContent; + + const params = { + codeType: 'barcode', + subType: this.data.barcodeType, + content: content, + batchMode: this.data.batchMode, + options: { + foregroundColor: this.data.foregroundColor, + backgroundColor: this.data.backgroundColor, + size: this.data.barcodeWidth + } + }; + + // 调用API服务 + const response = await apiService.generateCode(params); + + // 保存到历史记录 + this.saveToHistory(); + + if (this.data.batchMode) { + // 批量生成,保存压缩包 + const saveResult = await apiService.saveBatchResult(response.data); + // 跳转到结果页面 + wx.navigateTo({ + url: '/pages/generate-result/generate-result?type=batch&path=' + encodeURIComponent(saveResult.path) + }); + } else { + // 单个生成,保存图片 + const saveResult = await apiService.saveImageToAlbum(response.data); + // 跳转到结果页面 + wx.navigateTo({ + url: '/pages/generate-result/generate-result?type=single&path=' + encodeURIComponent(saveResult.path) + }); + } + } catch (error) { + throw error; + } + }, + + + + // 保存到历史记录 + saveToHistory() { + const historyManager = require('../../utils/history-manager.js'); + const timestamp = new Date().getTime(); + + const data = { + batchMode: this.data.batchMode, + batchContent: this.data.batchContent, + inputContent: this.data.inputContent, + barcodeType: this.data.barcodeType, + foregroundColor: this.data.foregroundColor, + backgroundColor: this.data.backgroundColor, + barcodeWidth: this.data.barcodeWidth, + timestamp: timestamp, + localGenerated: this.data.isOfflineMode + }; + + historyManager.saveBarcodeRecord(data); + }, + + +}); \ No newline at end of file diff --git a/pages/generate-barcode/generate-barcode.json b/pages/generate-barcode/generate-barcode.json new file mode 100644 index 0000000..e9ce31f --- /dev/null +++ b/pages/generate-barcode/generate-barcode.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "小籽二维码", + "backgroundTextStyle": "dark", + "backgroundColor": "#f8f9fa" +} \ No newline at end of file diff --git a/pages/generate-barcode/generate-barcode.wxml b/pages/generate-barcode/generate-barcode.wxml new file mode 100644 index 0000000..ba0e230 --- /dev/null +++ b/pages/generate-barcode/generate-barcode.wxml @@ -0,0 +1,190 @@ + + + + + 生成条形码 + + + + + + + 输入内容 + + {{batchMode ? '单个' : '批量'}} + + + + + + + + + + + + {{batchMode ? batchLines + '/50行' : inputContent.length + '/' + maxLength + '字'}} + + + + + + + 条形码类型 + + + Code 128 + 通用格式 + + + EAN-13 + 商品条码 + + + EAN-8 + 短商品码 + + + Code 39 + 字母数字 + + + ITF + 数字格式 + + + + + + + 美化选项 + + + + 条形码颜色 + + + + + + + + + + + + + + + + + + + + + 背景色 + + + + + + + + + + + + + + + + + + + + + 尺寸大小 + + + + 200px + + + 标准 + 300px + + + + 400px + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/generate-barcode/generate-barcode.wxss b/pages/generate-barcode/generate-barcode.wxss new file mode 100644 index 0000000..e647faf --- /dev/null +++ b/pages/generate-barcode/generate-barcode.wxss @@ -0,0 +1,524 @@ +/* 生成条形码页面样式 - iOS风格 */ + +/* 主容器 */ +.container { + min-height: 100vh; + background: linear-gradient(180deg, + #f0f9ff 0%, + #f8fafc 50%, + #f1f5f9 100%); +} + +/* 标题区域 */ +.header-section { + padding: 20rpx 40rpx 20rpx; + text-align: center; +} + +.page-title { + font-size: 50rpx; + font-weight: 700; + color: #1e293b; + line-height: 1.2; + margin-bottom: 16rpx; +} + +.page-subtitle { + font-size: 26rpx; + color: #64748b; + line-height: 1.5; +} + +.page-subtitle text { + display: block; +} + +/* 输入区域 */ +.input-section { + padding: 0 40rpx 40rpx; +} + +.input-card { + background: white; + border-radius: 24rpx; + padding: 32rpx; + box-shadow: 0 4rpx 32rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.02); +} + +.input-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; +} + +.input-title { + font-size: 32rpx; + font-weight: 600; + color: #1e293b; +} + +/* 批量模式开关 */ +.batch-toggle { + display: flex; + align-items: center; + gap: 16rpx; +} + +.toggle-text { + font-size: 26rpx; + color: #64748b; + font-weight: 500; +} + +.toggle-switch { + width: 80rpx; + height: 40rpx; + background: #e2e8f0; + border-radius: 20rpx; + position: relative; + transition: all 0.3s ease; +} + +.toggle-switch.active { + background: linear-gradient(135deg, #22c55e, #16a34a); +} + +.toggle-circle { + width: 32rpx; + height: 32rpx; + background: white; + border-radius: 50%; + position: absolute; + top: 4rpx; + left: 4rpx; + transition: all 0.3s ease; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); +} + +.toggle-switch.active .toggle-circle { + left: 44rpx; +} + +/* 输入框样式 */ +.input-area { + width: 100%; + min-height: 120rpx; + padding: 24rpx; + background: #f8fafc; + border: 2rpx solid #e2e8f0; + border-radius: 16rpx; + font-size: 28rpx; + color: #1e293b; + line-height: 1.5; + box-sizing: border-box; +} + +.input-area.batch-input { + min-height: 200rpx; +} + +.input-footer { + margin-top: 16rpx; + text-align: right; +} + +.char-count { + font-size: 24rpx; + color: #94a3b8; +} + +/* 类型选择区域 */ +.type-section { + padding: 0 40rpx 40rpx; +} + +.section-title { + font-size: 32rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 24rpx; + padding-left: 8rpx; +} + +.type-grid { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 16rpx; +} + +.type-grid .type-card:first-child { + grid-column: 1 / -1; +} + +.type-card { + background: white; + border-radius: 16rpx; + padding: 20rpx 16rpx; + text-align: center; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + border: 2rpx solid transparent; + transition: all 0.3s ease; + min-height: 80rpx; + display: flex; + flex-direction: column; + justify-content: center; +} + +.type-card.active { + border-color: #22c55e; + box-shadow: 0 4rpx 24rpx rgba(34, 197, 94, 0.15); +} + +.type-name { + font-size: 24rpx; + font-weight: 600; + color: #1e293b; + display: block; + margin-bottom: 4rpx; +} + +.type-desc { + font-size: 20rpx; + color: #64748b; + display: block; +} + +/* 美化选项区域 */ +.beautify-section { + padding: 0 40rpx 40rpx; +} + +.beautify-options { + background: white; + border-radius: 24rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.02); +} + +.option-group { + margin-bottom: 40rpx; +} + +.option-group:last-child { + margin-bottom: 0; +} + +.option-label { + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 20rpx; + display: block; +} + +/* 颜色选择 */ +.color-scroll { + width: 100%; +} + +.color-list { + display: flex; + gap: 24rpx; + padding: 8rpx; +} + +.color-item { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + position: relative; + transition: all 0.3s ease; + flex-shrink: 0; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); +} + +.color-item:active { + transform: scale(0.9); +} + +.color-item.active { + transform: scale(1.1); + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2); +} + +.color-item.active::after { + content: '✓'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 24rpx; + font-weight: bold; + text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5); +} + +/* 前景色对勾 - 白色 */ +.foreground-color.active::after { + color: white; +} + +/* 背景色对勾 - 黑色 */ +.background-color.active::after { + color: black; + text-shadow: 0 1rpx 2rpx rgba(255, 255, 255, 0.5); +} + +/* 尺寸选择 */ +.size-options { + display: flex; + gap: 16rpx; +} + +.size-item { + flex: 1; + background: #f9fafb; + border: 2rpx solid #e5e7eb; + border-radius: 16rpx; + padding: 24rpx 16rpx; + text-align: center; + transition: all 0.3s ease; +} + +.size-item:active { + transform: scale(0.98); +} + +.size-item.active { + border-color: #22c55e; + background: linear-gradient(135deg, rgba(34, 197, 94, 0.05), rgba(22, 163, 74, 0.02)); + box-shadow: 0 2rpx 8rpx rgba(34, 197, 94, 0.15); +} + +.size-text { + display: block; + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 8rpx; +} + +.size-desc { + font-size: 24rpx; + color: #6b7280; +} + +/* 生成按钮区域 */ +.generate-section { + padding: 0 40rpx 40rpx; +} + +.generate-button { + width: 100% !important; + height: 120rpx; + margin:0 0; + background: linear-gradient(135deg, #22c55e, #16a34a); + border-radius: 20rpx; + border: none; + color: white; + font-size: 0; + position: relative; + overflow: hidden; + transition: all 0.3s ease; + box-shadow: 0 8rpx 24rpx rgba(34, 197, 94, 0.3); +} + +.generate-button:active { + transform: scale(0.98); + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.4); +} + +.generate-button.generating { + background: linear-gradient(135deg, #9ca3af, #6b7280); + box-shadow: 0 4rpx 16rpx rgba(156, 163, 175, 0.3); +} + +.button-normal, .button-loading { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 12rpx; +} + +.button-icon { + width: 50rpx; + height: 50rpx; + filter: brightness(0) invert(1); +} + +.button-text { + font-size: 40rpx; + font-weight: 600; + color: white; + line-height: 1; + margin: 0; + padding: 0; +} + +/* 加载动画 */ +.loading-spinner { + width: 32rpx; + height: 32rpx; + border: 3rpx solid rgba(255, 255, 255, 0.3); + border-top: 3rpx solid white; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 底部安全区域 */ +.bottom-safe-area { + height: 120rpx; +} + +/* 本地生成结果弹窗 */ +.result-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + backdrop-filter: blur(4rpx); +} + +.result-modal.show { + opacity: 1; + visibility: visible; +} + +.result-content { + width: 90%; + max-width: 600rpx; + background: white; + border-radius: 28rpx; + overflow: hidden; + animation: resultShow 0.3s ease-out; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15); +} + +@keyframes resultShow { + 0% { + opacity: 0; + transform: scale(0.8) translateY(40rpx); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.result-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 40rpx 40rpx 20rpx; + border-bottom: 1rpx solid #f0f0f0; +} + +.result-title { + font-size: 36rpx; + font-weight: bold; + color: #333333; +} + +.result-close { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: #666666; + transition: all 0.3s ease; +} + +.result-close:active { + background: #e5e5e5; + transform: scale(0.9); +} + +.result-body { + padding: 40rpx; + text-align: center; +} + +.result-barcode-image { + width: 100%; + max-width: 400rpx; + height: 160rpx; + border-radius: 16rpx; + margin-bottom: 24rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); +} + +.result-desc { + font-size: 28rpx; + color: #666666; + line-height: 1.5; +} + +.result-actions { + display: flex; + gap: 16rpx; + padding: 32rpx; + border-top: 1rpx solid #f1f5f9; +} + +.result-btn { + flex: 1; + height: 80rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 500; + transition: all 0.3s ease; + margin: 0; + padding: 0; + border: none; + background: none; +} + +.result-btn.primary { + background: linear-gradient(135deg, #22c55e, #16a34a); + color: white; + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.3); +} + +.result-btn.primary:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 8rpx rgba(34, 197, 94, 0.3); +} + +.result-btn.secondary { + background: #f8fafc; + color: #475569; + border: 1rpx solid #e2e8f0; +} + +.result-btn.secondary:active { + background: #e2e8f0; +} \ No newline at end of file diff --git a/pages/generate-qr/generate-qr.js b/pages/generate-qr/generate-qr.js new file mode 100644 index 0000000..7a34342 --- /dev/null +++ b/pages/generate-qr/generate-qr.js @@ -0,0 +1,357 @@ +// 生成二维码页面 - iOS风格 - 混合生成版本(服务器端+本地生成) +const apiService = require('../../utils/api-service.js'); +const MessageUtil = require('../../utils/message-util.js'); +const drawQrcode = require('../../utils/qrcode.js'); + + +Page({ + data: { + // 输入内容 + inputContent: '', + batchContent: '', + batchMode: false, + batchLines: 0, + + // 二维码类型 + qrType: 'standard', + + // 美化选项 + foregroundColor: '#000000', + backgroundColor: '#ffffff', + qrSize: 300, + + // 生成状态 + generating: false, + + // 输入限制 + maxLength: 1000, + maxBatchLength: 5000, + + // 离线模式状态 + isOfflineMode: false + }, + + onLoad(options) { + if (options.type) { + this.setData({ + qrType: options.type + }); + } + + // 检查离线模式 + this.checkOfflineMode(); + }, + + onShow() { + // 页面显示时重新检查离线状态 + this.checkOfflineMode(); + }, + + // 检查离线模式状态 + checkOfflineMode() { + try { + const app = getApp(); + const isOffline = app.isOfflineMode(); + + this.setData({ + isOfflineMode: isOffline + }); + + console.log('二维码生成页面 - 离线模式状态:', isOffline); + } catch (error) { + console.error('检查离线模式失败:', error); + this.setData({ + isOfflineMode: false + }); + } + }, + + // 切换批量模式 + toggleBatchMode() { + const newBatchMode = !this.data.batchMode; + this.setData({ + batchMode: newBatchMode + }); + + // 清空输入内容 + if (newBatchMode) { + this.setData({ + batchContent: '', + batchLines: 0 + }); + } else { + this.setData({ + inputContent: '' + }); + } + }, + + // 单个输入变化 + onInputChange(e) { + this.setData({ + inputContent: e.detail.value + }); + }, + + // 批量输入变化 + onBatchInputChange: function(e) { + var content = e.detail.value; + var lines = content.split('\n').filter(function(line) { + return line.trim() !== ''; + }); + + this.setData({ + batchContent: content, + batchLines: lines.length + }); + }, + + // 切换二维码类型 + switchType: function(e) { + var type = e.currentTarget.dataset.type; + + // 如果在离线模式下选择微型码,给出提示 + if (type === 'micro' && this.data.isOfflineMode) { + wx.showModal({ + title: '功能限制', + content: '离线模式下暂不支持微型二维码生成。\n\n微型二维码需要在线服务支持,请等待网络恢复后使用。', + confirmText: '知道了', + showCancel: false + }); + return; + } + + // 根据类型设置不同的输入限制 + var maxLength, maxBatchLength; + if (type === 'micro') { + // 微型码容量较小 (Micro QR Code 最大支持24个字符) + maxLength = 24; // Micro QR Code 最大容量 + maxBatchLength = 1200; // 50 * 24 + + // 显示 Micro QR Code 说明 + wx.showModal({ + title: 'Micro QR Code 说明', + content: 'Micro QR Code 是更小的二维码格式,但容量有限:\n• 最多支持24个字符\n• 适合简短文本、数字\n• 扫描距离较近\n• 部分扫码器可能不支持', + confirmText: '我知道了', + showCancel: false + }); + } else { + // 标准码容量较大 + maxLength = 1000; + maxBatchLength = 5000; + } + + this.setData({ + qrType: type, + maxLength: maxLength, + maxBatchLength: maxBatchLength + }); + }, + + // 选择颜色 + selectColor: function(e) { + var type = e.currentTarget.dataset.type; + var color = e.currentTarget.dataset.color; + if (type === 'foreground') { + this.setData({ + foregroundColor: color + }); + } else if (type === 'background') { + this.setData({ + backgroundColor: color + }); + } + }, + + // 选择尺寸 + selectSize: function(e) { + var size = parseInt(e.currentTarget.dataset.size); + this.setData({ + qrSize: size + }); + }, + + // 生成二维码 + generateQR: function() { + if (this.data.generating) return; + + // 检查离线模式和微型码的组合 + if (this.data.isOfflineMode && this.data.qrType === 'micro') { + wx.showModal({ + title: '功能限制', + content: '离线模式下暂不支持微型二维码生成。\n\n您可以:\n• 切换为标准二维码\n• 或等待网络恢复后使用微型二维码', + confirmText: '切换标准码', + cancelText: '取消', + success: (res) => { + if (res.confirm) { + // 用户选择切换为标准二维码 + this.setData({ + qrType: 'standard', + maxLength: 1000, + maxBatchLength: 5000 + }); + + wx.showToast({ + title: '已切换为标准二维码', + icon: 'success', + duration: 1500 + }); + } + } + }); + return; + } + + // 验证输入 + if (this.data.batchMode) { + if (!this.data.batchContent.trim()) { + wx.showToast({ + title: '请输入批量内容', + icon: 'none' + }); + return; + } + + var lines = this.data.batchContent.split('\n').filter(function(line) { + return line.trim() !== ''; + }); + if (lines.length === 0) { + wx.showToast({ + title: '请输入有效内容', + icon: 'none' + }); + return; + } + + if (lines.length > 50) { + wx.showToast({ + title: '批量生成最多50个', + icon: 'none' + }); + return; + } + + // 检查微型码的单行长度限制 + if (this.data.qrType === 'micro') { + var longLine = lines.find(function(line) { + return line.length > 24; + }); + if (longLine) { + wx.showToast({ + title: 'Micro QR Code 每行最多24个字符', + icon: 'none' + }); + return; + } + } + } else { + if (!this.data.inputContent.trim()) { + wx.showToast({ + title: '请输入内容', + icon: 'none' + }); + return; + } + } + + // 开始生成 + this.setData({ generating: true }); + + var self = this; + + // 统一跳转到结果页面,由结果页面根据离线状态选择生成方式 + var generateData = { + content: this.data.batchMode ? this.data.batchContent : this.data.inputContent, + type: this.data.qrType, + foregroundColor: this.data.foregroundColor, + backgroundColor: this.data.backgroundColor, + size: this.data.qrSize, + batchMode: this.data.batchMode + }; + + // 保存生成历史 + this.saveGenerateHistory(generateData); + + // 跳转到结果页面 + var dataParam = encodeURIComponent(JSON.stringify(generateData)); + wx.navigateTo({ + url: '/pages/generate-result/generate-result?data=' + dataParam, + success: function() { + self.setData({ generating: false }); + }, + fail: function() { + self.setData({ generating: false }); + } + }); + }, + + + + // 使用服务器端生成 + generateWithServer: async function() { + try { + const content = this.data.batchMode ? + this.data.batchContent.split('\n').filter(line => line.trim()) : + this.data.inputContent; + + const params = { + codeType: 'qrcode', + subType: this.data.qrType, + content: content, + batchMode: this.data.batchMode, + options: { + foregroundColor: this.data.foregroundColor, + backgroundColor: this.data.backgroundColor, + size: this.data.qrSize + } + }; + + // 调用API服务 + const response = await apiService.generateCode(params); + + if (this.data.batchMode) { + // 批量生成,保存压缩包 + const saveResult = await apiService.saveBatchResult(response.data); + return { + isBatch: true, + filePath: saveResult.path, + fileName: saveResult.fileName + }; + } else { + // 单个生成,保存图片 + const saveResult = await apiService.saveImageToAlbum(response.data); + return { + isBatch: false, + filePath: saveResult.path + }; + } + } catch (error) { + throw error; + } + }, + + // 模拟生成过程 + simulateGeneration: function() { + return new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, 1500); // 模拟1.5秒生成时间 + }); + }, + + // 保存生成历史 + saveGenerateHistory: function(params) { + const historyManager = require('../../utils/history-manager.js'); + historyManager.saveGenerateHistory(params); + }, + + + + // 页面分享 + onShareAppMessage() { + return { + title: '小籽二维码 - 生成二维码', + path: '/pages/generate-qr/generate-qr' + }; + } +}); \ No newline at end of file diff --git a/pages/generate-qr/generate-qr.json b/pages/generate-qr/generate-qr.json new file mode 100644 index 0000000..e9ce31f --- /dev/null +++ b/pages/generate-qr/generate-qr.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "小籽二维码", + "backgroundTextStyle": "dark", + "backgroundColor": "#f8f9fa" +} \ No newline at end of file diff --git a/pages/generate-qr/generate-qr.wxml b/pages/generate-qr/generate-qr.wxml new file mode 100644 index 0000000..154475e --- /dev/null +++ b/pages/generate-qr/generate-qr.wxml @@ -0,0 +1,188 @@ + + + + + 生成二维码 + + + + + + + 输入内容 + + {{batchMode ? '单个' : '批量'}} + + + + + + + + + + + + {{batchMode ? batchLines + '/50行' : inputContent.length + '/' + maxLength + '字'}} + + + + + + + + 二维码类型 + + + + + + + 标准码 + 适用于大部分场景 + + + + + + 微型码 + {{isOfflineMode ? '离线不可用' : '体积更小,信息有限'}} + + + + + + + + + + + 美化选项 + + + + + 前景色 + + + + + + + + + + + + + + + + + + + + + 背景色 + + + + + + + + + + + + + + + + + + + + + 尺寸大小 + + + + 200px + + + + 300px + + + + 400px + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/generate-qr/generate-qr.wxss b/pages/generate-qr/generate-qr.wxss new file mode 100644 index 0000000..c65af23 --- /dev/null +++ b/pages/generate-qr/generate-qr.wxss @@ -0,0 +1,574 @@ +/* 生成二维码页面样式 - iOS风格 */ +.container { + min-height: 100vh; + background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%); +} + +/* 标题区域 */ +.header-section { + padding: 20rpx 40rpx 20rpx; + text-align: center; +} + +.page-title { + font-size: 50rpx; + font-weight: 700; + color: #1d1d1f; + margin-bottom: 20rpx; + letter-spacing: -1rpx; +} + +.page-subtitle { + display: flex; + flex-direction: column; + gap: 4rpx; +} + +.page-subtitle text { + font-size: 28rpx; + color: #6b7280; + font-weight: 400; + line-height: 1.3; +} + +/* 输入区域 */ +.input-section { + padding: 0 40rpx 40rpx; +} + +.input-card { + background: white; + border-radius: 24rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.02); +} + +.input-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; +} + +.input-title { + font-size: 32rpx; + font-weight: 600; + color: #1d1d1f; +} + +/* 批量模式切换开关 */ +.batch-toggle { + display: flex; + align-items: center; + gap: 16rpx; +} + +.toggle-text { + font-size: 28rpx; + color: #6b7280; + font-weight: 500; +} + +.toggle-switch { + width: 80rpx; + height: 40rpx; + background: #e5e7eb; + border-radius: 20rpx; + position: relative; + transition: all 0.3s ease; +} + +.toggle-switch.active { + background: linear-gradient(135deg, #22c55e, #16a34a); +} + +.toggle-circle { + width: 32rpx; + height: 32rpx; + background: white; + border-radius: 50%; + position: absolute; + top: 4rpx; + left: 4rpx; + transition: all 0.3s ease; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15); +} + +.toggle-switch.active .toggle-circle { + transform: translateX(40rpx); +} + +/* 输入框样式 */ +.input-area { + width: 100%; + padding: 24rpx; + border: 2rpx solid #e5e7eb; + border-radius: 20rpx; + font-size: 28rpx; + background: #f9fafb; + color: #1d1d1f; + transition: all 0.3s ease; + box-sizing: border-box; +} + +.input-area:focus { + border-color: #22c55e; + background: white; + box-shadow: 0 0 0 6rpx rgba(34, 197, 94, 0.1); +} + +.single-input { + min-height: 200rpx; +} + +.batch-input { + min-height: 300rpx; + font-family: monospace; + line-height: 1.6; +} + +.input-footer { + display: flex; + justify-content: flex-end; + margin-top: 16rpx; +} + +.char-count { + font-size: 24rpx; + color: #9ca3af; +} + +/* 类型选择区域 */ +.type-section { + padding: 0 40rpx 40rpx; +} + +.section-title { + font-size: 32rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 24rpx; + padding-left: 8rpx; +} + +.type-cards { + display: flex; + gap: 24rpx; +} + +.type-card { + flex: 1; + background: white; + border-radius: 20rpx; + padding: 32rpx 24rpx; + text-align: center; + border: 2rpx solid #e5e7eb; + transition: all 0.3s ease; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.02); +} + +.type-card:active { + transform: scale(0.98); +} + +.type-card.active { + border-color: #22c55e; + background: linear-gradient(135deg, rgba(34, 197, 94, 0.05), rgba(22, 163, 74, 0.02)); + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.15); +} + +.type-icon { + margin-bottom: 16rpx; + display: flex; + justify-content: center; + align-items: center; + height: 48rpx; +} + +.type-icon-img { + width: 50rpx; + height: 50rpx; +} + +.type-name { + display: block; + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 8rpx; +} + +.type-desc { + font-size: 24rpx; + color: #6b7280; +} + +/* 禁用状态的类型卡片 */ +.type-card.disabled { + opacity: 0.5; + position: relative; + pointer-events: none; +} + +.type-card.disabled .type-name { + color: #9ca3af; +} + +.type-card.disabled .type-desc { + color: #d1d5db; +} + +.disabled-overlay { + position: absolute; + top: 8rpx; + right: 8rpx; + width: 32rpx; + height: 32rpx; + background: rgba(239, 68, 68, 0.1); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.disabled-icon { + width: 20rpx; + height: 20rpx; + opacity: 0.7; +} + +/* 美化选项区域 */ +.beautify-section { + padding: 0 40rpx 40rpx; +} + +.beautify-options { + background: white; + border-radius: 24rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.02); +} + +.option-group { + margin-bottom: 40rpx; +} + +.option-group:last-child { + margin-bottom: 0; +} + +.option-label { + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 20rpx; + display: block; +} + +/* 颜色选择 */ +.color-scroll { + width: 100%; +} + +.color-list { + display: flex; + gap: 24rpx; + padding: 8rpx; +} + +.color-item { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + position: relative; + transition: all 0.3s ease; + flex-shrink: 0; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); +} + +.color-item:active { + transform: scale(0.9); +} + +.color-item.active { + transform: scale(1.1); + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2); +} + +.color-item.active::after { + content: '✓'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 24rpx; + font-weight: bold; + text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5); +} + +/* 前景色对勾 - 白色 */ +.foreground-color.active::after { + color: white; +} + +/* 背景色对勾 - 黑色 */ +.background-color.active::after { + color: black; + text-shadow: 0 1rpx 2rpx rgba(255, 255, 255, 0.5); +} + +/* 尺寸选择 */ +.size-options { + display: flex; + gap: 16rpx; +} + +.size-item { + flex: 1; + background: #f9fafb; + border: 2rpx solid #e5e7eb; + border-radius: 16rpx; + padding: 24rpx 16rpx; + text-align: center; + transition: all 0.3s ease; +} + +.size-item:active { + transform: scale(0.98); +} + +.size-item.active { + border-color: #22c55e; + background: linear-gradient(135deg, rgba(34, 197, 94, 0.05), rgba(22, 163, 74, 0.02)); + box-shadow: 0 2rpx 8rpx rgba(34, 197, 94, 0.15); +} + +.size-text { + display: block; + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 8rpx; +} + +.size-desc { + font-size: 24rpx; + color: #6b7280; +} + +/* 生成按钮区域 */ +.generate-section { + padding: 0 40rpx 40rpx; +} + +.generate-button { + width: 100% !important; + height: 120rpx; + margin:0 0; + background: linear-gradient(135deg, #22c55e, #16a34a); + border-radius: 20rpx; + border: none; + color: white; + font-size: 0; + position: relative; + overflow: hidden; + transition: all 0.3s ease; + box-shadow: 0 8rpx 24rpx rgba(34, 197, 94, 0.3); +} + +.generate-button:active { + transform: scale(0.98); + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.4); +} + +.generate-button.generating { + background: linear-gradient(135deg, #9ca3af, #6b7280); + box-shadow: 0 4rpx 16rpx rgba(156, 163, 175, 0.3); +} + +.button-normal, .button-loading { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 12rpx; +} + +.button-icon { + width: 50rpx; + height: 50rpx; + filter: brightness(0) invert(1); +} + +.button-text { + font-size: 40rpx; + font-weight: 600; + color: white; + line-height: 1; + margin: 0; + padding: 0; +} + +.loading-spinner { + width: 32rpx; + height: 32rpx; + border: 3rpx solid rgba(255, 255, 255, 0.3); + border-top: 3rpx solid white; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 底部安全区域 */ +.bottom-safe-area { + height: 120rpx; +} + +/* 本地生成结果弹窗 */ +.result-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + backdrop-filter: blur(4rpx); +} + +.result-modal.show { + opacity: 1; + visibility: visible; +} + +.result-content { + width: 90%; + max-width: 600rpx; + background: white; + border-radius: 28rpx; + overflow: hidden; + animation: resultShow 0.3s ease-out; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15); +} + +@keyframes resultShow { + 0% { + opacity: 0; + transform: scale(0.8) translateY(40rpx); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.result-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 40rpx 40rpx 20rpx; + border-bottom: 1rpx solid #f0f0f0; +} + +.result-title { + font-size: 36rpx; + font-weight: bold; + color: #333333; +} + +.result-close { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: #666666; + transition: all 0.3s ease; +} + +.result-close:active { + background: #e5e5e5; + transform: scale(0.9); +} + +.result-body { + padding: 40rpx; + text-align: center; +} + +.result-qr-image { + width: 300rpx; + height: 300rpx; + border-radius: 16rpx; + margin-bottom: 24rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); +} + +.result-desc { + font-size: 28rpx; + color: #666666; + line-height: 1.5; +} + +.result-actions { + display: flex; + gap: 16rpx; + padding: 32rpx; + border-top: 1rpx solid #f1f5f9; +} + +.result-btn { + flex: 1; + height: 80rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 500; + transition: all 0.3s ease; + margin: 0; + padding: 0; + border: none; + background: none; +} + +.result-btn.primary { + background: linear-gradient(135deg, #22c55e, #16a34a); + color: white; + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.3); +} + +.result-btn.primary:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 8rpx rgba(34, 197, 94, 0.3); +} + +.result-btn.secondary { + background: #f8fafc; + color: #475569; + border: 1rpx solid #e2e8f0; +} + +.result-btn.secondary:active { + background: #e2e8f0; +} \ No newline at end of file diff --git a/pages/generate-result/generate-result.js b/pages/generate-result/generate-result.js new file mode 100644 index 0000000..0d71954 --- /dev/null +++ b/pages/generate-result/generate-result.js @@ -0,0 +1,1037 @@ +// 码生成结果页面(支持二维码和条形码) +const drawQrcode = require('../../utils/qrcode.js'); +const apiService = require('../../utils/api-service.js'); +const MessageUtil = require('../../utils/message-util.js'); +const JsBarcodeUtil = require('../../utils/jsbarcode-util.js'); + +Page({ + data: { + // 基础数据 + content: '', + codeType: 'barcode', // 'barcode' 或 'qr' + barcodeType: '', + qrType: '', + typeText: '', + batchMode: false, + barcodes: [], + + // 样式设置 + foregroundColor: '#000000', + backgroundColor: '#ffffff', + barcodeWidth: 300, + qrSize: 300, + + // Canvas 尺寸 + canvasWidth: 300, + canvasHeight: 120, + + // 缩放相关 + scaleRatio: 1, + batchScaleRatio: 1, + screenWidth: 0, // 初始为0,将在onLoad时动态获取 + + // 状态控制 + loading: false, + loadingText: '正在生成...', + + // 离线模式状态 + isOfflineMode: false + }, + + onLoad(options) { + console.log('码结果页面参数:', options); + + // 同步获取屏幕信息(必须在其他计算之前完成) + this.initScreenInfo(); + + // 检查离线模式状态 + this.checkOfflineMode(); + + if (options.data) { + try { + const data = JSON.parse(decodeURIComponent(options.data)); + console.log('解析的数据:', data); + + // 判断码类型 + const isQR = data.type === 'standard' || data.type === 'micro' || data.qrType || !data.barcodeType; + const codeType = isQR ? 'qr' : 'barcode'; + + // 处理批量数据 + let barcodes = []; + if (data.batchMode) { + if (isQR) { + // 二维码批量模式,需要从内容中解析 + const lines = data.content.split('\n').filter(line => line.trim()); + barcodes = lines.map(line => ({ content: line.trim() })); + } else { + // 条形码批量模式,内容可能是数组或字符串 + if (Array.isArray(data.content)) { + barcodes = data.content.map(item => ({ content: item })); + } else { + const lines = data.content.split('\n').filter(line => line.trim()); + barcodes = lines.map(line => ({ content: line.trim() })); + } + } + } + + this.setData({ + content: Array.isArray(data.content) ? data.content[0] : data.content || '', + codeType: codeType, + barcodeType: data.barcodeType || 'Code 128', + qrType: data.type || 'standard', + typeText: this.getTypeText(isQR ? data.type : data.barcodeType), + batchMode: data.batchMode || false, + barcodes: barcodes, + foregroundColor: data.foregroundColor || '#000000', + backgroundColor: data.backgroundColor || '#ffffff', + barcodeWidth: data.barcodeWidth || 300, + qrSize: data.size || 300 + }); + + // 设置Canvas尺寸和缩放比例(此时screenWidth已经是真实值) + this.setCanvasSize(); + + // 保存到历史记录 + this.saveToHistory(data); + + // 延迟绘制码 + setTimeout(() => { + if (this.data.isOfflineMode) { + console.log('离线模式,使用本地生成'); + // 检查是否为批量生成 + if (data.batchMode) { + MessageUtil.showError('离线模式下暂不支持批量生成'); + return; + } + + if (isQR) { + this.generateQRLocally(data); + } else { + this.generateBarcodeLocally(data); + } + } else { + console.log('在线模式,使用服务器生成'); + // 在线模式应该通过服务器API生成,这里应该通过API调用获得结果 + // 由于当前服务器接口未提供,这里会报错,这是正确的行为 + this.generateWithServer(data).catch(error => { + console.error('服务器生成失败:', error); + MessageUtil.showNetworkError(error); + }); + } + }, 500); + + } catch (error) { + console.error('解析参数失败:', error); + wx.showToast({ + title: '参数错误', + icon: 'none' + }); + } + } + }, + + // 初始化屏幕信息(同步执行,确保后续计算准确) + initScreenInfo() { + try { + const systemInfo = wx.getSystemInfoSync(); + const screenWidth = systemInfo.screenWidth || systemInfo.windowWidth || 375; + + console.log('屏幕信息初始化:', { + screenWidth: screenWidth, + screenHeight: systemInfo.screenHeight, + pixelRatio: systemInfo.pixelRatio, + system: systemInfo.system, + model: systemInfo.model + }); + + this.setData({ + screenWidth: screenWidth + }); + + // 存储到页面实例,供后续方法使用 + this.screenWidth = screenWidth; + + } catch (error) { + console.error('获取屏幕信息失败,使用通用默认值:', error); + // 使用iPhone 6/7/8的标准宽度作为fallback + const fallbackWidth = 375; + this.setData({ + screenWidth: fallbackWidth + }); + this.screenWidth = fallbackWidth; + } + }, + + // 获取类型显示文本 + getTypeText(type) { + const typeMap = { + // 条形码类型 + 'Code 128': 'Code 128 条形码', + 'EAN-13': 'EAN-13 商品条码', + 'EAN-8': 'EAN-8 商品条码', + 'Code 39': 'Code 39 条形码', + 'ITF': 'ITF 交叉25码', + // 二维码类型 + 'standard': '标准二维码', + 'micro': '微型二维码' + }; + return typeMap[type] || type; + }, + + // 设置Canvas尺寸 + setCanvasSize() { + // 确保屏幕宽度已经正确获取 + const currentScreenWidth = this.data.screenWidth || this.screenWidth || 375; + + if (currentScreenWidth <= 0) { + console.warn('屏幕宽度获取异常,使用默认值375px'); + this.setData({ screenWidth: 375 }); + } + + if (this.data.codeType === 'qr') { + // 二维码是正方形 + const actualSize = this.data.qrSize; + + // 精确计算可用宽度 + const pagePadding = Math.floor(currentScreenWidth * 0.08); // 约8%作为页面边距 + const cardPadding = Math.floor(currentScreenWidth * 0.12); // 约12%作为卡片边距 + const maxWidth = currentScreenWidth - pagePadding - cardPadding; + + // 计算缩放比例 - 支持放大和缩小 + let scaleRatio = maxWidth / actualSize; + + // 限制缩放比例在合理范围内 + // 最小缩放:0.2(避免过度缩小) + // 最大缩放:2.5(避免过度放大) + scaleRatio = Math.max(0.2, Math.min(2.5, scaleRatio)); + + console.log(`二维码缩放计算详情: + 屏幕宽度: ${currentScreenWidth}px + 实际尺寸: ${actualSize}px + 页面边距: ${pagePadding}px + 卡片边距: ${cardPadding}px + 可用宽度: ${maxWidth}px + 计算比例: ${(maxWidth / actualSize).toFixed(3)} + 最终比例: ${scaleRatio.toFixed(3)} ${scaleRatio > 1 ? '(放大)' : scaleRatio < 1 ? '(缩小)' : '(原尺寸)'} + 显示尺寸: ${Math.round(actualSize * scaleRatio)}px`); + + // 直接计算显示尺寸,避免使用 transform: scale() + const displayWidth = Math.round(actualSize * scaleRatio); + const displayHeight = Math.round(actualSize * scaleRatio); + + this.setData({ + canvasWidth: actualSize, + canvasHeight: actualSize, + displayWidth: displayWidth, + displayHeight: displayHeight, + scaleRatio: scaleRatio + }); + + } else { + // 条形码是长方形 + const actualWidth = this.data.barcodeWidth; + const actualHeight = Math.max(80, actualWidth * 0.3); + + // 计算可用宽度 + const pagePadding = Math.floor(currentScreenWidth * 0.08); + const cardPadding = Math.floor(currentScreenWidth * 0.12); + const maxWidth = currentScreenWidth - pagePadding - cardPadding; + + // 计算缩放比例 - 支持放大和缩小 + let scaleRatio = maxWidth / actualWidth; + + // 限制缩放比例在合理范围内 + scaleRatio = Math.max(0.2, Math.min(2.5, scaleRatio)); + + // 如果计算出的缩放比例接近1.0,则不进行缩放 + if (scaleRatio > 0.95 && scaleRatio < 1.05) { + scaleRatio = 1.0; + } + + console.log(`条形码缩放计算详情: + 屏幕宽度: ${currentScreenWidth}px + 实际尺寸: ${actualWidth}x${actualHeight}px + 页面边距: ${pagePadding}px + 卡片边距: ${cardPadding}px + 可用宽度: ${maxWidth}px + 计算比例: ${(maxWidth / actualWidth).toFixed(3)} + 最终比例: ${scaleRatio.toFixed(3)} ${scaleRatio > 1 ? '(放大)' : scaleRatio < 1 ? '(缩小)' : '(原尺寸)'} + 显示尺寸: ${Math.round(actualWidth * scaleRatio)}x${Math.round(actualHeight * scaleRatio)}px`); + + // 直接计算显示尺寸,避免使用 transform: scale() + const displayWidth = Math.round(actualWidth * scaleRatio); + const displayHeight = Math.round(actualHeight * scaleRatio); + + this.setData({ + canvasWidth: actualWidth, + canvasHeight: actualHeight, + displayWidth: displayWidth, + displayHeight: displayHeight, + scaleRatio: scaleRatio + }); + } + }, + + // 绘制二维码 + drawQRCodes() { + if (this.data.batchMode) { + this.drawBatchQRCodes(); + } else { + this.drawSingleQRCode(); + } + }, + + // 绘制条形码 + drawBarcodes() { + if (this.data.batchMode) { + this.drawBatchBarcodes(); + } else { + this.drawSingleBarcode(); + } + }, + + // 绘制单个二维码 + drawSingleQRCode() { + try { + if (this.data.qrType === 'micro') { + // 微型二维码仅支持在线生成 + wx.showToast({ + title: '微型二维码仅支持在线生成', + icon: 'none' + }); + return; + } else { + // 使用标准二维码库 - 使用显示尺寸进行绘制 + drawQrcode({ + canvasId: 'barcodeCanvas', + text: this.data.content, + width: this.data.displayWidth, + height: this.data.displayHeight, + foreground: this.data.foregroundColor, + background: this.data.backgroundColor, + _this: this + }); + } + } catch (error) { + console.error('绘制二维码失败:', error); + wx.showToast({ + title: '生成失败', + icon: 'none' + }); + } + }, + + // 绘制单个条形码 + drawSingleBarcode() { + const ctx = wx.createCanvasContext('barcodeCanvas', this); + // 使用显示尺寸进行绘制 + this.drawBarcodeOnCanvas(ctx, this.data.content, this.data.displayWidth, this.data.displayHeight); + }, + + // 绘制批量二维码 + drawBatchQRCodes() { + // 计算批量模式下的缩放比例 + const screenWidth = this.data.screenWidth || 375; + const itemWidth = 150; // 二维码固定宽度 + const availableWidth = screenWidth * 0.3; // 可用宽度约为屏幕的30% + const batchScaleRatio = Math.min(1.0, availableWidth / itemWidth); + + // 计算批量显示尺寸 + const batchDisplayWidth = Math.round(itemWidth * batchScaleRatio); + const batchDisplayHeight = Math.round(itemWidth * batchScaleRatio); + + this.setData({ + batchScaleRatio: batchScaleRatio, + batchDisplayWidth: batchDisplayWidth, + batchDisplayHeight: batchDisplayHeight + }); + + this.data.barcodes.forEach((item, index) => { + const canvasId = `barcodeCanvas${index}`; + + setTimeout(() => { + try { + if (this.data.qrType === 'micro') { + // 微型二维码仅支持在线生成 + console.log(`批量第${index}个微型二维码跳过(仅支持在线生成)`); + return; + } else { + // 标准二维码 - 使用批量显示尺寸 + drawQrcode({ + canvasId: canvasId, + text: item.content, + width: this.data.batchDisplayWidth, + height: this.data.batchDisplayHeight, + foreground: this.data.foregroundColor, + background: this.data.backgroundColor, + _this: this + }); + } + } catch (error) { + console.error(`绘制第${index}个二维码失败:`, error); + } + }, index * 100); + }); + }, + + // 绘制批量条形码 + drawBatchBarcodes() { + // 计算批量模式下的缩放比例 + const screenWidth = this.data.screenWidth || 375; + const itemWidth = 200; // 条形码固定宽度 + const itemHeight = 80; // 条形码固定高度 + const availableWidth = screenWidth * 0.35; // 可用宽度约为屏幕的35% + const batchScaleRatio = Math.min(1.0, availableWidth / itemWidth); + + // 计算批量显示尺寸 + const batchDisplayWidth = Math.round(itemWidth * batchScaleRatio); + const batchDisplayHeight = Math.round(itemHeight * batchScaleRatio); + + this.setData({ + batchScaleRatio: batchScaleRatio, + batchDisplayWidth: batchDisplayWidth, + batchDisplayHeight: batchDisplayHeight + }); + + this.data.barcodes.forEach((item, index) => { + setTimeout(() => { + const ctx = wx.createCanvasContext(`barcodeCanvas${index}`, this); + // 使用批量显示尺寸进行绘制 + this.drawBarcodeOnCanvas(ctx, item.content, this.data.batchDisplayWidth, this.data.batchDisplayHeight); + }, index * 100); + }); + }, + + // 在Canvas上绘制条形码 + drawBarcodeOnCanvas(ctx, content, width, height) { + try { + const pattern = this.generateBarcodePattern(content); + if (!pattern) { + throw new Error('无法生成条形码图案'); + } + + // 获取设备像素比,提高绘制质量 + const systemInfo = wx.getSystemInfoSync(); + const pixelRatio = systemInfo.pixelRatio || 2; + + // 计算实际绘制尺寸(考虑像素密度) + const drawWidth = width * pixelRatio; + const drawHeight = height * pixelRatio; + + // 设置Canvas尺寸 + ctx.scale(pixelRatio, pixelRatio); + + // 清空画布 + ctx.setFillStyle(this.data.backgroundColor || '#ffffff'); + ctx.fillRect(0, 0, width, height); + + // 绘制条形码 + const barWidth = width / pattern.length; + + // 将字符串转换为数组进行遍历 + for (let i = 0; i < pattern.length; i++) { + if (pattern[i] === '1') { + ctx.setFillStyle(this.data.foregroundColor || '#000000'); + ctx.fillRect(i * barWidth, 0, barWidth, height * 0.8); + } + } + + // 绘制文本 - 根据Canvas大小动态调整字体大小 + const fontSize = Math.max(10, Math.min(20, height * 0.15)); + ctx.setFillStyle(this.data.foregroundColor || '#000000'); + ctx.setFontSize(fontSize); + ctx.setTextAlign('center'); + ctx.fillText(content, width / 2, height * 0.95); + + // 使用与二维码库相同的draw方式,确保渲染完成 + ctx.draw(false, () => { + console.log('条形码Canvas绘制完成,尺寸:', width, 'x', height, '像素比:', pixelRatio); + }); + } catch (error) { + console.error('绘制条形码失败:', error); + wx.showToast({ + title: '生成条形码失败', + icon: 'none' + }); + } + }, + + // 生成条形码图案(标准Code 128实现) + generateBarcodePattern(content) { + // 标准Code 128编码表 (值0-106: 0-102数据 + 103-105起始码 + 106停止码) + const CODE128_PATTERNS = [ + // 值 0-9 + '11011001100', '11001101100', '11001100110', '10010011000', '10010001100', + '10001001100', '10011001000', '10011000100', '10001100100', '11001001000', + // 值 10-19 + '11001000100', '11000100100', '10110011100', '10011011100', '10011001110', + '10111001100', '10011101100', '10011100110', '11001110010', '11001011100', + // 值 20-29 + '11001001110', '11011100100', '11001110100', '11101101110', '11101001100', + '11100101100', '11100100110', '11101100100', '11100110100', '11100110010', + // 值 30-39 + '11011011000', '11011000110', '11000110110', '10100011000', '10001011000', + '10001000110', '10110001000', '10001101000', '10001100010', '11010001000', + // 值 40-49 + '11000101000', '11000100010', '10110111000', '10110001110', '10001101110', + '10111011000', '10111000110', '10001110110', '11101110110', '11010001110', + // 值 50-59 + '11000101110', '11011101000', '11011100010', '11011101110', '11101011000', + '11101000110', '11100010110', '11101101000', '11101100010', '11100011010', + // 值 60-69 + '11101111010', '11001000010', '11110001010', '10100110000', '10100001100', + '10010110000', '10010000110', '10000101100', '10000100110', '10110010000', + // 值 70-79 + '10110000100', '10011010000', '10011000010', '10000110100', '10000110010', + '11000010010', '11001010000', '11110111010', '11000010100', '10001111010', + // 值 80-89 + '10100111100', '10010111100', '10010011110', '10111100100', '10011110100', + '10011110010', '11110100100', '11110010100', '11110010010', '11011011110', + // 值 90-99 + '11011110110', '11110110110', '10101111000', '10100011110', '10001011110', + '10111101000', '10111100010', '11110101000', '11110100010', '10111011110', + // 值 100-102 (FNC4, Code A, FNC1) + '10111101110', '11101011110', '11110101110', + // 值 103-105 (Start A, Start B, Start C) + '11010000100', '11010010000', '11010011100', + // 值 106 (Stop) + '1100011101011' + ]; + + // 验证输入 + if (!content || content.length === 0) { + console.error('内容不能为空'); + return null; + } + + try { + // Start Code B (值104) - 用于ASCII 32-127字符 + const START_CODE_B = 104; + let pattern = CODE128_PATTERNS[START_CODE_B]; + let checksum = START_CODE_B; // 起始码也参与校验计算 + + // 编码每个字符 + for (let i = 0; i < content.length; i++) { + const char = content.charAt(i); + const ascii = char.charCodeAt(0); + + // Code 128 Set B: ASCII 32-127 映射到值 0-95 + if (ascii >= 32 && ascii <= 127) { + const codeValue = ascii - 32; // 转换为Code 128值 + + // 验证值范围 (0-95对应ASCII 32-127) + if (codeValue >= 0 && codeValue <= 95) { + pattern += CODE128_PATTERNS[codeValue]; + // 校验和计算:值 × 位置权重,起始码位置为0,数据从位置1开始 + checksum += codeValue * (i + 1); + } else { + console.error(`字符值超出Code Set B范围: ${char} (ASCII: ${ascii}, 值: ${codeValue})`); + return null; + } + } else { + console.error(`不支持的ASCII字符: ${char} (ASCII: ${ascii})`); + return null; + } + } + + // 计算校验码 (modulo 103) + const checksumValue = checksum % 103; + + // 验证校验码值范围 + if (checksumValue < 0 || checksumValue >= CODE128_PATTERNS.length - 1) { + console.error(`校验码值超出范围: ${checksumValue}`); + return null; + } + + // 添加校验码 + pattern += CODE128_PATTERNS[checksumValue]; + + // 添加停止码 (值106,索引106) + const STOP_CODE = 106; + pattern += CODE128_PATTERNS[STOP_CODE]; + + console.log('Code 128标准生成详情:', { + content: content, + contentLength: content.length, + startCode: START_CODE_B, + checksumCalculation: checksum, + checksumValue: checksumValue, + stopCode: STOP_CODE, + totalPatternLength: pattern.length, + expectedModules: (content.length + 3) * 11 + 2, // (数据+起始+校验+停止)*11 + 停止码额外2模块 + patternPreview: pattern.substring(0, 50) + '...' + }); + + return pattern; + + } catch (error) { + console.error('Code 128标准生成失败:', error); + return null; + } + }, + + // 保存到相册 + saveToAlbum() { + wx.showLoading({ + title: '正在保存...' + }); + + const canvasId = 'barcodeCanvas'; + + const { displayWidth, displayHeight } = this.data; + + wx.canvasToTempFilePath({ + canvasId: canvasId, + width: displayWidth, + height: displayHeight, + destWidth: displayWidth * 2, // 提高分辨率 + destHeight: displayHeight * 2, + success: (res) => { + this.saveImageToAlbum(res.tempFilePath); + }, + fail: (error) => { + console.error('生成图片失败:', error); + wx.hideLoading(); + wx.showToast({ + title: '生成图片失败', + icon: 'none' + }); + } + }, this); + }, + + // 分享条形码 + shareBarcode() { + wx.showLoading({ + title: '正在生成分享图片...' + }); + + const { displayWidth, displayHeight } = this.data; + + wx.canvasToTempFilePath({ + canvasId: 'barcodeCanvas', + width: displayWidth, + height: displayHeight, + destWidth: displayWidth * 2, + destHeight: displayHeight * 2, + success: (res) => { + wx.hideLoading(); + this.shareToFriend(res.tempFilePath); + }, + fail: (error) => { + console.error('生成分享图片失败:', error); + wx.hideLoading(); + wx.showToast({ + title: '生成图片失败', + icon: 'none' + }); + } + }, this); + }, + + // 分享给朋友 + shareToFriend(imagePath) { + wx.showActionSheet({ + itemList: ['发送给朋友', '分享到朋友圈', '保存图片'], + success: (res) => { + if (res.tapIndex === 0) { + // 发送给朋友 + wx.showShareImageMenu({ + path: imagePath, + fail: (error) => { + console.error('分享失败:', error); + wx.showToast({ + title: '分享失败', + icon: 'none' + }); + } + }); + } else if (res.tapIndex === 1) { + // 分享到朋友圈(小程序无法直接分享到朋友圈,可以保存图片让用户手动分享) + this.saveImageToAlbum(imagePath); + } else if (res.tapIndex === 2) { + // 保存图片 + this.saveImageToAlbum(imagePath); + } + } + }); + }, + + // 保存图片到相册 + saveImageToAlbum(imagePath) { + wx.getSetting({ + success: (res) => { + if (res.authSetting['scope.writePhotosAlbum']) { + // 已经授权 + this.doSaveImage(imagePath); + } else { + // 未授权,请求授权 + wx.authorize({ + scope: 'scope.writePhotosAlbum', + success: () => { + this.doSaveImage(imagePath); + }, + fail: () => { + wx.showModal({ + title: '提示', + content: '需要您授权保存相册权限', + showCancel: false, + confirmText: '去设置', + success: (modalRes) => { + if (modalRes.confirm) { + wx.openSetting(); + } + } + }); + } + }); + } + } + }); + }, + + // 执行保存图片 + doSaveImage(imagePath) { + wx.saveImageToPhotosAlbum({ + filePath: imagePath, + success: () => { + wx.hideLoading(); + wx.showToast({ + title: '保存成功', + icon: 'success' + }); + }, + fail: (error) => { + console.error('保存失败:', error); + wx.hideLoading(); + wx.showToast({ + title: '保存失败', + icon: 'none' + }); + } + }); + }, + + // 保存全部到相册 + saveAllToAlbum() { + if (this.data.barcodes.length === 0) { + wx.showToast({ + title: '没有要保存的内容', + icon: 'none' + }); + return; + } + + wx.showModal({ + title: '确认保存', + content: `确定要保存全部 ${this.data.barcodes.length} 个${this.data.codeType === 'qr' ? '二维码' : '条形码'}到相册吗?`, + success: (res) => { + if (res.confirm) { + this.setData({ + loading: true, + loadingText: '正在批量保存...' + }); + this.saveBatchImages(); + } + } + }); + }, + + // 批量保存图片 + saveBatchImages() { + let savedCount = 0; + const totalCount = this.data.barcodes.length; + + const saveNext = (index) => { + if (index >= totalCount) { + this.setData({ + loading: false + }); + wx.showToast({ + title: `成功保存${savedCount}张图片`, + icon: 'success' + }); + return; + } + + const canvasId = `barcodeCanvas${index}`; + + wx.canvasToTempFilePath({ + canvasId: canvasId, + width: this.data.batchDisplayWidth, + height: this.data.batchDisplayHeight, + success: (res) => { + this.doSaveImage(res.tempFilePath); + savedCount++; + setTimeout(() => saveNext(index + 1), 500); + }, + fail: (error) => { + console.error(`保存第${index + 1}个图片失败:`, error); + setTimeout(() => saveNext(index + 1), 500); + } + }, this); + }; + + saveNext(0); + }, + + // 分享全部 + shareAll() { + wx.showToast({ + title: '批量分享功能开发中', + icon: 'none' + }); + }, + + // 逐个分享 + async shareAllOneByOne() { + if (this.data.barcodes.length === 0) { + wx.showToast({ + title: '没有要分享的内容', + icon: 'none' + }); + return; + } + + this.setData({ + loading: true, + loadingText: '正在准备分享...' + }); + + try { + for (let i = 0; i < this.data.barcodes.length; i++) { + const canvasId = `barcodeCanvas${i}`; + + const tempFilePath = await new Promise((resolve, reject) => { + wx.canvasToTempFilePath({ + canvasId: canvasId, + width: this.data.batchDisplayWidth, + height: this.data.batchDisplayHeight, + success: (res) => resolve(res.tempFilePath), + fail: reject + }, this); + }); + + // 这里可以调用分享API,或者保存到临时目录 + console.log(`第${i + 1}个码的临时文件路径:`, tempFilePath); + } + + this.setData({ + loading: false + }); + + wx.showToast({ + title: '准备完成', + icon: 'success' + }); + + } catch (error) { + console.error('批量分享失败:', error); + this.setData({ + loading: false + }); + wx.showToast({ + title: '分享失败', + icon: 'none' + }); + } + }, + + // 重新生成 + regenerate() { + wx.navigateBack(); + }, + + // 保存到历史记录 + saveToHistory(data) { + const historyManager = require('../../utils/history-manager.js'); + historyManager.saveQRGenerateRecord(data); + }, + + // 分享给朋友 + onShareAppMessage() { + return { + title: `我生成了一个${this.data.codeType === 'qr' ? '二维码' : '条形码'}`, + path: '/pages/index/index' + }; + }, + + // 检查离线模式状态 + checkOfflineMode() { + const app = getApp(); + const isOffline = app.isOfflineMode(); + console.log('结果页检查离线模式状态:', isOffline); + this.setData({ isOfflineMode: isOffline }); + }, + + // 本地生成二维码 + generateQRLocally(data) { + const content = Array.isArray(data.content) ? data.content[0] : data.content; + const trimmedContent = content.trim(); + + if (data.type === 'micro') { + // 微型二维码仅支持在线生成 + MessageUtil.showError('微型二维码仅支持在线生成,请确保网络连接正常'); + return; + } else { + // 生成标准二维码 + this.generateStandardQRLocally(trimmedContent, data); + } + }, + + // 本地生成标准二维码 + generateStandardQRLocally(content, data) { + try { + // 使用页面设置的显示尺寸,确保与WXML中的显示尺寸一致 + drawQrcode({ + width: this.data.displayWidth, + height: this.data.displayHeight, + canvasId: 'barcodeCanvas', + text: content, + foreground: data.foregroundColor || '#000000', + background: data.backgroundColor || '#ffffff', + _this: this, + callback: (res) => { + console.log('标准二维码本地生成完成,尺寸:', this.data.displayWidth, 'x', this.data.displayHeight); + this.handleLocalGenerateSuccess(); + } + }); + } catch (error) { + console.error('标准二维码本地生成失败:', error); + MessageUtil.showError('生成失败: ' + error.message); + } + }, + + + + // 本地生成条形码 + generateBarcodeLocally(data) { + const content = Array.isArray(data.content) ? data.content[0] : data.content; + const trimmedContent = content.trim(); + + // 简单的兼容性检查 + if (!trimmedContent || trimmedContent.length === 0) { + MessageUtil.showError('请输入有效内容'); + return; + } + + // 检查内容长度(Code 128 通常支持较长的内容) + if (trimmedContent.length > 100) { + MessageUtil.showError('内容过长,请减少字符数量'); + return; + } + + try { + console.log('[generateBarcodeLocally] 开始使用JsBarcode生成条形码:', trimmedContent); + + // 使用Canvas 2D API获取canvas节点 + wx.createSelectorQuery() + .in(this) + .select('#barcodeCanvas') + .fields({ + node: true, + size: true + }) + .exec((res) => { + if (!res[0] || !res[0].node) { + console.error('[generateBarcodeLocally] 无法获取canvas节点'); + MessageUtil.showError('Canvas初始化失败'); + return; + } + + const canvas = res[0].node; + const dpr = wx.getSystemInfoSync().pixelRatio || 1; + + // 设置canvas尺寸为显示尺寸(简化处理) + canvas.width = this.data.displayWidth; + canvas.height = this.data.displayHeight; + + console.log('[generateBarcodeLocally] Canvas设置:', { + width: canvas.width, + height: canvas.height, + displayWidth: this.data.displayWidth, + displayHeight: this.data.displayHeight, + dpr: dpr + }); + + // 使用JsBarcode生成条形码,使用逻辑尺寸 + const options = { + width: 2, + height: this.data.displayHeight - 20, // 留出一些边距 + displayValue: false, // 不显示文本,避免重叠 + background: data.backgroundColor || '#ffffff', + lineColor: data.foregroundColor || '#000000', + margin: 10, + marginTop: 10, + marginBottom: 10, + marginLeft: 10, + marginRight: 10 + }; + + try { + JsBarcodeUtil.generateCode128(canvas, trimmedContent, options); + console.log('[generateBarcodeLocally] 条形码生成完成'); + this.handleLocalGenerateSuccess(); + } catch (error) { + console.error('[generateBarcodeLocally] JsBarcode生成失败:', error); + MessageUtil.showError('生成失败: ' + error.message); + } + }); + + } catch (error) { + console.error('[generateBarcodeLocally] 条形码本地生成失败:', error); + MessageUtil.showError('生成失败: ' + error.message); + } + }, + + // 服务器生成 + async generateWithServer(data) { + try { + const params = { + codeType: this.data.codeType === 'qr' ? 'qrcode' : 'barcode', + subType: this.data.codeType === 'qr' ? data.type : data.barcodeType, + content: data.content, + batchMode: data.batchMode, + options: { + foregroundColor: data.foregroundColor, + backgroundColor: data.backgroundColor, + size: this.data.codeType === 'qr' ? data.size : data.barcodeWidth + } + }; + + // 调用API服务 + const response = await apiService.generateCode(params); + + if (data.batchMode) { + // 批量生成,处理服务器返回的批量结果 + this.handleServerBatchResult(response.data); + } else { + // 单个生成,处理服务器返回的单个结果 + this.handleServerSingleResult(response.data); + } + } catch (error) { + throw error; + } + }, + + // 处理服务器单个生成结果 + handleServerSingleResult(data) { + // 这里应该根据服务器返回的数据来显示结果 + // 由于服务器接口未实现,这个方法暂时为空 + console.log('服务器单个生成结果:', data); + }, + + // 处理服务器批量生成结果 + handleServerBatchResult(data) { + // 这里应该根据服务器返回的数据来显示批量结果 + // 由于服务器接口未实现,这个方法暂时为空 + console.log('服务器批量生成结果:', data); + }, + + // 处理本地生成成功 + handleLocalGenerateSuccess() { + // 本地生成完成,但不自动保存到相册,让用户自己点击按钮保存 + console.log('本地生成完成,等待用户操作'); + MessageUtil.showSuccess((this.data.codeType === 'qr' ? '二维码' : '条形码') + '生成完成'); + } +}); \ No newline at end of file diff --git a/pages/generate-result/generate-result.json b/pages/generate-result/generate-result.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/pages/generate-result/generate-result.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/pages/generate-result/generate-result.wxml b/pages/generate-result/generate-result.wxml new file mode 100644 index 0000000..380a6e2 --- /dev/null +++ b/pages/generate-result/generate-result.wxml @@ -0,0 +1,90 @@ + + + + + 生成结果 + 共生成 {{barcodes.length}} 个{{codeType === 'qr' ? '二维码' : '条形码'}} + {{codeType === 'qr' ? '二维码' : '条形码'}}生成成功 + + + + + + + + + + + + + {{content}} + {{typeText}} + + + + + + + + + + + + + + + + + + {{item.content}} + 第 {{index + 1}} 个 + + + + + + + + + + + + + + + + + + + + + + + {{loadingText}} + + \ No newline at end of file diff --git a/pages/generate-result/generate-result.wxss b/pages/generate-result/generate-result.wxss new file mode 100644 index 0000000..bad1873 --- /dev/null +++ b/pages/generate-result/generate-result.wxss @@ -0,0 +1,262 @@ +/* 码生成结果页面样式(支持二维码和条形码) - iOS风格 */ +.container { + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 40rpx 30rpx 30rpx; +} + +/* 顶部导航 */ +.header { + text-align: center; + margin-bottom: 60rpx; +} + +.title { + font-size: 48rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 16rpx; +} + +.subtitle { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.8); +} + +/* 单个条形码展示 */ +.single-barcode { + display: flex; + flex-direction: column; + align-items: center; +} + +.barcode-card { + background: #ffffff; + border-radius: 24rpx; + padding: 40rpx; + margin-bottom: 40rpx; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 600rpx; +} + +.barcode-container { + justify-content: center; + align-items: center; + margin-bottom: 30rpx; + width: 100%; + /* min-height: 600rpx; 最小高度,可以根据内容扩展 */ + margin: 0 auto 30rpx auto; /* 居中显示 */ +} + +.barcode-canvas { + border: 2rpx solid #f0f0f0; + border-radius: 12rpx; + background: #ffffff; +} + +.qr-canvas { + border: 2rpx solid #f0f0f0; + border-radius: 12rpx; + background: #ffffff; +} + +.barcode-info { + text-align: center; +} + +.barcode-content { + font-size: 28rpx; + color: #333333; + margin-bottom: 12rpx; + word-break: break-all; + line-height: 1.5; +} + +.barcode-type { + font-size: 24rpx; + color: #666666; +} + +/* 批量条形码展示 */ +.batch-barcode { + flex: 1; + display: flex; + flex-direction: column; +} + +.barcode-list { + flex: 1; + max-height: 800rpx; + margin-bottom: 40rpx; +} + +.barcode-item { + background: #ffffff; + border-radius: 20rpx; + margin-bottom: 20rpx; + padding: 30rpx; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08); +} + +.barcode-item-container { + display: flex; + align-items: center; +} + +.barcode-canvas-small { + border: 2rpx solid #f0f0f0; + border-radius: 8rpx; + margin-right: 30rpx; + flex-shrink: 0; + background: #ffffff; +} + +.qr-canvas-small { + border: 2rpx solid #f0f0f0; + border-radius: 8rpx; + margin-right: 30rpx; + flex-shrink: 0; + background: #ffffff; +} + +.barcode-item-info { + flex: 1; + min-width: 0; +} + +.barcode-item-content { + font-size: 28rpx; + color: #333333; + margin-bottom: 8rpx; + word-break: break-all; + line-height: 1.4; +} + +.barcode-item-index { + font-size: 24rpx; + color: #666666; +} + +/* 操作按钮 */ +.actions { + display: flex; + gap: 20rpx; + width: 100%; + max-width: 600rpx; +} + +.batch-actions { + display: flex; + gap: 20rpx; + margin-bottom: 20rpx; +} + +.bottom-actions { + display: flex; + justify-content: center; + margin-top: 40rpx; +} + +.action-btn { + flex: 1; + height: 88rpx; + border-radius: 44rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + font-weight: 500; + border: none; + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.action-btn::after { + border: none; +} + +.action-btn.primary { + background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); + color: #ffffff; + box-shadow: 0 4rpx 16rpx rgba(76, 175, 80, 0.3); +} + +.action-btn.primary:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 8rpx rgba(76, 175, 80, 0.3); +} + +.action-btn.secondary { + background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%); + color: #ffffff; + box-shadow: 0 4rpx 16rpx rgba(33, 150, 243, 0.3); +} + +.action-btn.secondary:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 8rpx rgba(33, 150, 243, 0.3); +} + +.action-btn.outline { + background: rgba(255, 255, 255, 0.2); + color: #ffffff; + border: 2rpx solid rgba(255, 255, 255, 0.3); + backdrop-filter: blur(10rpx); + max-width: 300rpx; +} + +.action-btn.outline:active { + background: rgba(255, 255, 255, 0.3); +} + +.btn-text { + font-size: inherit; + font-weight: inherit; +} + +/* 加载提示 */ +.loading-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.loading-content { + background: #ffffff; + border-radius: 20rpx; + padding: 60rpx 40rpx; + display: flex; + flex-direction: column; + align-items: center; + min-width: 300rpx; +} + +.loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid #f0f0f0; + border-top: 4rpx solid #4CAF50; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 30rpx; +} + +.loading-text { + font-size: 28rpx; + color: #333333; + text-align: center; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/pages/history/history.js b/pages/history/history.js new file mode 100644 index 0000000..f724053 --- /dev/null +++ b/pages/history/history.js @@ -0,0 +1,784 @@ +// 历史记录页面 - iOS风格 +Page({ + data: { + historyList: [], + selectMode: false, + selectedItems: [], + showDetail: false, + currentItem: null, + searchValue: '', + showSearch: false, + currentPage: 1, + pageSize: 20, + hasMore: true, + loading: false + }, + + onLoad(options) { + this.loadHistory(); + + // 如果没有历史记录,创建一些测试数据 + if (options.demo === 'true') { + this.createTestData(); + } + }, + + onShow() { + // 每次显示页面时重新加载历史记录 + this.loadHistory(); + }, + + // 加载历史记录 + loadHistory() { + try { + const historyManager = require('../../utils/history-manager.js'); + const scanHistory = historyManager.getHistory('scanHistory'); + + // 只显示扫描历史记录,不包含生成记录 + const allHistory = scanHistory.map(item => ({ ...item, source: 'scan' })); + + // 按时间排序(最新的在前面) + allHistory.sort((a, b) => { + const timeA = a.scanTime || a.timestamp || 0; + const timeB = b.scanTime || b.timestamp || 0; + return timeB - timeA; + }); + + const processedHistory = allHistory.map(item => this.processHistoryItem(item)); + + this.setData({ + historyList: processedHistory, + selectMode: false, + selectedItems: [], + hasMore: false // 暂时不支持分页 + }); + } catch (error) { + console.error('加载历史记录失败:', error); + wx.showToast({ + title: '加载失败', + icon: 'none' + }); + } + }, + + // 处理历史记录项 + processHistoryItem(item) { + const now = new Date(); + const recordTime = new Date(item.scanTime || item.timestamp || item.time); + const timeDiff = now - recordTime; + + // 格式化时间显示 + let timeText = ''; + let fullTime = this.formatFullTime(recordTime); + + if (timeDiff < 60000) { // 1分钟内 + timeText = '刚刚'; + } else if (timeDiff < 3600000) { // 1小时内 + timeText = Math.floor(timeDiff / 60000) + '分钟前'; + } else if (timeDiff < 86400000) { // 24小时内 + timeText = Math.floor(timeDiff / 3600000) + '小时前'; + } else if (timeDiff < 604800000) { // 7天内 + timeText = Math.floor(timeDiff / 86400000) + '天前'; + } else { + timeText = this.formatDate(recordTime); + } + + // 识别内容类型 + const contentType = this.identifyContentType(item.content); + + // 生成预览文本 + const preview = this.generatePreview(item.content, contentType); + + // 生成额外信息 + const extra = this.generateExtraInfo(item.content, contentType); + + return { + ...item, + timeText, + fullTime, + type: contentType.type, + typeText: contentType.text, + sourceText: item.source === 'generate' ? '生成' : '扫描', + preview, + extra, + selected: false + }; + }, + + // 识别内容类型 + identifyContentType(content) { + // URL检测 + if (/^https?:\/\/.+/i.test(content)) { + return { type: 'url', text: '网址' }; + } + + // WiFi信息检测 + if (/^WIFI:/i.test(content)) { + return { type: 'wifi', text: 'WiFi' }; + } + + // 电话号码检测 + if (/^tel:/i.test(content) || /^(\+?86)?1[3-9]\d{9}$/.test(content)) { + return { type: 'phone', text: '电话' }; + } + + // 联系人信息检测 + if (/^BEGIN:VCARD/i.test(content)) { + return { type: 'contact', text: '联系人' }; + } + + // 邮箱检测 + if (/^mailto:/i.test(content) || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content)) { + return { type: 'email', text: '邮箱' }; + } + + // 地理位置检测 + if (/^geo:/i.test(content)) { + return { type: 'location', text: '位置' }; + } + + // 默认为文本 + return { type: 'text', text: '文本' }; + }, + + // 生成预览文本 + generatePreview(content, contentType) { + const maxLength = 100; + + switch (contentType.type) { + case 'wifi': + const wifiMatch = content.match(/S:([^;]*)/); + return wifiMatch ? `WiFi: ${wifiMatch[1]}` : content.substring(0, maxLength); + + case 'url': + try { + const url = new URL(content); + return url.hostname + url.pathname; + } catch { + return content.substring(0, maxLength); + } + + case 'phone': + return content.replace(/^tel:/, ''); + + case 'email': + return content.replace(/^mailto:/, ''); + + default: + return content.length > maxLength ? + content.substring(0, maxLength) + '...' : content; + } + }, + + // 生成额外信息 + generateExtraInfo(content, contentType) { + switch (contentType.type) { + case 'wifi': + const passwordMatch = content.match(/P:([^;]*)/); + return passwordMatch && passwordMatch[1] ? '需要密码' : '开放网络'; + + case 'url': + try { + const url = new URL(content); + return url.protocol === 'https:' ? '安全连接' : '普通连接'; + } catch { + return null; + } + + case 'contact': + const nameMatch = content.match(/FN:([^\r\n]*)/); + return nameMatch ? `联系人: ${nameMatch[1]}` : null; + + default: + return null; + } + }, + + // 格式化完整时间 + formatFullTime(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hour = String(date.getHours()).padStart(2, '0'); + const minute = String(date.getMinutes()).padStart(2, '0'); + const second = String(date.getSeconds()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; + }, + + // 格式化日期 + formatDate(date) { + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${month}月${day}日`; + }, + + // 切换选择模式 + toggleSelectMode() { + this.setData({ + selectMode: !this.data.selectMode, + selectedItems: [] + }); + }, + + // 切换选择项 + toggleSelect(e) { + const { item } = e.currentTarget.dataset; + const selectedItems = [...this.data.selectedItems]; + const index = selectedItems.indexOf(item.id); + + if (index >= 0) { + selectedItems.splice(index, 1); + } else { + selectedItems.push(item.id); + } + + this.setData({ selectedItems }); + }, + + // 查看详情 + viewDetail(e) { + const { item } = e.currentTarget.dataset; + this.setData({ + currentItem: item, + showDetail: true + }); + }, + + // 隐藏详情 + hideDetail() { + this.setData({ + showDetail: false, + currentItem: null + }); + }, + + // 阻止事件冒泡 + stopPropagation() { + // 空函数,用于阻止事件冒泡 + }, + + // 复制内容 + copyContent(e) { + // 阻止事件冒泡 + e.stopPropagation && e.stopPropagation(); + + const content = e.currentTarget.dataset.content; + + wx.setClipboardData({ + data: content, + success: () => { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + + // 如果是在弹窗中,关闭弹窗 + if (this.data.showDetail) { + this.hideDetail(); + } + }, + fail: () => { + wx.showToast({ + title: '复制失败', + icon: 'none' + }); + } + }); + }, + + // 分享内容 + shareContent(e) { + // 阻止事件冒泡 + e.stopPropagation && e.stopPropagation(); + + const item = e.currentTarget.dataset.item; + + wx.showActionSheet({ + itemList: ['发送给朋友', '分享到朋友圈', '复制链接'], + success: (res) => { + switch (res.tapIndex) { + case 0: + // 发送给朋友的逻辑 + wx.showToast({ + title: '功能开发中', + icon: 'none' + }); + break; + case 1: + // 分享到朋友圈的逻辑 + wx.showToast({ + title: '功能开发中', + icon: 'none' + }); + break; + case 2: + // 复制链接 + this.copyContent({ currentTarget: { dataset: { content: item.content } } }); + break; + } + + // 如果是在弹窗中,关闭弹窗 + if (this.data.showDetail) { + this.hideDetail(); + } + } + }); + }, + + // 删除选中项 + deleteSelected() { + if (this.data.selectedItems.length === 0) return; + + wx.showModal({ + title: '确认删除', + content: `确定要删除选中的 ${this.data.selectedItems.length} 条记录吗?`, + confirmText: '删除', + confirmColor: '#dc2626', + success: (res) => { + if (res.confirm) { + this.performDelete(this.data.selectedItems); + } + } + }); + }, + + // 清空所有历史 + clearAllHistory() { + wx.showModal({ + title: '确认清空', + content: '确定要清空所有历史记录吗?此操作不可恢复。', + confirmText: '清空', + confirmColor: '#dc2626', + success: (res) => { + if (res.confirm) { + this.performClearAll(); + } + } + }); + }, + + // 执行删除 + performDelete(idsToDelete) { + try { + const historyManager = require('../../utils/history-manager.js'); + const success = historyManager.deleteHistoryItems('scanHistory', idsToDelete); + + if (success) { + wx.showToast({ + title: '删除成功', + icon: 'success' + }); + // 重新加载数据 + this.loadHistory(); + } else { + wx.showToast({ + title: '删除失败', + icon: 'none' + }); + } + } catch (error) { + console.error('删除失败:', error); + wx.showToast({ + title: '删除失败', + icon: 'none' + }); + } + }, + + // 执行清空 + performClearAll() { + try { + const historyManager = require('../../utils/history-manager.js'); + const success = historyManager.clearHistory('scanHistory'); + + if (success) { + wx.showToast({ + title: '清空成功', + icon: 'success' + }); + // 重新加载数据 + this.loadHistory(); + } else { + wx.showToast({ + title: '清空失败', + icon: 'none' + }); + } + } catch (error) { + console.error('清空失败:', error); + wx.showToast({ + title: '清空失败', + icon: 'none' + }); + } + }, + + // 跳转到扫码页面 + goToScan() { + // 直接跳转到首页并触发扫码 + wx.reLaunch({ + url: '/pages/index/index?autoScan=true' + }); + }, + + // 创建测试数据 + createTestData() { + const testData = [ + { + id: '1', + content: 'https://www.baidu.com', + scanTime: Date.now() - 60000 // 1分钟前 + }, + { + id: '2', + content: 'WIFI:T:WPA;S:MyWiFi;P:password123;H:false;;', + scanTime: Date.now() - 3600000 // 1小时前 + }, + { + id: '3', + content: '13812345678', + scanTime: Date.now() - 86400000 // 1天前 + }, + { + id: '4', + content: 'BEGIN:VCARD\nVERSION:3.0\nFN:张三\nTEL:13800138000\nEND:VCARD', + scanTime: Date.now() - 172800000 // 2天前 + }, + { + id: '5', + content: '这是一段普通的文本内容,用于测试文本类型的二维码识别功能。', + scanTime: Date.now() - 259200000 // 3天前 + } + ]; + + try { + wx.setStorageSync('scanHistory', testData); + this.loadHistory(); + wx.showToast({ + title: '测试数据已创建', + icon: 'success' + }); + } catch (error) { + console.error('创建测试数据失败:', error); + } + }, + + // 搜索功能 + onSearchInput: function(e) { + var value = e.detail.value; + this.setData({ + searchValue: value + }); + + // 实时搜索 + this.performSearch(value); + }, + + // 执行搜索 + performSearch: function(keyword) { + if (!keyword.trim()) { + this.loadHistory(); + return; + } + + try { + var scanHistory = wx.getStorageSync('scanHistory') || []; + + // 过滤匹配的记录 + var filteredHistory = scanHistory.filter(function(item) { + var content = item.content || ''; + return content.toLowerCase().includes(keyword.toLowerCase()); + }); + + // 合并并处理 + var allHistory = filteredHistory.map(function(item) { + return Object.assign({}, item, { source: 'scan' }); + }); + + // 按时间排序 + allHistory.sort(function(a, b) { + return (b.scanTime || b.timestamp || 0) - (a.scanTime || a.timestamp || 0); + }); + + // 处理每个项目 + var processedHistory = allHistory.map(this.processHistoryItem.bind(this)); + + this.setData({ + historyList: processedHistory + }); + + } catch (error) { + console.error('搜索失败:', error); + } + }, + + // 清空搜索 + clearSearch: function() { + this.setData({ + searchValue: '', + showSearch: false + }); + this.loadHistory(); + }, + + // 显示搜索框 + showSearchInput: function() { + this.setData({ + showSearch: true + }); + }, + + // 隐藏搜索框 + hideSearchInput: function() { + this.setData({ + showSearch: false, + searchValue: '' + }); + this.loadHistory(); + }, + + // 点击历史记录项目 + onItemTap: function(e) { + var item = e.currentTarget.dataset.item; + + if (this.data.selectMode) { + // 选择模式下切换选中状态 + this.toggleItemSelection(item); + } else { + // 正常模式下处理内容 + this.processItemContent(item.content); + } + }, + + // 处理项目内容 + processItemContent: function(content) { + // URL处理 + if (/^https?:\/\/.+/i.test(content)) { + wx.showModal({ + title: '发现网址', + content: content, + confirmText: '复制', + showCancel: false, + success: function(res) { + if (res.confirm) { + // 复制到剪贴板 + wx.setClipboardData({ + data: content, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // WiFi信息处理 + if (/^WIFI:/i.test(content)) { + var wifiMatch = content.match(/WIFI:T:([^;]*);S:([^;]*);P:([^;]*);H:([^;]*);?/); + if (wifiMatch) { + var wifiInfo = '网络名称: ' + wifiMatch[2] + '\n' + + '密码: ' + wifiMatch[3] + '\n' + + '加密类型: ' + wifiMatch[1]; + + wx.showModal({ + title: 'WiFi信息', + content: wifiInfo, + confirmText: '复制密码', + cancelText: '知道了', + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: wifiMatch[3], + success: function() { + wx.showToast({ + title: '密码已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + } + + // 电话号码处理 + if (/^tel:/i.test(content) || /^(\+?86)?1[3-9]\d{9}$/.test(content)) { + var phoneNumber = content.replace(/^tel:/, ''); + wx.showModal({ + title: '电话号码', + content: phoneNumber, + confirmText: '拨打', + cancelText: '复制', + success: function(res) { + if (res.confirm) { + wx.makePhoneCall({ + phoneNumber: phoneNumber, + fail: function() { + wx.showToast({ + title: '拨打失败', + icon: 'none' + }); + } + }); + } else if (res.cancel) { + wx.setClipboardData({ + data: phoneNumber, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // 邮箱处理 + if (/^mailto:/i.test(content) || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content)) { + var email = content.replace(/^mailto:/, ''); + wx.showModal({ + title: '邮箱地址', + content: email, + confirmText: '复制', + showCancel: false, + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: email, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // 普通文本 + wx.showModal({ + title: '内容详情', + content: content, + confirmText: '复制', + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: content, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + }, + + // 进入选择模式 + enterSelectMode: function() { + this.setData({ + selectMode: true, + selectedItems: [] + }); + }, + + // 退出选择模式 + exitSelectMode: function() { + this.setData({ + selectMode: false, + selectedItems: [] + }); + + // 重置所有项目的选中状态 + var historyList = this.data.historyList.map(function(item) { + return Object.assign({}, item, { selected: false }); + }); + + this.setData({ + historyList: historyList + }); + }, + + // 切换项目选中状态 + toggleItemSelection: function(targetItem) { + var historyList = this.data.historyList; + var selectedItems = this.data.selectedItems.slice(); // 复制数组 + + // 找到目标项目并切换选中状态 + for (var i = 0; i < historyList.length; i++) { + if (historyList[i].id === targetItem.id) { + historyList[i].selected = !historyList[i].selected; + + if (historyList[i].selected) { + // 添加到选中列表 + selectedItems.push(historyList[i]); + } else { + // 从选中列表移除 + selectedItems = selectedItems.filter(function(item) { + return item.id !== targetItem.id; + }); + } + break; + } + } + + this.setData({ + historyList: historyList, + selectedItems: selectedItems + }); + }, + + // 全选 + selectAll: function() { + var historyList = this.data.historyList.map(function(item) { + return Object.assign({}, item, { selected: true }); + }); + + this.setData({ + historyList: historyList, + selectedItems: historyList.slice() + }); + }, + + // 取消全选 + deselectAll: function() { + var historyList = this.data.historyList.map(function(item) { + return Object.assign({}, item, { selected: false }); + }); + + this.setData({ + historyList: historyList, + selectedItems: [] + }); + }, + + // 分享功能 + onShareAppMessage() { + return { + title: '小籽二维码 - 扫码生成一站式服务', + path: '/pages/index/index' + }; + } +}); + +// 工具函数:保存扫描记录 +export function saveScanRecord(content) { + const historyManager = require('../../utils/history-manager.js'); + return historyManager.saveScanRecord(content); +} \ No newline at end of file diff --git a/pages/history/history.json b/pages/history/history.json new file mode 100644 index 0000000..03e9159 --- /dev/null +++ b/pages/history/history.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "扫描记录", + "backgroundTextStyle": "dark", + "backgroundColor": "#f8f9fa" +} \ No newline at end of file diff --git a/pages/history/history.wxml b/pages/history/history.wxml new file mode 100644 index 0000000..8fb254c --- /dev/null +++ b/pages/history/history.wxml @@ -0,0 +1,124 @@ + + + + + 扫描记录 + + 最近20条扫描记录 + + + + + + + 共 {{historyList.length}} 条记录 + + + + {{selectMode ? '取消' : '选择'}} + + + 清空 + + + 删除({{selectedItems.length}}) + + + + + + + + + + + + + + + + + + {{item.typeText}} + {{item.sourceText}} + {{item.timeText}} + + + + 复制 + + + 分享 + + + + + + {{item.preview}} + + + + + + + + + + + + + 暂无扫描记录 + 扫描二维码后,记录会显示在这里 + + 立即扫码 + + + + + + + + + + + + 记录详情 + + + + + + + + 类型 + {{currentItem.typeText}} + + + 时间 + {{currentItem.fullTime}} + + + 内容 + + {{currentItem.content}} + + + + + + + 复制 + + + 分享 + + + + \ No newline at end of file diff --git a/pages/history/history.wxss b/pages/history/history.wxss new file mode 100644 index 0000000..28272aa --- /dev/null +++ b/pages/history/history.wxss @@ -0,0 +1,477 @@ +/* 历史记录页面样式 - iOS风格 */ + +/* 主容器 */ +.container { + min-height: 100vh; + background: linear-gradient(180deg, + #f0f9ff 0%, + #f8fafc 50%, + #f1f5f9 100%); + padding: 0 32rpx; +} + +/* 标题区域 */ +.header-section { + padding: 40rpx 0; + text-align: center; +} + +.page-title { + font-size: 48rpx; + font-weight: 700; + color: #1e293b; + line-height: 1.2; + margin-bottom: 16rpx; +} + +.page-subtitle { + font-size: 26rpx; + color: #64748b; + line-height: 1.5; +} + +/* 操作栏 */ +.action-bar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 24rpx 32rpx; + background: white; + border-radius: 20rpx; + margin-bottom: 24rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); +} + +.record-count text { + font-size: 26rpx; + color: #64748b; + font-weight: 500; +} + +.action-buttons { + display: flex; + gap: 16rpx; +} + +.action-btn { + padding: 12rpx 24rpx; + border-radius: 12rpx; + background: #f1f5f9; + border: 1rpx solid #e2e8f0; + transition: all 0.3s ease; +} + +.action-btn text { + font-size: 24rpx; + color: #475569; + font-weight: 500; +} + +.action-btn.danger { + background: #fef2f2; + border-color: #fecaca; +} + +.action-btn.danger text { + color: #dc2626; +} + +.action-btn:active { + transform: scale(0.95); +} + +/* 历史记录列表 */ +.history-list { + display: flex; + flex-direction: column; + gap: 16rpx; +} + +.history-item { + background: white; + border-radius: 20rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid rgba(0, 0, 0, 0.02); + transition: all 0.3s ease; + display: flex; + align-items: flex-start; + gap: 20rpx; +} + +.history-item:active { + transform: scale(0.98); + box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.08); +} + +.history-item.select-mode { + padding-left: 20rpx; +} + +/* 选择框 */ +.select-checkbox { + width: 40rpx; + height: 40rpx; + border: 2rpx solid #d1d5db; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-top: 8rpx; + transition: all 0.3s ease; +} + +.select-checkbox.checked { + background: #22c55e; + border-color: #22c55e; +} + +.check-icon { + font-size: 20rpx; + color: white; + font-weight: bold; + opacity: 0; + transition: opacity 0.3s ease; +} + +.select-checkbox.checked .check-icon { + opacity: 1; +} + +/* 记录内容 */ +.item-content { + flex: 1; + min-width: 0; +} + +.content-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 16rpx; +} + +.content-type { + display: flex; + align-items: center; + gap: 16rpx; +} + +.type-tag { + display: inline-block; + padding: 6rpx 16rpx; + border-radius: 12rpx; + font-size: 20rpx; + font-weight: 600; + color: white; + background: #6b7280; +} + +.type-tag.url { + background: #3b82f6; +} + +.type-tag.text { + background: #22c55e; +} + +.type-tag.wifi { + background: #8b5cf6; +} + +.type-tag.contact { + background: #f59e0b; +} + +.type-tag.phone { + background: #ef4444; +} + +.source-tag { + display: inline-block; + padding: 4rpx 12rpx; + border-radius: 8rpx; + font-size: 18rpx; + font-weight: 500; + color: white; + background: #64748b; +} + +.source-tag.scan { + background: #06b6d4; +} + +.source-tag.generate { + background: #f59e0b; +} + +.scan-time { + font-size: 22rpx; + color: #94a3b8; +} + +.content-actions { + display: flex; + gap: 12rpx; +} + +.action-btn-text { + padding: 0 12rpx; + transition: all 0.3s ease; +} + +.action-btn-text:active { + opacity: 0.6; +} + +.action-btn-text text { + font-size: 26rpx; + color: #3b82f6; + font-weight: 500; + text-decoration: underline; +} + +/* 内容文本 */ +.content-text { + margin-bottom: 12rpx; +} + +.content-preview { + font-size: 28rpx; + color: #1e293b; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + word-break: break-all; +} + +.content-footer { + margin-top: 12rpx; +} + +.extra-info { + font-size: 24rpx; + color: #64748b; + background: #f1f5f9; + padding: 8rpx 16rpx; + border-radius: 8rpx; + display: inline-block; +} + +/* 空状态 */ +.empty-state { + text-align: center; + padding: 120rpx 40rpx; +} + +.empty-icon { + margin-bottom: 32rpx; + opacity: 0.6; + display: flex; + justify-content: center; + align-items: center; +} + +.empty-icon-img { + width: 120rpx; + height: 120rpx; +} + +.empty-title { + font-size: 36rpx; + font-weight: 600; + color: #1e293b; + display: block; + margin-bottom: 16rpx; +} + +.empty-desc { + font-size: 26rpx; + color: #64748b; + line-height: 1.5; + display: block; + margin-bottom: 48rpx; +} + +.empty-action { + display: inline-block; + padding: 20rpx 40rpx; + background: linear-gradient(135deg, #22c55e, #16a34a); + border-radius: 20rpx; + box-shadow: 0 4rpx 20rpx rgba(34, 197, 94, 0.3); + transition: all 0.3s ease; +} + +.empty-action:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 12rpx rgba(34, 197, 94, 0.25); +} + +.empty-action text { + font-size: 28rpx; + font-weight: 600; + color: white; +} + +/* 详情弹窗 */ +.detail-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.detail-modal.show { + opacity: 1; + visibility: visible; +} + +.modal-content { + width: 90%; + max-width: 600rpx; + background: white; + border-radius: 24rpx; + overflow: hidden; + transform: scale(0.9); + transition: transform 0.3s ease; +} + +.detail-modal.show .modal-content { + transform: scale(1); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + border-bottom: 1rpx solid #f1f5f9; +} + +.modal-title { + font-size: 32rpx; + font-weight: 600; + color: #1e293b; +} + +.modal-close { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: #f8fafc; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.modal-close:active { + transform: scale(0.9); + background: #e2e8f0; +} + +.modal-close text { + font-size: 24rpx; + color: #64748b; +} + +.modal-body { + padding: 32rpx; + max-height: 60vh; + overflow-y: auto; +} + +.detail-item { + display: flex; + margin-bottom: 24rpx; +} + +.detail-item.full { + flex-direction: column; +} + +.detail-label { + font-size: 26rpx; + color: #64748b; + font-weight: 500; + width: 120rpx; + flex-shrink: 0; +} + +.detail-value { + font-size: 26rpx; + color: #1e293b; + flex: 1; +} + +.detail-content { + margin-top: 12rpx; + padding: 20rpx; + background: #f8fafc; + border-radius: 12rpx; + border: 1rpx solid #e2e8f0; +} + +.detail-text { + font-size: 26rpx; + color: #1e293b; + line-height: 1.6; + word-break: break-all; +} + +.modal-actions { + display: flex; + gap: 16rpx; + padding: 32rpx; + border-top: 1rpx solid #f1f5f9; +} + +.modal-btn { + flex: 1; + padding: 24rpx; + border-radius: 16rpx; + text-align: center; + transition: all 0.3s ease; +} + +.modal-btn.secondary { + background: #f8fafc; + border: 1rpx solid #e2e8f0; +} + +.modal-btn.secondary text { + font-size: 28rpx; + font-weight: 500; + color: #475569; +} + +.modal-btn.primary { + background: linear-gradient(135deg, #22c55e, #16a34a); +} + +.modal-btn.primary text { + font-size: 28rpx; + font-weight: 600; + color: white; +} + +.modal-btn:active { + transform: scale(0.95); +} + +/* 底部安全区域 */ +.bottom-safe-area { + height: 80rpx; +} \ No newline at end of file diff --git a/pages/index/index.js b/pages/index/index.js new file mode 100644 index 0000000..24d0f1a --- /dev/null +++ b/pages/index/index.js @@ -0,0 +1,903 @@ +// 二维码工具箱首页 - iOS风格 +Page({ + data: { + // 扫码动画状态 + scanAnimation: false, + + // 广告显示状态 + showAd: true, + + // 历史记录数量统计 + totalHistory: 0, + + // 最近使用项目(前6个) + recentItems: [], + + // 最近使用详情弹窗 + showRecentDetail: false, + currentRecentItem: null, + + // 扫码模式设置 + scanMode: 'custom', // 'custom' 或 'wechat' + showModeSelector: false, + + // 离线模式状态 + isOfflineMode: false, + + // 初始化状态 + isInitializing: true, + initializationError: null, + showOfflineBanner: false + }, + + onLoad: function(options) { + console.log('首页加载'); + + // 检查应用初始化状态 + this.checkAppInitialization(); + + this.loadScanMode(); + + // 如果应用还在初始化,则不加载数据,等待初始化完成 + if (!this.data.isInitializing) { + this.loadData(); + this.checkOfflineMode(); + } + + // 检查是否需要自动扫码 + if (options.autoScan === 'true') { + // 延迟一点时间确保页面完全加载 + var self = this; + setTimeout(function() { + if (!self.data.isInitializing) { + self.startScan(); + } + }, 500); + } + }, + + onShow: function() { + console.log('首页显示'); + + // 检查应用初始化状态 + this.checkAppInitialization(); + + // 如果应用还在初始化,则不刷新数据 + if (!this.data.isInitializing) { + // 页面显示时刷新数据 + this.loadData(); + this.checkOfflineMode(); + } + }, + + // 检查应用初始化状态 + checkAppInitialization: function() { + try { + const app = getApp(); + const isInitializing = app.isInitializing(); + const initError = app.getInitializationError(); + + this.setData({ + isInitializing: isInitializing, + initializationError: initError + }); + + console.log('应用初始化状态:', { isInitializing, initError }); + } catch (error) { + console.error('检查初始化状态失败:', error); + // 如果无法获取状态,假设初始化已完成 + this.setData({ + isInitializing: false, + initializationError: null + }); + } + }, + + // 应用初始化完成回调 + onAppInitComplete: function(success) { + console.log('收到应用初始化完成通知,成功:', success); + + this.setData({ + isInitializing: false + }); + + if (success) { + // 初始化成功,加载数据 + this.loadData(); + this.checkOfflineMode(); + } else { + // 初始化失败,显示离线模式 + this.handleInitializationFailure(); + } + }, + + // 处理初始化失败 + handleInitializationFailure: function() { + console.log('处理初始化失败,进入离线模式'); + + // 延迟显示离线横幅,给用户更好的体验 + setTimeout(() => { + this.setData({ + showOfflineBanner: true + }); + }, 500); + + // 仍然加载本地数据 + this.loadData(); + this.checkOfflineMode(); + }, + + // 加载所有数据 + loadData: function() { + try { + this.loadHistoryStats(); + this.loadRecentItems(); + } catch (error) { + console.error('加载数据失败:', error); + } + }, + + // 加载扫码模式设置 + loadScanMode: function() { + try { + var savedMode = wx.getStorageSync('scanMode'); + if (savedMode === 'custom' || savedMode === 'wechat') { + this.setData({ + scanMode: savedMode + }); + console.log('加载扫码模式:', savedMode); + } else { + // 默认使用自定义模式 + this.setData({ + scanMode: 'custom' + }); + console.log('使用默认扫码模式: custom'); + } + } catch (error) { + console.error('加载扫码模式失败:', error); + this.setData({ + scanMode: 'custom' + }); + } + }, + + // 检查离线模式状态 + checkOfflineMode: function() { + try { + const app = getApp(); + const isOffline = app.isOfflineMode(); + + this.setData({ + isOfflineMode: isOffline + }); + + console.log('当前离线模式状态:', isOffline); + } catch (error) { + console.error('检查离线模式失败:', error); + this.setData({ + isOfflineMode: false + }); + } + }, + + // 切换离线模式(测试功能) + toggleOfflineMode: function(e) { + const isOffline = e.detail.value; + console.log('手动切换离线模式:', isOffline); + + try { + const app = getApp(); + + // 更新全局状态 + app.globalData.isOfflineMode = isOffline; + + // 更新本地存储 + if (isOffline) { + wx.setStorageSync('app_offline_mode', true); + wx.setStorageSync('test_offline_mode', true); // 标记为测试模式 + } else { + wx.removeStorageSync('app_offline_mode'); + wx.removeStorageSync('test_offline_mode'); + } + + // 更新页面状态 + this.setData({ + isOfflineMode: isOffline + }); + + // 显示状态提示 + if (isOffline) { + wx.showToast({ + title: '已切换为离线模式', + icon: 'none', + duration: 1500 + }); + } else { + wx.showToast({ + title: '已切换为在线模式', + icon: 'success', + duration: 1500 + }); + } + + } catch (error) { + console.error('切换离线模式失败:', error); + wx.showToast({ + title: '切换失败', + icon: 'error' + }); + } + }, + + // 加载历史记录统计 + loadHistoryStats: function() { + const historyManager = require('../../utils/history-manager.js'); + const stats = historyManager.getHistoryStats(); + this.setData({ + totalHistory: stats.scanCount + }); + }, + + // 加载最近使用项目 + loadRecentItems: function() { + try { + var scanHistory = wx.getStorageSync('scanHistory') || []; + var self = this; + + // 只显示扫描历史,按时间排序 + var allItems = scanHistory + .sort(function(a, b) { + return (b.scanTime || b.timestamp || 0) - (a.scanTime || a.timestamp || 0); + }) + .slice(0, 6); + + this.setData({ + recentItems: allItems.map(function(item) { + return self.processRecentItem(item); + }) + }); + } catch (error) { + console.error('加载最近使用项目失败:', error); + this.setData({ recentItems: [] }); + } + }, + + // 处理最近使用项目 + processRecentItem: function(item) { + var content = item.content || ''; + var isGenerated = !!item.type; // 生成的记录有type字段 + + // 识别内容类型 + var contentType = this.identifyContentType(content); + + // 生成标题 + var title = this.generateItemTitle(content, contentType); + + // 生成时间显示 + var time = this.formatItemTime(item.scanTime || item.timestamp); + + // 使用Object.assign替代扩展运算符 + return Object.assign({}, item, { + type: contentType.type, + typeText: contentType.text, + title: title, + time: time, + isGenerated: isGenerated + }); + }, + + // 识别内容类型 + identifyContentType: function(content) { + // URL检测 + if (/^https?:\/\/.+/i.test(content)) { + return { type: 'url', text: '网址' }; + } + + // WiFi信息检测 + if (/^WIFI:/i.test(content)) { + return { type: 'wifi', text: 'WiFi' }; + } + + // 电话号码检测 + if (/^tel:/i.test(content) || /^(\+?86)?1[3-9]\d{9}$/.test(content)) { + return { type: 'phone', text: '电话' }; + } + + // 联系人信息检测 + if (/^BEGIN:VCARD/i.test(content)) { + return { type: 'contact', text: '联系人' }; + } + + // 邮箱检测 + if (/^mailto:/i.test(content) || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content)) { + return { type: 'email', text: '邮箱' }; + } + + // 地理位置检测 + if (/^geo:/i.test(content)) { + return { type: 'location', text: '位置' }; + } + + // 默认为文本 + return { type: 'text', text: '文本' }; + }, + + // 生成项目标题 + generateItemTitle: function(content, contentType) { + var maxLength = 12; + + switch (contentType.type) { + case 'wifi': + var wifiMatch = content.match(/S:([^;]*)/); + return wifiMatch ? wifiMatch[1] : '未知网络'; + + case 'url': + try { + var url = new URL(content); + return url.hostname; + } catch (e) { + return content.substring(0, maxLength); + } + + case 'phone': + return content.replace(/^tel:/, ''); + + case 'email': + return content.replace(/^mailto:/, ''); + + case 'contact': + var nameMatch = content.match(/FN:([^\r\n]*)/); + return nameMatch ? nameMatch[1] : '联系人'; + + default: + return content.length > maxLength ? + content.substring(0, maxLength) + '...' : content; + } + }, + + // 格式化项目时间 + formatItemTime: function(timestamp) { + if (!timestamp) return '未知时间'; + + var now = new Date(); + var time = new Date(timestamp); + var timeDiff = now - time; + + if (timeDiff < 60000) { // 1分钟内 + return '刚刚'; + } else if (timeDiff < 3600000) { // 1小时内 + return Math.floor(timeDiff / 60000) + '分钟前'; + } else if (timeDiff < 86400000) { // 24小时内 + return Math.floor(timeDiff / 3600000) + '小时前'; + } else if (timeDiff < 604800000) { // 7天内 + return Math.floor(timeDiff / 86400000) + '天前'; + } else { + var month = time.getMonth() + 1; + var day = time.getDate(); + return month + '月' + day + '日'; + } + }, + + // 开始扫码 - 根据模式选择 + startScan: function() { + // 检查是否可以操作 + if (!this.checkCanOperate()) return; + + console.log('开始扫码 - 模式:', this.data.scanMode, '离线模式:', this.data.isOfflineMode); + + // 设置扫码动画 + this.setData({ + scanAnimation: true + }); + + // 离线模式下强制使用微信API扫码 + if (this.data.isOfflineMode) { + console.log('离线模式,使用微信API扫码'); + this.startWechatScan(); + return; + } + + // 根据当前模式选择扫码方式 + if (this.data.scanMode === 'custom') { + this.startCustomScan(); + } else { + this.startWechatScan(); + } + }, + + // 自定义摄像头扫码 + startCustomScan: function() { + console.log('启动自定义摄像头扫码'); + + var self = this; + + // 跳转到自定义扫码页面 + wx.navigateTo({ + url: '/pages/custom-scan/custom-scan', + success: function() { + console.log('跳转到自定义扫码页面成功'); + }, + fail: function(err) { + console.error('跳转到自定义扫码页面失败:', err); + // 如果自定义扫码页面跳转失败,回退到微信API + self.startWechatScan(); + }, + complete: function() { + // 停止扫码动画 + setTimeout(() => { + self.setData({ + scanAnimation: false + }); + }, 500); + } + }); + }, + + // 微信API扫码(备用方法) + startWechatScan: function() { + console.log('开始扫码 - 使用微信API'); + + var self = this; + + wx.scanCode({ + onlyFromCamera: false, // 支持摄像头扫码+相册选择 + scanType: ['qrCode', 'datamatrix', 'pdf417'], + success: function(res) { + console.log('微信API扫码成功:', res); + self.handleScanResult(res); + }, + fail: function(err) { + console.error('微信API扫码失败:', err); + + // 检查是否是用户主动取消 + if (err.errMsg && err.errMsg.includes('cancel')) { + // 用户取消,不显示错误提示 + return; + } + + // 真正的扫码失败才显示提示 + wx.showModal({ + title: '识别失败', + content: '无法识别二维码。请确保二维码清晰可见,或尝试选择相册中的图片进行识别。', + confirmText: '知道了', + showCancel: false + }); + }, + complete: function() { + // 停止扫码动画 + self.setData({ + scanAnimation: false + }); + } + }); + }, + + // 处理扫码结果 + handleScanResult: function(result) { + console.log('处理扫码结果:', result); + + // 兼容不同的结果格式 + var content; + if (typeof result === 'string') { + // 直接传入字符串(自定义扫码页面) + content = result; + } else if (result.result) { + // 微信API扫码结果格式 + content = result.result; + } else { + console.error('未知的扫码结果格式:', result); + return; + } + + this.saveScanHistory(content); + this.processScanResult(content); + }, + + // 保存扫描历史 + saveScanHistory: function(content) { + const historyManager = require('../../utils/history-manager.js'); + const success = historyManager.saveScanRecord(content); + + if (success) { + // 更新页面数据 + this.loadData(); + } + }, + + // 处理扫描结果 + processScanResult: function(content) { + console.log('处理扫描内容:', content); + + // URL处理 + if (/^https?:\/\/.+/i.test(content)) { + wx.showModal({ + title: '发现网址', + content: content, + confirmText: '复制', + showCancel: false, + success: function(res) { + if (res.confirm) { + // 复制到剪贴板 + wx.setClipboardData({ + data: content, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // WiFi信息处理 + if (/^WIFI:/i.test(content)) { + var wifiMatch = content.match(/WIFI:T:([^;]*);S:([^;]*);P:([^;]*);H:([^;]*);?/); + if (wifiMatch) { + var wifiInfo = '网络名称: ' + wifiMatch[2] + '\n' + + '密码: ' + wifiMatch[3] + '\n' + + '加密类型: ' + wifiMatch[1]; + + wx.showModal({ + title: 'WiFi信息', + content: wifiInfo, + confirmText: '复制密码', + cancelText: '知道了', + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: wifiMatch[3], + success: function() { + wx.showToast({ + title: '密码已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + } + + // 电话号码处理 + if (/^tel:/i.test(content) || /^(\+?86)?1[3-9]\d{9}$/.test(content)) { + var phoneNumber = content.replace(/^tel:/, ''); + wx.showModal({ + title: '电话号码', + content: phoneNumber, + confirmText: '拨打', + cancelText: '复制', + success: function(res) { + if (res.confirm) { + wx.makePhoneCall({ + phoneNumber: phoneNumber, + fail: function() { + wx.showToast({ + title: '拨打失败', + icon: 'none' + }); + } + }); + } else if (res.cancel) { + wx.setClipboardData({ + data: phoneNumber, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // 邮箱处理 + if (/^mailto:/i.test(content) || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(content)) { + var email = content.replace(/^mailto:/, ''); + wx.showModal({ + title: '邮箱地址', + content: email, + confirmText: '复制', + showCancel: false, + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: email, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + return; + } + + // 普通文本 + wx.showModal({ + title: '扫描结果', + content: content.length > 100 ? content.substring(0, 100) + '...' : content, + confirmText: '复制', + success: function(res) { + if (res.confirm) { + wx.setClipboardData({ + data: content, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + } + }); + } + } + }); + }, + + // 检查是否可以操作 + checkCanOperate: function() { + if (this.data.isInitializing) { + wx.showToast({ + title: '初始化中,请稍候...', + icon: 'loading', + duration: 1500 + }); + return false; + } + return true; + }, + + // 前往生成二维码页面 + goToGenerateQR: function() { + if (!this.checkCanOperate()) return; + + wx.navigateTo({ + url: '/pages/generate-qr/generate-qr' + }); + }, + + // 前往生成条形码页面 + goToGenerateBarcode: function() { + if (!this.checkCanOperate()) return; + + wx.navigateTo({ + url: '/pages/generate-barcode/generate-barcode' + }); + }, + + // 前往历史记录页面 + goToHistory: function() { + if (!this.checkCanOperate()) return; + + wx.navigateTo({ + url: '/pages/history/history' + }); + }, + + + + // 打开相册扫码 + chooseImage: function() { + // 检查是否可以操作 + if (!this.checkCanOperate()) return; + + console.log('打开相册扫码'); + + var self = this; + + // 直接调用扫码,但允许从相册选择 + wx.scanCode({ + onlyFromCamera: false, // 允许从相册选择 + scanType: ['qrCode', 'datamatrix', 'pdf417'], + success: function(res) { + console.log('图片识别成功:', res); + self.handleScanResult(res); + }, + fail: function(err) { + console.error('图片识别失败:', err); + + // 检查是否是用户主动取消 + if (err.errMsg && err.errMsg.includes('cancel')) { + // 用户取消,不显示错误提示 + return; + } + + // 真正的识别失败才显示提示 + wx.showModal({ + title: '识别失败', + content: '无法识别图片中的二维码。请确保图片清晰且包含标准二维码(不支持微型二维码和条形码)。\n\n提示:点击"打开相册"后,请选择"从相册选择"选项。', + confirmText: '知道了', + showCancel: false + }); + } + }); + }, + + // 查看最近使用项目 + viewRecentItem: function(e) { + var item = e.currentTarget.dataset.item; + + // 为弹窗准备完整的数据 + var fullItem = Object.assign({}, item, { + sourceText: item.isGenerated ? '生成' : '扫描', + fullTime: this.formatFullTime(new Date(item.scanTime || item.timestamp)) + }); + + this.setData({ + currentRecentItem: fullItem, + showRecentDetail: true + }); + }, + + // 隐藏最近使用详情弹窗 + hideRecentDetail: function() { + this.setData({ + showRecentDetail: false, + currentRecentItem: null + }); + }, + + // 阻止事件冒泡 + stopPropagation: function() { + // 空函数,用于阻止事件冒泡 + }, + + // 复制最近使用内容 + copyRecentContent: function() { + var content = this.data.currentRecentItem.content; + var self = this; + + wx.setClipboardData({ + data: content, + success: function() { + wx.showToast({ + title: '已复制', + icon: 'success' + }); + self.hideRecentDetail(); + }, + fail: function() { + wx.showToast({ + title: '复制失败', + icon: 'none' + }); + } + }); + }, + + // 分享最近使用内容 + shareRecentContent: function() { + var item = this.data.currentRecentItem; + var self = this; + + wx.showActionSheet({ + itemList: ['发送给朋友', '分享到朋友圈', '复制内容'], + success: function(res) { + switch (res.tapIndex) { + case 0: + // 发送给朋友的逻辑 + wx.showToast({ + title: '功能开发中', + icon: 'none' + }); + break; + case 1: + // 分享到朋友圈的逻辑 + wx.showToast({ + title: '功能开发中', + icon: 'none' + }); + break; + case 2: + // 复制内容 + self.copyRecentContent(); + break; + } + + self.hideRecentDetail(); + } + }); + }, + + // 格式化完整时间 + formatFullTime: function(date) { + var year = date.getFullYear(); + var month = String(date.getMonth() + 1).padStart(2, '0'); + var day = String(date.getDate()).padStart(2, '0'); + var hour = String(date.getHours()).padStart(2, '0'); + var minute = String(date.getMinutes()).padStart(2, '0'); + var second = String(date.getSeconds()).padStart(2, '0'); + + return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; + }, + + // 广告点击 + onAdClick: function() { + wx.showToast({ + title: '广告跳转', + icon: 'none' + }); + }, + + // 关闭广告 + closeAd: function() { + this.setData({ + showAd: false + }); + }, + + // 分享功能 + onShareAppMessage: function() { + return { + title: '小籽二维码 - 扫码生成一站式服务', + path: '/pages/index/index' + }; + }, + + // 显示扫码模式选择器 + showScanModeSelector: function() { + console.log('显示扫码模式选择器'); + this.setData({ + showModeSelector: true + }); + + // 触感反馈 + wx.vibrateShort({ + type: 'medium' + }); + }, + + // 隐藏扫码模式选择器 + hideModeSelector: function() { + this.setData({ + showModeSelector: false + }); + }, + + // 设置扫码模式 + setScanMode: function(e) { + var mode = e.currentTarget.dataset.mode; + console.log('设置扫码模式:', mode); + + this.setData({ + scanMode: mode, + showModeSelector: false + }); + + // 触感反馈 + wx.vibrateShort({ + type: 'light' + }); + + // 显示提示 + var modeText = mode === 'custom' ? '自定义摄像头' : '微信API扫码'; + wx.showToast({ + title: '已切换到' + modeText, + icon: 'none', + duration: 2000 + }); + + // 保存模式设置到本地存储 + try { + wx.setStorageSync('scanMode', mode); + } catch (error) { + console.error('保存扫码模式失败:', error); + } + }, + + // 停止事件冒泡(模式选择器用) + stopPropagation: function() { + // 阻止事件冒泡 + } +}); diff --git a/pages/index/index.json b/pages/index/index.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/pages/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/pages/index/index.wxml b/pages/index/index.wxml new file mode 100644 index 0000000..5da8c28 --- /dev/null +++ b/pages/index/index.wxml @@ -0,0 +1,236 @@ + + + + + + 小籽二维码 + 扫码生成 · 简单便捷 + + + + + + + + + 初始化中 + 正在连接服务,请稍候... + + + + + + + + + + + + 离线模式 + 部分功能受限,扫码功能正常使用 + + + + + + + + + + + + + + + + + + 生成二维码 + 文本 · 链接 · 名片 · WiFi + + + + + + + + + + + + 条形码 + Code128 · EAN13 · Code39 + + + + + + + + + + 扫描记录 + {{totalHistory}}条记录 + + + + + + + + + + 打开相册 + 识别图中码 + + + + + + + + + + + + + + 最近扫码 + + 查看全部 + + + + + + + + + + + + + + + + + + {{item.title}} + {{item.time}} + + + + + + + + + + + + + 记录详情 + + + + + + + + 类型 + {{currentRecentItem.typeText}} + + + 来源 + {{currentRecentItem.sourceText}} + + + 时间 + {{currentRecentItem.fullTime}} + + + 内容 + + {{currentRecentItem.content}} + + + + + + + 复制 + + + 分享 + + + + + + + + + + + + 扫一扫 + + + + + + + + + + + 选择扫码模式 + × + + + + 📱 + + 自定义摄像头 + 拍照识别,支持手动控制 + + + + + 🔍 + + 微信API扫码 + 系统自动识别,速度更快 + + + + + + diff --git a/pages/index/index.wxss b/pages/index/index.wxss new file mode 100644 index 0000000..63da4cf --- /dev/null +++ b/pages/index/index.wxss @@ -0,0 +1,1050 @@ +/**二维码工具箱首页样式 - iOS风格**/ + +/* 引入本地图标样式 */ +@import "../../assets/icons/icons.wxss"; + +/* 全局设置 */ +page { + background-color: #f8f9fa; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif; + color: #1d1d1f; + padding: 0; + margin: 0; +} + +/* 主容器 */ +.container { + height: 100vh; + background-color: #f8f9fa; + box-sizing: border-box; +} + +/* Logo和标题区域 */ +.header-section { + padding: 40rpx 40rpx 40rpx; /* 恢复合理的内边距,状态栏下方留适当空间 */ + text-align: center; + background: linear-gradient(135deg, #e8f5e8 0%, #f0fdf4 100%); +} + +.app-logo { + margin-bottom: 20rpx; +} + +.logo-icon { + width: 150rpx; + height: 150rpx; + margin: 0 auto; + border-radius: 28rpx; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 32rpx rgba(34, 197, 94, 0.3); + overflow: hidden; +} + +.icon-qr { + font-size: 64rpx; + color: white; +} + +.logo-icon-img { + width: 150rpx; + height: 150rpx; + border-radius: 28rpx; +} + +.app-title { + display: block; + font-size: 64rpx; + font-weight: 700; + color: #1d1d1f; + margin-bottom: 10rpx; + letter-spacing: -1rpx; +} + +.app-subtitle { + display: block; + font-size: 28rpx; + color: #6b7280; + font-weight: 400; +} + +/* 功能按钮区域 */ +.function-section { + padding: 0 40rpx; + margin-bottom: 40rpx; +} + +.function-grid { + display: grid; + grid-template-columns: 2fr 1fr; + grid-template-rows: auto auto; + gap: 24rpx; + grid-template-areas: + "large album" + "small history"; +} + +/* 功能卡片基础样式 */ +.function-card { + background: white; + border-radius: 24rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + transition: all 0.3s ease; + border: 1rpx solid rgba(0, 0, 0, 0.02); +} + +.function-card:active { + transform: scale(0.98); + box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.08); +} + +/* 各卡片激活状态保持原有颜色 */ +.function-card.large:active { + background: linear-gradient(135deg, #16a34a 0%, #15803d 100%); +} + +.function-card.small:active { + background: linear-gradient(135deg, #f56500 0%, #ea580c 100%); +} + +.function-card.history:active { + background: linear-gradient(135deg, #1d4ed8 0%, #1e40af 100%); +} + +.function-card.album:active { + background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); +} + +/* 大卡片 - 生成二维码 */ +.function-card.large { + grid-area: large; + background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%); + color: white; +} + +.function-card.large .card-title { + color: white; +} + +.function-card.large .card-subtitle { + color: rgba(255, 255, 255, 0.8); +} + +/* 打开相册卡片 - 右上 */ +.function-card.album { + grid-area: album; + background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); + color: white; +} + +.function-card.album .card-title { + color: white; +} + +.function-card.album .card-subtitle { + color: rgba(255, 255, 255, 0.8); +} + +/* 扫描记录卡片 - 右下 */ +.function-card.history { + grid-area: history; + background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); + color: white; +} + +.function-card.history .card-title { + color: white; +} + +.function-card.history .card-subtitle { + color: rgba(255, 255, 255, 0.8); +} + +/* 小卡片 - 条形码 */ +.function-card.small { + grid-area: small; + background: linear-gradient(135deg, #ff6b35 0%, #f56500 100%); + color: white; +} + +.function-card.small .card-title { + color: white; +} + +.function-card.small .card-subtitle { + color: rgba(255, 255, 255, 0.8); +} + +/* 卡片内容 */ +.card-content { + display: flex; + flex-direction: column; + height: 100%; +} + +.card-icon { + margin-bottom: 16rpx; + font-size: 48rpx; +} + +.large-icon { + font-size: 56rpx; +} + +.medium-icon { + font-size: 48rpx; +} + +.small-icon { + font-size: 40rpx; +} + +/* 功能卡片图标样式 */ +.card-icon-img { + width: 70rpx; + height: 70rpx; +} + +.card-title { + font-size: 32rpx; + font-weight: 600; + margin-bottom: 8rpx; + color: #1d1d1f; +} + +.card-subtitle { + font-size: 24rpx; + color: #6b7280; + margin-bottom: 16rpx; + line-height: 1.4; +} + +/* 移除了选项标签和选择器相关样式,界面更简洁 */ + +/* 广告Banner区域 */ +.ad-section { + margin: 0 40rpx 40rpx; +} + +.ad-banner { + background: white; + border-radius: 20rpx; + padding: 32rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04); + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + overflow: hidden; +} + +.ad-banner::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 8rpx; + height: 100%; + background: linear-gradient(135deg, #ff6b35, #f56500); +} + +.ad-content { + display: flex; + align-items: center; + flex: 1; +} + +.ad-icon { + width: 80rpx; + height: 80rpx; + background: linear-gradient(135deg, #ff6b35, #f56500); + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 24rpx; + font-size: 36rpx; + color: white; +} + +.ad-text { + flex: 1; +} + +.ad-title { + display: block; + font-size: 28rpx; + font-weight: 600; + color: #1d1d1f; + margin-bottom: 4rpx; +} + +.ad-desc { + display: block; + font-size: 24rpx; + color: #6b7280; +} + +.ad-close { + width: 48rpx; + height: 48rpx; + border-radius: 24rpx; + background: #f3f4f6; + display: flex; + align-items: center; + justify-content: center; + margin-left: 16rpx; + font-size: 28rpx; + color: #9ca3af; +} + +/* 最近记录区域 */ +.recent-section { + margin: 0 40rpx 40rpx; +} + +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24rpx; +} + +.section-title { + font-size: 40rpx; + font-weight: 700; + color: #1d1d1f; +} + +.section-action { + display: flex; + align-items: center; + gap: 8rpx; +} + +.action-text { + font-size: 24rpx; + color: #22c55e; + font-weight: 500; +} + +.section-action .ion { + font-size: 20rpx; + color: #22c55e; +} + +.recent-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20rpx; + padding: 8rpx 0; +} + +.recent-item { + text-align: center; +} + +.item-preview { + width: 120rpx; + height: 120rpx; + background: white; + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 12rpx; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); +} + +.item-thumbnail { + width: 100%; + height: 100%; + border-radius: 20rpx; +} + +.item-icon { + font-size: 36rpx; + color: #22c55e; +} + +.item-icon-img { + width: 36rpx; + height: 36rpx; +} + +.item-title { + font-size: 22rpx; + color: #1d1d1f; + font-weight: 500; + display: block; + margin-bottom: 4rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.item-time { + font-size: 20rpx; + color: #9ca3af; + display: block; +} + +/* 底部安全区域 */ +.bottom-safe-area { + height: 200rpx; /* 确保最后的内容不被悬浮按钮遮挡 */ +} + +/* 底部悬浮扫码按钮 */ +.scan-float-button { + position: fixed; + bottom: 80rpx; + left: 50%; + transform: translateX(-50%); + width: 160rpx; + height: 160rpx; + background: linear-gradient(135deg, #22c55e, #16a34a); + border-radius: 50%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 32rpx rgba(34, 197, 94, 0.4); + z-index: 1000; + transition: all 0.3s ease; + animation: breathe 3s infinite ease-in-out; +} + +.scan-float-button:active { + transform: translateX(-50%) scale(0.95); +} + +.scan-button-content { + display: flex; + flex-direction: column; + align-items: center; + z-index: 2; +} + +.scan-icon { + margin-bottom: 8rpx; + font-size: 48rpx; + color: white; +} + +/* 扫码按钮图标样式 */ +.scan-icon-img { + width: 48rpx; + height: 48rpx; +} + +.scan-text { + font-size: 20rpx; + color: white; + font-weight: 600; +} + +/* 扫码按钮向外放射波纹效果 - 绿色到白色渐变 */ +.scan-ripple { + position: absolute; + width: 160rpx; + height: 160rpx; + border: 4rpx solid rgba(34, 197, 94, 0.8); + border-radius: 50%; + animation: rippleOutward 2s infinite ease-out; + top: 0; + left: 0; +} + +.scan-ripple-2 { + animation-delay: 0.6s; + border-color: rgba(34, 197, 94, 0.6); + border-width: 3rpx; +} + +.scan-ripple-3 { + animation-delay: 1.2s; + border-color: rgba(34, 197, 94, 0.4); + border-width: 2rpx; +} + +@keyframes rippleOutward { + 0% { + transform: scale(1); + opacity: 1; + border-color: rgba(34, 197, 94, 0.8); + border-width: 4rpx; + } + 25% { + opacity: 0.9; + border-color: rgba(34, 197, 94, 0.6); + border-width: 3rpx; + } + 50% { + opacity: 0.6; + border-color: rgba(120, 220, 150, 0.5); + border-width: 2rpx; + } + 75% { + opacity: 0.3; + border-color: rgba(200, 240, 210, 0.4); + border-width: 1rpx; + } + 100% { + transform: scale(2.2); + opacity: 0; + border-color: rgba(255, 255, 255, 0.2); + border-width: 1rpx; + } +} + +/* 扫码时的快速波纹效果 */ +.scanning .scan-ripple { + animation-duration: 1s; + border-color: rgba(34, 197, 94, 1); + border-width: 5rpx; +} + +.scanning .scan-ripple-2 { + animation-delay: 0.3s; + border-color: rgba(34, 197, 94, 0.8); +} + +.scanning .scan-ripple-3 { + animation-delay: 0.6s; + border-color: rgba(34, 197, 94, 0.6); +} + +/* 呼吸灯效果 */ +@keyframes breathe { + 0%, 100% { + box-shadow: 0 8rpx 32rpx rgba(34, 197, 94, 0.4); + } + 50% { + box-shadow: 0 8rpx 40rpx rgba(34, 197, 94, 0.6); + } +} + +/* 响应式适配 */ +@media (max-width: 375px) { + .function-grid { + gap: 20rpx; + } + + .function-card { + padding: 24rpx; + } + + .app-title { + font-size: 56rpx; + } +} + +/* 深色模式适配 */ +@media (prefers-color-scheme: dark) { + page { + background-color: #000000; + color: #ffffff; + } + + .container { + background-color: #000000; + } + + .header-section { + background: linear-gradient(135deg, #1a2e1a 0%, #0f1f0f 100%); + } + + .app-title { + color: #ffffff; + } + + .function-card { + background: #1c1c1e; + border-color: rgba(255, 255, 255, 0.1); + } + + .card-title { + color: #ffffff; + } + + .ad-banner { + background: #1c1c1e; + } + + .ad-title { + color: #ffffff; + } + + .item-preview { + background: #1c1c1e; + } + + .item-title { + color: #ffffff; + } +} + +/* 详情弹窗样式 */ +.detail-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.detail-modal.show { + opacity: 1; + visibility: visible; +} + +.modal-content { + width: 90%; + max-width: 600rpx; + background: white; + border-radius: 24rpx; + overflow: hidden; + transform: scale(0.9); + transition: transform 0.3s ease; +} + +.detail-modal.show .modal-content { + transform: scale(1); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + border-bottom: 1rpx solid #f1f5f9; +} + +.modal-title { + font-size: 32rpx; + font-weight: 600; + color: #1e293b; +} + +.modal-close { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: #f8fafc; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.modal-close:active { + transform: scale(0.9); + background: #e2e8f0; +} + +.modal-close text { + font-size: 24rpx; + color: #64748b; +} + +.modal-body { + padding: 32rpx; + max-height: 60vh; + overflow-y: auto; +} + +.detail-item { + display: flex; + margin-bottom: 24rpx; +} + +.detail-item.full { + flex-direction: column; +} + +.detail-label { + font-size: 26rpx; + color: #64748b; + font-weight: 500; + width: 120rpx; + flex-shrink: 0; +} + +.detail-value { + font-size: 26rpx; + color: #1e293b; + flex: 1; +} + +.detail-content { + margin-top: 12rpx; + padding: 20rpx; + background: #f8fafc; + border-radius: 12rpx; + border: 1rpx solid #e2e8f0; +} + +.detail-text { + font-size: 26rpx; + color: #1e293b; + line-height: 1.6; + word-break: break-all; +} + +.modal-actions { + display: flex; + gap: 16rpx; + padding: 32rpx; + border-top: 1rpx solid #f1f5f9; +} + +.modal-btn { + flex: 1; + height: 80rpx; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 500; + transition: all 0.3s ease; +} + +.modal-btn.primary { + background: linear-gradient(135deg, #22c55e, #16a34a); + color: white; + box-shadow: 0 4rpx 16rpx rgba(34, 197, 94, 0.3); +} + +.modal-btn.primary:active { + transform: translateY(2rpx); + box-shadow: 0 2rpx 8rpx rgba(34, 197, 94, 0.3); +} + +.modal-btn.secondary { + background: #f8fafc; + color: #475569; + border: 1rpx solid #e2e8f0; +} + +.modal-btn.secondary:active { + background: #e2e8f0; +} + +/* 扫码模式选择器样式 */ +.scan-mode-text { + font-size: 18rpx; + color: rgba(255, 255, 255, 0.7); + margin-top: 4rpx; + font-weight: 400; +} + +.mode-selector-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + padding: 40rpx; + backdrop-filter: blur(4rpx); +} + +.mode-selector { + width: 100%; + max-width: 600rpx; + background: #ffffff; + border-radius: 28rpx; + overflow: hidden; + animation: modeShow 0.3s ease-out; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15); +} + +@keyframes modeShow { + 0% { + opacity: 0; + transform: scale(0.8) translateY(40rpx); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.mode-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 40rpx 40rpx 20rpx; + border-bottom: 1rpx solid #f0f0f0; +} + +.mode-title { + font-size: 36rpx; + font-weight: bold; + color: #333333; +} + +.mode-close { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: #666666; + transition: all 0.3s ease; +} + +.mode-close:active { + background: #e5e5e5; + transform: scale(0.9); +} + +.mode-options { + padding: 30rpx 40rpx 40rpx; +} + +.mode-option { + display: flex; + align-items: center; + padding: 30rpx 24rpx; + border-radius: 20rpx; + margin-bottom: 16rpx; + transition: all 0.3s ease; + position: relative; + background: #f8f9fa; + border: 2rpx solid transparent; +} + +.mode-option:last-child { + margin-bottom: 0; +} + +.mode-option.active { + background: linear-gradient(135deg, #667eea15, #764ba215); + border-color: #667eea; + box-shadow: 0 4rpx 20rpx rgba(102, 126, 234, 0.15); +} + +.mode-option:active { + transform: scale(0.98); + background: #f0f0f0; +} + +.mode-option.active:active { + background: linear-gradient(135deg, #667eea20, #764ba220); +} + +.mode-icon { + width: 80rpx; + height: 80rpx; + border-radius: 16rpx; + background: linear-gradient(135deg, #667eea, #764ba2); + display: flex; + align-items: center; + justify-content: center; + font-size: 36rpx; + margin-right: 24rpx; + flex-shrink: 0; +} + +.mode-info { + flex: 1; + display: flex; + flex-direction: column; +} + +.mode-name { + font-size: 32rpx; + font-weight: 600; + color: #333333; + margin-bottom: 8rpx; +} + +.mode-desc { + font-size: 26rpx; + color: #666666; + line-height: 1.4; +} + +.mode-check { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: #667eea; + color: white; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + font-weight: bold; + margin-left: 16rpx; + flex-shrink: 0; +} + +/* 初始化横幅 */ +.init-banner { + margin-top: 24rpx; + background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); + border-radius: 16rpx; + padding: 16rpx 20rpx; + border: 1rpx solid #3b82f6; +} + +.init-content { + display: flex; + align-items: center; + gap: 12rpx; +} + +.init-loading { + display: flex; + align-items: center; + justify-content: center; + width: 28rpx; + height: 28rpx; +} + +.loading-spinner { + width: 20rpx; + height: 20rpx; + border: 2rpx solid #93c5fd; + border-top: 2rpx solid #3b82f6; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.init-text { + flex: 1; + display: flex; + flex-direction: column; + gap: 4rpx; +} + +.init-title { + font-size: 28rpx; + font-weight: 600; + color: #1e40af; + line-height: 1.2; +} + +.init-desc { + font-size: 24rpx; + color: #2563eb; + line-height: 1.3; +} + +/* 离线模式横幅 */ +.offline-banner { + margin-top: 24rpx; + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + border-radius: 16rpx; + padding: 16rpx 20rpx; + border: 1rpx solid #fbbf24; +} + +.offline-content { + display: flex; + align-items: center; + justify-content: center; + gap: 12rpx; + position: relative; +} + +.offline-icon { + position: absolute; + left: 0; + display: flex; + align-items: center; + justify-content: center; + width: 32rpx; + height: 32rpx; +} + +.offline-icon-img { + width: 24rpx; + height: 24rpx; +} + +.offline-text { + display: flex; + flex-direction: column; + gap: 4rpx; + text-align: center; +} + +.offline-title { + font-size: 28rpx; + font-weight: 600; + color: #92400e; + line-height: 1.2; +} + +.offline-desc { + font-size: 24rpx; + color: #a16207; + line-height: 1.3; +} + +/* 离线模式测试开关 */ +.offline-switch-container { + margin-top: 24rpx; + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border-radius: 16rpx; + padding: 20rpx; + border: 1rpx solid #0ea5e9; +} + +.offline-switch-content { + display: flex; + align-items: center; + justify-content: space-between; +} + +.switch-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4rpx; +} + +.switch-title { + font-size: 28rpx; + font-weight: 600; + color: #0c4a6e; + line-height: 1.2; +} + +.switch-desc { + font-size: 24rpx; + color: #0369a1; + line-height: 1.3; +} + +.offline-switch { + transform: scale(1.1); +} + + diff --git a/pages/logs/logs.js b/pages/logs/logs.js new file mode 100644 index 0000000..85f6aac --- /dev/null +++ b/pages/logs/logs.js @@ -0,0 +1,18 @@ +// logs.js +const util = require('../../utils/util.js') + +Page({ + data: { + logs: [] + }, + onLoad() { + this.setData({ + logs: (wx.getStorageSync('logs') || []).map(log => { + return { + date: util.formatTime(new Date(log)), + timeStamp: log + } + }) + }) + } +}) diff --git a/pages/logs/logs.json b/pages/logs/logs.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/pages/logs/logs.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/pages/logs/logs.wxml b/pages/logs/logs.wxml new file mode 100644 index 0000000..85cf1bf --- /dev/null +++ b/pages/logs/logs.wxml @@ -0,0 +1,6 @@ + + + + {{index + 1}}. {{log.date}} + + diff --git a/pages/logs/logs.wxss b/pages/logs/logs.wxss new file mode 100644 index 0000000..33f9d9e --- /dev/null +++ b/pages/logs/logs.wxss @@ -0,0 +1,16 @@ +page { + height: 100vh; + display: flex; + flex-direction: column; +} +.scrollarea { + flex: 1; + overflow-y: hidden; +} +.log-item { + margin-top: 20rpx; + text-align: center; +} +.log-item:last-child { + padding-bottom: env(safe-area-inset-bottom); +} diff --git a/project.config.json b/project.config.json new file mode 100644 index 0000000..7ba177c --- /dev/null +++ b/project.config.json @@ -0,0 +1,38 @@ +{ + "compileType": "miniprogram", + "libVersion": "trial", + "packOptions": { + "ignore": [], + "include": [] + }, + "setting": { + "coverView": true, + "es6": false, + "postcss": true, + "minified": true, + "enhance": true, + "showShadowRootInWxmlPanel": true, + "packNpmRelationList": [], + "compileWorklet": false, + "uglifyFileName": false, + "uploadWithSourceMap": true, + "packNpmManually": false, + "minifyWXSS": true, + "minifyWXML": true, + "localPlugins": false, + "condition": false, + "swc": false, + "disableSWC": true, + "disableUseStrict": false, + "useCompilerPlugins": false, + "ignoreUploadUnusedFiles": true, + "useIsolateContext": false + }, + "condition": {}, + "editorSetting": { + "tabIndent": "auto", + "tabSize": 2 + }, + "appid": "touristappid", + "simulatorPluginLibVersion": {} +} \ No newline at end of file diff --git a/sitemap.json b/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/theme.json b/theme.json new file mode 100644 index 0000000..ebfa485 --- /dev/null +++ b/theme.json @@ -0,0 +1,14 @@ +{ + "light": { + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#f8f9fa", + "backgroundTextStyle": "dark" + }, + "dark": { + "navigationBarBackgroundColor": "#000000", + "navigationBarTextStyle": "white", + "backgroundColor": "#000000", + "backgroundTextStyle": "light" + } +} \ No newline at end of file diff --git a/utils/api-service.js b/utils/api-service.js new file mode 100644 index 0000000..e31e8bc --- /dev/null +++ b/utils/api-service.js @@ -0,0 +1,481 @@ +// API服务文件 +const ConfigManager = require('../config/config-manager.js'); +const ApiConfig = ConfigManager.getApiConfig(); +const MessageUtil = require('./message-util.js'); + +class ApiService { + constructor() { + this.config = ApiConfig; + this.userUniqueId = null; + this.accessToken = null; + this.userCode = null; + this.userInfo = null; + this.isLoggedIn = false; + this.init(); + } + + // 初始化 + init() { + this.ensureUserUniqueId(); + this.loadAuthInfo(); + } + + // 加载认证信息 + loadAuthInfo() { + try { + const authKeys = this.config.auth.storageKeys; + this.accessToken = wx.getStorageSync(authKeys.accessToken); + this.userCode = wx.getStorageSync(authKeys.userCode); + this.userInfo = wx.getStorageSync(authKeys.userInfo); + this.isLoggedIn = wx.getStorageSync(authKeys.isLoggedIn) || false; + } catch (error) { + console.error('加载认证信息失败:', error); + this.clearAuthInfo(); + } + } + + // 保存认证信息 + saveAuthInfo(authData) { + try { + const authKeys = this.config.auth.storageKeys; + + // 提取关键信息 + this.accessToken = authData.token; + this.userCode = authData.user_code; + this.userInfo = authData; + this.isLoggedIn = true; + + // 验证必要信息是否存在 + if (!this.accessToken) { + console.error('警告:token为空'); + } + if (!this.userCode) { + console.error('警告:user_code为空'); + } + + // 保存到本地存储 + wx.setStorageSync(authKeys.accessToken, this.accessToken); + wx.setStorageSync(authKeys.userCode, this.userCode); + wx.setStorageSync(authKeys.userInfo, this.userInfo); + wx.setStorageSync(authKeys.isLoggedIn, true); + + console.log('认证信息保存成功'); + + } catch (error) { + console.error('保存认证信息失败:', error); + } + } + + // 清除认证信息 + clearAuthInfo() { + try { + const authKeys = this.config.auth.storageKeys; + this.accessToken = null; + this.userCode = null; + this.userInfo = null; + this.isLoggedIn = false; + + wx.removeStorageSync(authKeys.accessToken); + wx.removeStorageSync(authKeys.userCode); + wx.removeStorageSync(authKeys.userInfo); + wx.removeStorageSync(authKeys.isLoggedIn); + } catch (error) { + console.error('清除认证信息失败:', error); + } + } + + // 检查登录状态 + isUserLoggedIn() { + return this.isLoggedIn && this.accessToken && this.userCode; + } + + // 静默登录 + async silentLogin() { + try { + // 获取微信授权码 + const loginResult = await new Promise((resolve, reject) => { + wx.login({ + success: resolve, + fail: reject + }); + }); + + if (!loginResult.code) { + throw new Error('获取微信授权码失败'); + } + + // 调用登录接口 + const response = await this.wxLogin(loginResult.code); + + // 检查登录是否成功 + // 根据接口文档,成功时 result="true" 且 statusCode=3001 + // 注意:statusCode可能是字符串格式 + if (response.result === "true" && (response.statusCode === 3001 || response.statusCode === "3001")) { + console.log('静默登录成功'); + + // 登录成功,保存认证信息 + this.saveAuthInfo(response.data); + + return { + success: true, + userInfo: response.data + }; + } else { + // 登录失败 + console.error('登录失败:', response.message || '未知错误'); + throw new Error(response.message || '登录失败'); + } + } catch (error) { + console.error('静默登录失败:', error); + this.clearAuthInfo(); + throw new Error('服务异常'); + } + } + + // 微信登录接口 + async wxLogin(code) { + return new Promise((resolve, reject) => { + const url = this.config.server.baseUrl + this.config.endpoints.wxLogin; + + // 构建表单数据 + const requestData = { + code: code, + param_corpCode: this.config.auth.corpCode + }; + + wx.request({ + url: url, + method: 'POST', + data: requestData, // 使用表单数据格式 + timeout: this.config.server.timeout, + header: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json' + }, + success: (res) => { + + if (res.statusCode === 200) { + resolve(res.data); + } else { + reject(new Error(`HTTP错误: ${res.statusCode}`)); + } + }, + fail: (error) => { + console.error('登录网络请求失败:', error); + reject(new Error('网络请求失败')); + } + }); + }); + } + + // 确保用户唯一标识 + ensureUserUniqueId() { + try { + let userId = wx.getStorageSync(this.config.userIdentity.storageKey); + if (!userId) { + // 生成唯一标识:前缀 + 时间戳 + 随机数 + userId = this.config.userIdentity.prefix + + Date.now() + '_' + + Math.random().toString(36).substr(2, 9); + wx.setStorageSync(this.config.userIdentity.storageKey, userId); + } + this.userUniqueId = userId; + } catch (error) { + console.error('获取用户唯一标识失败:', error); + // 临时标识 + this.userUniqueId = this.config.userIdentity.prefix + 'temp_' + Date.now(); + } + } + + // 检查频率限制 + checkRateLimit(isBatch = false) { + const now = Date.now(); + const rateConfig = this.config.rateLimit; + + try { + // 检查单个/批量生成间隔 + const intervalKey = isBatch ? + rateConfig.storageKeys.lastBatchGen : + rateConfig.storageKeys.lastSingleGen; + const requiredInterval = isBatch ? + rateConfig.batchGenInterval : + rateConfig.singleGenInterval; + + const lastGenTime = wx.getStorageSync(intervalKey) || 0; + const timeSinceLastGen = now - lastGenTime; + + if (timeSinceLastGen < requiredInterval) { + const waitTime = Math.ceil((requiredInterval - timeSinceLastGen) / 1000); + return { + allowed: false, + message: `请等待${waitTime}秒后再试`, + waitTime: waitTime + }; + } + + // 检查每分钟请求限制 + const history = this.getRequestHistory(); + const currentMinute = Math.floor(now / 60000); + const currentMinuteRequests = history.filter(time => + Math.floor(time / 60000) === currentMinute + ).length; + + if (currentMinuteRequests >= rateConfig.maxRequestsPerMinute) { + return { + allowed: false, + message: '请求过于频繁,请稍后再试', + waitTime: 60 + }; + } + + return { allowed: true }; + } catch (error) { + console.error('频率检查失败:', error); + return { allowed: true }; // 检查失败时允许请求 + } + } + + // 获取请求历史 + getRequestHistory() { + try { + const history = wx.getStorageSync(this.config.rateLimit.storageKeys.requestHistory) || []; + const now = Date.now(); + // 只保留最近一分钟的记录 + return history.filter(time => now - time < 60000); + } catch (error) { + console.error('获取请求历史失败:', error); + return []; + } + } + + // 记录请求时间 + recordRequest(isBatch = false) { + const now = Date.now(); + const rateConfig = this.config.rateLimit; + + try { + // 记录单个/批量生成时间 + const intervalKey = isBatch ? + rateConfig.storageKeys.lastBatchGen : + rateConfig.storageKeys.lastSingleGen; + wx.setStorageSync(intervalKey, now); + + // 记录到请求历史 + const history = this.getRequestHistory(); + history.push(now); + wx.setStorageSync(rateConfig.storageKeys.requestHistory, history); + } catch (error) { + console.error('记录请求时间失败:', error); + } + } + + // 生成二维码/条形码 + async generateCode(params) { + const { + codeType, // 码类型:'qrcode' | 'barcode' + subType, // 子类型:'standard' | 'micro' | 'Code 128' 等 + content, // 内容(单个或数组) + batchMode = false, // 是否批量模式 + options = {} // 其他选项(颜色、尺寸等) + } = params; + + // 频率检查 + const rateCheck = this.checkRateLimit(batchMode); + if (!rateCheck.allowed) { + throw new Error(rateCheck.message); + } + + // 验证参数 + this.validateParams(params); + + // 构建请求数据 + const requestData = this.buildRequestData(params); + + try { + // 记录请求时间 + this.recordRequest(batchMode); + + // 发送请求 + const response = await this.sendRequest(requestData); + return response; + } catch (error) { + console.error('生成请求失败:', error); + throw error; + } + } + + // 验证参数 + validateParams(params) { + const { codeType, subType, content, batchMode } = params; + + if (!codeType || !subType) { + throw new Error('缺少必要的码类型参数'); + } + + if (!content || (Array.isArray(content) && content.length === 0)) { + throw new Error('内容不能为空'); + } + + if (batchMode) { + const contentArray = Array.isArray(content) ? content : [content]; + if (contentArray.length > this.config.rateLimit.maxBatchSize) { + throw new Error(`批量生成最多支持${this.config.rateLimit.maxBatchSize}个`); + } + } + } + + // 构建请求数据 + buildRequestData(params) { + const { codeType, subType, content, batchMode, options = {} } = params; + + // 映射码类型 + let mappedType; + if (codeType === 'qrcode') { + mappedType = this.config.codeTypes.qrcode[subType]; + } else if (codeType === 'barcode') { + mappedType = this.config.codeTypes.barcode[subType]; + } + + if (!mappedType) { + throw new Error('不支持的码类型'); + } + + return { + user_id: this.userUniqueId, + code_type: mappedType, + batch_mode: batchMode, + content: batchMode ? (Array.isArray(content) ? content : [content]) : content, + options: { + foreground_color: options.foregroundColor || '#000000', + background_color: options.backgroundColor || '#ffffff', + size: options.size || 300, + ...options + } + }; + } + + // 发送请求 + sendRequest(data) { + return new Promise((resolve, reject) => { + const url = this.config.server.baseUrl + this.config.endpoints.codeGeneration; + + // 构建请求头,包含认证信息 + const headers = { + 'content-type': 'application/json' + }; + + // 添加认证头(如果已登录) + if (this.isUserLoggedIn()) { + headers['S-Access-Token'] = this.accessToken; + headers['S-Code'] = this.userCode; + } + + wx.request({ + url: url, + method: 'POST', + data: data, + timeout: this.config.server.timeout, + responseType: data.batch_mode ? 'arraybuffer' : 'arraybuffer', // 都用arraybuffer接收二进制数据 + header: headers, + success: (res) => { + // 检查并更新令牌 + this.handleTokenRefresh(res); + + if (res.statusCode === 200) { + resolve({ + success: true, + data: res.data, + isBatch: data.batch_mode + }); + } else { + reject(new Error(`服务器错误: ${res.statusCode}`)); + } + }, + fail: (error) => { + console.error('请求失败:', error); + reject(new Error('网络请求失败,请检查网络连接')); + } + }); + }); + } + + // 处理令牌刷新 + handleTokenRefresh(response) { + try { + const newToken = response.header['S-Access-Token'] || response.header['s-access-token']; + if (newToken && newToken !== this.accessToken) { + console.log('令牌已更新'); + this.accessToken = newToken; + wx.setStorageSync(this.config.auth.storageKeys.accessToken, newToken); + } + } catch (error) { + console.error('处理令牌刷新失败:', error); + } + } + + // 保存图片到相册 + async saveImageToAlbum(arrayBuffer, filename = null) { + try { + // 将arrayBuffer转换为base64 + const base64 = wx.arrayBufferToBase64(arrayBuffer); + const base64Data = 'data:image/png;base64,' + base64; + + // 保存到临时文件 + const tempFilePath = await new Promise((resolve, reject) => { + wx.getFileSystemManager().writeFile({ + filePath: wx.env.USER_DATA_PATH + '/' + (filename || Date.now() + '.png'), + data: arrayBuffer, + success: (res) => { + resolve(wx.env.USER_DATA_PATH + '/' + (filename || Date.now() + '.png')); + }, + fail: reject + }); + }); + + // 保存到相册 + await new Promise((resolve, reject) => { + wx.saveImageToPhotosAlbum({ + filePath: tempFilePath, + success: resolve, + fail: reject + }); + }); + + return { success: true, path: tempFilePath }; + } catch (error) { + console.error('保存图片失败:', error); + throw error; + } + } + + // 保存压缩包(批量生成结果) + async saveBatchResult(arrayBuffer, filename = null) { + try { + const fileName = filename || `qr_batch_${Date.now()}.zip`; + const filePath = wx.env.USER_DATA_PATH + '/' + fileName; + + await new Promise((resolve, reject) => { + wx.getFileSystemManager().writeFile({ + filePath: filePath, + data: arrayBuffer, + success: resolve, + fail: reject + }); + }); + + return { + success: true, + path: filePath, + fileName: fileName + }; + } catch (error) { + console.error('保存批量结果失败:', error); + throw error; + } + } +} + +// 创建单例实例 +const apiService = new ApiService(); + +module.exports = apiService; \ No newline at end of file diff --git a/utils/barcode-simple.js b/utils/barcode-simple.js new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/utils/barcode-simple.js @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/utils/history-manager.js b/utils/history-manager.js new file mode 100644 index 0000000..6fbb9e5 --- /dev/null +++ b/utils/history-manager.js @@ -0,0 +1,254 @@ +// 历史记录管理工具 +const ConfigManager = require('../config/config-manager.js'); +const ApiConfig = ConfigManager.getApiConfig(); + +/** + * 历史记录管理器 + */ +class HistoryManager { + constructor() { + this.config = ApiConfig.history; + } + + /** + * 保存扫码记录 + * @param {string} content - 扫码内容 + * @param {Object} extraData - 额外数据 + */ + saveScanRecord(content, extraData = {}) { + try { + const history = this.getHistory(this.config.storageKeys.scanHistory); + + const newRecord = { + id: Date.now().toString(), + content: content, + scanTime: Date.now(), + timestamp: Date.now(), // 兼容字段 + type: this.config.recordTypes.scan, + ...extraData + }; + + this.addToHistory(history, newRecord, this.config.storageKeys.scanHistory); + return true; + } catch (error) { + console.error('保存扫描记录失败:', error); + return false; + } + } + + /** + * 保存二维码生成记录 + * @param {Object} data - 生成数据 + */ + saveQRGenerateRecord(data) { + try { + const history = this.getHistory(this.config.storageKeys.scanHistory); + + const newRecord = { + id: Date.now(), + content: data.content, + type: data.codeType || (data.type === 'standard' || data.type === 'micro' ? 'qr' : 'barcode'), + subType: data.type || data.barcodeType, + time: new Date().toLocaleString(), + isGenerated: true, + batchMode: data.batchMode || false, + barcodes: data.barcodes || [], + recordType: this.config.recordTypes.qrGenerate + }; + + this.addToHistory(history, newRecord, this.config.storageKeys.scanHistory); + return true; + } catch (error) { + console.error('保存二维码生成记录失败:', error); + return false; + } + } + + /** + * 保存条形码生成记录 + * @param {Object} data - 生成数据 + */ + saveBarcodeRecord(data) { + try { + const history = this.getHistory(this.config.storageKeys.barcodeHistory); + + if (data.batchMode) { + const lines = data.batchContent.split('\n').filter(line => line.trim()); + lines.forEach((content, index) => { + const record = { + id: `${data.timestamp}_${index}`, + type: 'barcode', + subType: data.barcodeType, + content: content.trim(), + foregroundColor: data.foregroundColor, + backgroundColor: data.backgroundColor, + width: data.barcodeWidth, + createTime: data.timestamp + index, + batch: true, + batchIndex: index + 1, + batchTotal: lines.length, + recordType: this.config.recordTypes.barcodeGenerate + }; + + this.addToHistory(history, record, this.config.storageKeys.barcodeHistory, false); + }); + + // 批量模式最后统一保存和限制 + this.limitHistorySize(history); + wx.setStorageSync(this.config.storageKeys.barcodeHistory, history); + } else { + const record = { + id: data.timestamp.toString(), + type: 'barcode', + subType: data.barcodeType, + content: data.inputContent, + foregroundColor: data.foregroundColor, + backgroundColor: data.backgroundColor, + width: data.barcodeWidth, + createTime: data.timestamp, + batch: false, + recordType: this.config.recordTypes.barcodeGenerate + }; + + this.addToHistory(history, record, this.config.storageKeys.barcodeHistory); + } + + return true; + } catch (error) { + console.error('保存条形码记录失败:', error); + return false; + } + } + + /** + * 保存生成历史记录(通用) + * @param {Object} params - 生成参数 + */ + saveGenerateHistory(params) { + try { + const history = this.getHistory(this.config.storageKeys.generateHistory); + + const historyItem = { + id: Date.now(), + time: new Date(), + timestamp: Date.now(), + recordType: this.config.recordTypes.qrGenerate, + ...params + }; + + this.addToHistory(history, historyItem, this.config.storageKeys.generateHistory); + return true; + } catch (error) { + console.error('保存生成历史失败:', error); + return false; + } + } + + /** + * 获取历史记录 + * @param {string} storageKey - 存储键名 + */ + getHistory(storageKey) { + return wx.getStorageSync(storageKey) || []; + } + + /** + * 添加记录到历史中 + * @param {Array} history - 历史记录数组 + * @param {Object} newRecord - 新记录 + * @param {string} storageKey - 存储键名 + * @param {boolean} autoSave - 是否自动保存和限制大小 + */ + addToHistory(history, newRecord, storageKey, autoSave = true) { + // 检查是否已存在相同内容,如果存在则先删除 + const existingIndex = history.findIndex(item => item.content === newRecord.content); + if (existingIndex !== -1) { + history.splice(existingIndex, 1); + } + + // 添加到历史记录开头 + history.unshift(newRecord); + + if (autoSave) { + // 限制历史记录数量 + this.limitHistorySize(history); + + // 保存到本地存储 + wx.setStorageSync(storageKey, history); + } + } + + /** + * 限制历史记录大小 + * @param {Array} history - 历史记录数组 + */ + limitHistorySize(history) { + if (history.length > this.config.maxRecords) { + history.splice(this.config.maxRecords); + } + } + + /** + * 清空指定类型的历史记录 + * @param {string} storageKey - 存储键名 + */ + clearHistory(storageKey) { + try { + wx.removeStorageSync(storageKey); + return true; + } catch (error) { + console.error('清空历史记录失败:', error); + return false; + } + } + + /** + * 删除指定的历史记录项 + * @param {string} storageKey - 存储键名 + * @param {Array} idsToDelete - 要删除的记录ID数组 + */ + deleteHistoryItems(storageKey, idsToDelete) { + try { + const history = this.getHistory(storageKey); + const filteredHistory = history.filter(item => !idsToDelete.includes(item.id)); + wx.setStorageSync(storageKey, filteredHistory); + return true; + } catch (error) { + console.error('删除历史记录失败:', error); + return false; + } + } + + /** + * 获取历史记录统计信息 + */ + getHistoryStats() { + try { + const scanHistory = this.getHistory(this.config.storageKeys.scanHistory); + const generateHistory = this.getHistory(this.config.storageKeys.generateHistory); + const barcodeHistory = this.getHistory(this.config.storageKeys.barcodeHistory); + + return { + scanCount: scanHistory.length, + generateCount: generateHistory.length, + barcodeCount: barcodeHistory.length, + totalCount: scanHistory.length + generateHistory.length + barcodeHistory.length, + maxRecords: this.config.maxRecords + }; + } catch (error) { + console.error('获取历史记录统计失败:', error); + return { + scanCount: 0, + generateCount: 0, + barcodeCount: 0, + totalCount: 0, + maxRecords: this.config.maxRecords + }; + } + } +} + +// 创建单例实例 +const historyManager = new HistoryManager(); + +module.exports = historyManager; \ No newline at end of file diff --git a/utils/jsbarcode-util.js b/utils/jsbarcode-util.js new file mode 100644 index 0000000..40cf34e --- /dev/null +++ b/utils/jsbarcode-util.js @@ -0,0 +1,238 @@ +/** + * JsBarcode 条形码生成工具类 + * 基于JsBarcode库,适配微信小程序环境 + */ + +// 导入JsBarcode库的核心功能 +// 由于小程序环境限制,我们需要手动引入必要的编码模块 + +/** + * Code 128 编码表 + */ +const CODE128_PATTERNS = [ + "11011001100", "11001101100", "11001100110", "10010011000", "10010001100", + "10001001100", "10011001000", "10011000100", "10001100100", "11001001000", + "11001000100", "11000100100", "10110011100", "10011011100", "10011001110", + "10111001100", "10011101100", "10011100110", "11001110010", "11001011100", + "11001001110", "11011100100", "11001110100", "11101101110", "11101001100", + "11100101100", "11100100110", "11101100100", "11100110100", "11100110010", + "11011011000", "11011000110", "11000110110", "10100011000", "10001011000", + "10001000110", "10110001000", "10001101000", "10001100010", "11010001000", + "11000101000", "11000100010", "10110111000", "10110001110", "10001101110", + "10111011000", "10111000110", "10001110110", "11101110110", "11010001110", + "11000101110", "11011101000", "11011100010", "11011101110", "11101011000", + "11101000110", "11100010110", "11101101000", "11101100010", "11100011010", + "11101111010", "11001000010", "11110001010", "10100110000", "10100001100", + "10010110000", "10010000110", "10000101100", "10000100110", "10110010000", + "10110000100", "10011010000", "10011000010", "10000110100", "10000110010", + "11000010010", "11001010000", "11110111010", "11000010100", "10001111010", + "10100111100", "10010111100", "10010011110", "10111100100", "10011110100", + "10011110010", "11110100100", "11110010100", "11110010010", "11011011110", + "11011110110", "11110110110", "10101111000", "10100011110", "10001011110", + "10111101000", "10111100010", "11110101000", "11110100010", "10111011110", + "10111101110", "11101011110", "11110101110", "11010000100", "11010010000", + "11010011100", "1100011101011" +]; + +/** + * Code 128 字符集B映射表 (ASCII 32-127) + */ +const CODE128B_CHARS = {}; +for (let i = 32; i <= 126; i++) { + CODE128B_CHARS[String.fromCharCode(i)] = i - 32; +} + +class JsBarcodeUtil { + /** + * 生成Code 128条形码 + * @param {Object} canvas - 微信小程序canvas对象 + * @param {string} text - 要编码的文本 + * @param {Object} options - 配置选项 + */ + static generateCode128(canvas, text, options = {}) { + const defaultOptions = { + width: 2, + height: 100, + displayValue: false, + fontSize: 12, + fontFamily: 'monospace', + textAlign: 'center', + textPosition: 'bottom', + textMargin: 2, + background: '#ffffff', + lineColor: '#000000', + margin: 10, + marginTop: 10, + marginBottom: 10, + marginLeft: 10, + marginRight: 10 + }; + + const config = Object.assign({}, defaultOptions, options); + + try { + // 验证输入文本 + if (!text || typeof text !== 'string') { + throw new Error('文本不能为空且必须是字符串'); + } + + // 验证字符是否在Code 128B范围内 + for (let char of text) { + if (!(char in CODE128B_CHARS)) { + throw new Error(`字符 "${char}" 不在Code 128B支持范围内`); + } + } + + console.log('[JsBarcodeUtil] 开始生成条形码:', text); + + // 编码数据 + const encodedData = this.encodeCode128B(text); + console.log('[JsBarcodeUtil] 编码完成,模式数量:', encodedData.length); + + // 绘制条形码 + this.drawBarcode(canvas, encodedData, config); + + console.log('[JsBarcodeUtil] 条形码绘制完成'); + return true; + + } catch (error) { + console.error('[JsBarcodeUtil] 生成条形码失败:', error); + throw error; + } + } + + /** + * Code 128B 编码 + * @param {string} text - 要编码的文本 + * @returns {Array} 编码后的模式数组 + */ + static encodeCode128B(text) { + const codes = []; + + // Start Code B + codes.push(104); + + // 数据字符 + let checksum = 104; // 起始码也参与校验和计算 + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const value = CODE128B_CHARS[char]; + codes.push(value); + checksum += value * (i + 1); // 位置权重从1开始 + } + + // 校验码 + const checksumCode = checksum % 103; + codes.push(checksumCode); + + // Stop Code + codes.push(106); + + console.log('[JsBarcodeUtil] 编码序列:', codes); + console.log('[JsBarcodeUtil] 校验和:', checksum, '校验码:', checksumCode); + + return codes; + } + + /** + * 绘制条形码 + * @param {Object} canvas - canvas对象 + * @param {Array} codes - 编码数组 + * @param {Object} config - 配置选项 + */ + static drawBarcode(canvas, codes, config) { + const ctx = canvas.getContext('2d'); + + // 计算总宽度 + let totalBars = 0; + for (let code of codes) { + const pattern = CODE128_PATTERNS[code]; + totalBars += pattern.length; + } + + const canvasWidth = canvas.width; + const canvasHeight = canvas.height; + + console.log('[JsBarcodeUtil] Canvas尺寸检查:', { + canvasWidth, + canvasHeight, + totalBars + }); + + // 清除画布并设置背景 + ctx.fillStyle = config.background; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + + // 计算条形码绘制区域 + const drawWidth = canvasWidth - config.marginLeft - config.marginRight; + const drawHeight = canvasHeight - config.marginTop - config.marginBottom; + const barWidth = drawWidth / totalBars; + + console.log('[JsBarcodeUtil] 绘制参数:', { + canvasWidth, + canvasHeight, + drawWidth, + drawHeight, + totalBars, + barWidth + }); + + // 绘制条形码 + ctx.fillStyle = config.lineColor; + let x = config.marginLeft; + + for (let code of codes) { + const pattern = CODE128_PATTERNS[code]; + + for (let i = 0; i < pattern.length; i++) { + if (pattern[i] === '1') { + // 绘制黑条 + ctx.fillRect(x, config.marginTop, barWidth, drawHeight); + } + x += barWidth; + } + } + + // 如果需要显示文本 + if (config.displayValue) { + this.drawText(ctx, codes, config, canvasWidth, canvasHeight); + } + + // Canvas 2D API需要手动触发绘制 + // 注意:这里不需要调用draw(),因为Canvas 2D是实时绘制的 + } + + /** + * 绘制文本 + * @param {Object} ctx - canvas上下文 + * @param {Array} codes - 编码数组 + * @param {Object} config - 配置选项 + * @param {number} canvasWidth - 画布宽度 + * @param {number} canvasHeight - 画布高度 + */ + static drawText(ctx, codes, config, canvasWidth, canvasHeight) { + // 从编码中还原文本(去掉起始码、校验码、停止码) + let text = ''; + for (let i = 1; i < codes.length - 2; i++) { + const code = codes[i]; + text += String.fromCharCode(code + 32); + } + + ctx.fillStyle = config.lineColor; + ctx.font = `${config.fontSize}px ${config.fontFamily}`; + ctx.textAlign = config.textAlign; + + let textX = canvasWidth / 2; + let textY; + + if (config.textPosition === 'top') { + textY = config.marginTop - config.textMargin; + } else { + textY = canvasHeight - config.marginBottom + config.fontSize + config.textMargin; + } + + ctx.fillText(text, textX, textY); + } +} + +module.exports = JsBarcodeUtil; \ No newline at end of file diff --git a/utils/message-util.js b/utils/message-util.js new file mode 100644 index 0000000..fe1b766 --- /dev/null +++ b/utils/message-util.js @@ -0,0 +1,139 @@ +// 消息提示工具类 +class MessageUtil { + + // 成功提示(短提示) + static showSuccess(message, duration = 2000) { + wx.showToast({ + title: message, + icon: 'success', + duration: duration + }); + } + + // 错误提示(根据内容长度选择合适的提示方式) + static showError(message, options = {}) { + // 如果消息较短,使用toast + if (message.length <= 8) { + wx.showToast({ + title: message, + icon: 'error', + duration: options.duration || 2000 + }); + } else { + // 消息较长,使用modal + wx.showModal({ + title: options.title || '提示', + content: message, + showCancel: false, + confirmText: '确定', + confirmColor: '#007AFF' + }); + } + } + + // 警告提示 + static showWarning(message, options = {}) { + if (message.length <= 8) { + wx.showToast({ + title: message, + icon: 'none', + duration: options.duration || 2000 + }); + } else { + wx.showModal({ + title: options.title || '警告', + content: message, + showCancel: false, + confirmText: '确定', + confirmColor: '#FF9500' + }); + } + } + + // 信息提示 + static showInfo(message, options = {}) { + if (message.length <= 8) { + wx.showToast({ + title: message, + icon: 'none', + duration: options.duration || 2000 + }); + } else { + wx.showModal({ + title: options.title || '信息', + content: message, + showCancel: false, + confirmText: '确定', + confirmColor: '#007AFF' + }); + } + } + + // 确认对话框 + static showConfirm(message, options = {}) { + return new Promise((resolve) => { + wx.showModal({ + title: options.title || '确认', + content: message, + showCancel: true, + cancelText: options.cancelText || '取消', + confirmText: options.confirmText || '确定', + cancelColor: '#000000', + confirmColor: '#007AFF', + success: (res) => { + resolve(res.confirm); + }, + fail: () => { + resolve(false); + } + }); + }); + } + + // 加载提示 + static showLoading(message = '加载中...') { + wx.showLoading({ + title: message, + mask: true + }); + } + + // 隐藏加载提示 + static hideLoading() { + wx.hideLoading(); + } + + // 网络错误专用提示 + static showNetworkError(error) { + let message = '网络连接失败'; + let title = '网络错误'; + + if (error && error.message) { + if (error.message.includes('HTTP错误')) { + message = '服务器响应异常,请稍后重试'; + title = '服务器错误'; + } else if (error.message.includes('网络请求失败')) { + message = '网络连接失败,请检查网络设置'; + title = '网络错误'; + } else if (error.message.includes('服务异常')) { + message = '服务暂时不可用,请稍后重试'; + title = '服务异常'; + } else { + message = error.message; + } + } + + this.showError(message, { title }); + } + + // 服务异常专用提示(简短版本) + static showServiceError() { + wx.showToast({ + title: '服务异常', + icon: 'none', + duration: 2000 + }); + } +} + +module.exports = MessageUtil; \ No newline at end of file diff --git a/utils/qrcode.js b/utils/qrcode.js new file mode 100644 index 0000000..9feff1e --- /dev/null +++ b/utils/qrcode.js @@ -0,0 +1,8 @@ +// 微信小程序二维码生成库包装 +// 兼容 CommonJS 模块系统 + +// 导入原始库 +const weappQrcode = require('../assets/js/weapp.qrcode.esm.js'); + +// 导出函数 +module.exports = weappQrcode.default || weappQrcode; \ No newline at end of file diff --git a/utils/util.js b/utils/util.js new file mode 100644 index 0000000..764bc2c --- /dev/null +++ b/utils/util.js @@ -0,0 +1,19 @@ +const formatTime = date => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + + return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}` +} + +const formatNumber = n => { + n = n.toString() + return n[1] ? n : `0${n}` +} + +module.exports = { + formatTime +} -- Gitee