uni-app 扫码功能

章节介绍

扫码功能已成为移动应用的重要组成部分,它可以快速实现信息录入、产品溯源、支付等多种场景。uni-app 提供了便捷的扫码 API,支持二维码和条形码的扫描,并且可以自定义扫码界面。本章节将详细介绍 uni-app 中的扫码功能实现方法,包括基本扫码、自定义扫码界面、扫码结果处理等内容。

核心知识点

1. 扫码功能基础概念

  • 二维码(QR Code):一种矩阵式二维条码,能够存储更多信息
  • 条形码(Bar Code):一种线性一维条码,只能存储有限的数字和字母
  • 扫码:通过摄像头识别和解码条码信息的过程
  • ZXing:一种开源的条码识别库,被广泛应用于移动应用中
  • 扫码界面:用于显示摄像头画面和引导用户扫码的界面

2. uni-app 扫码 API

uni-app 提供了 uni.scanCode API 来实现扫码功能:

  • 基本用法:调用 uni.scanCode 方法打开默认扫码界面
  • 参数配置:通过参数配置扫码类型、是否显示扫码框等
  • 回调处理:处理扫码成功和失败的回调
  • 自定义界面:通过 scanType 参数和自定义组件实现

3. 扫码类型

uni-app 支持多种扫码类型:

  • 二维码:QR Code、Data Matrix、Aztec 等
  • 条形码:EAN-13、EAN-8、UPC-A、UPC-E、Code 39、Code 128 等
  • 混合模式:同时支持二维码和条形码

4. 扫码场景

扫码功能适用于多种场景:

  • 信息录入:快速录入网址、联系人、文本等信息
  • 产品溯源:扫描商品条码查询产品信息
  • 移动支付:扫描支付码完成支付
  • 门禁系统:扫描二维码开门
  • 票务验证:扫描电子票二维码验证身份
  • 会员卡:扫描会员卡二维码识别用户信息

5. 扫码界面自定义

  • 扫码框样式:自定义扫码框的大小、颜色、样式
  • 引导线:添加扫码引导线,提示用户扫码
  • 扫描动画:添加扫描动画效果,提升用户体验
  • 背景处理:处理扫码界面的背景,如添加半透明遮罩
  • 提示信息:添加扫码提示信息,引导用户操作

6. 扫码结果处理

  • 结果解析:解析扫码获取的信息
  • 结果验证:验证扫码结果的有效性
  • 结果存储:存储扫码历史记录
  • 结果分享:分享扫码获取的信息
  • 错误处理:处理扫码失败的情况

实用案例分析

案例一:基本扫码功能实现

场景:实现基本的二维码和条形码扫描功能。

实现步骤

  1. 调用 uni.scanCode API
  2. 配置扫码参数
  3. 处理扫码结果
  4. 测试扫码功能

代码示例

<template>
  <view class="container">
    <view class="header">
      <text class="title">扫码功能演示</text>
      <text class="subtitle">点击下方按钮开始扫码</text>
    </view>
    
    <view class="button-group">
      <button @click="scanQRCode" class="scan-button qr-button">
        <text class="button-text">扫描二维码</text>
      </button>
      
      <button @click="scanBarCode" class="scan-button bar-button">
        <text class="button-text">扫描条形码</text>
      </button>
      
      <button @click="scanAll" class="scan-button all-button">
        <text class="button-text">扫描所有码</text>
      </button>
    </view>
    
    <view class="result" v-if="scanResult">
      <text class="result-title">扫码结果</text>
      <text class="result-type">类型:{{ scanResult.scanType }}</text>
      <text class="result-text">内容:{{ scanResult.result }}</text>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      scanResult: null
    }
  },
  methods: {
    // 扫描二维码
    scanQRCode() {
      this.scanCode({
        scanType: ['qrCode'],
        title: '扫描二维码'
      })
    },
    
    // 扫描条形码
    scanBarCode() {
      this.scanCode({
        scanType: ['barCode'],
        title: '扫描条形码'
      })
    },
    
    // 扫描所有码
    scanAll() {
      this.scanCode({
        scanType: ['qrCode', 'barCode'],
        title: '扫描二维码/条形码'
      })
    },
    
    // 通用扫码方法
    scanCode(options) {
      uni.scanCode({
        // 扫码类型
        scanType: options.scanType,
        // 是否显示扫码框
        onlyFromCamera: true,
        // 扫码框样式
        frameColor: '#007AFF',
        // 扫码框线条颜色
        scanArea: [20, 20, 70, 70],
        // 成功回调
        success: (res) => {
          console.log('扫码成功:', res)
          this.scanResult = res
          
          // 显示扫码结果
          uni.showToast({
            title: '扫码成功',
            icon: 'success'
          })
          
          // 处理扫码结果
          this.handleScanResult(res)
        },
        // 失败回调
        fail: (err) => {
          console.error('扫码失败:', err)
          uni.showToast({
            title: '扫码失败',
            icon: 'none'
          })
        }
      })
    },
    
    // 处理扫码结果
    handleScanResult(res) {
      const result = res.result
      const scanType = res.scanType
      
      // 根据扫码结果类型进行不同处理
      if (this.isUrl(result)) {
        // 是网址,打开网页
        uni.showModal({
          title: '打开网址',
          content: result,
          success: (modalRes) => {
            if (modalRes.confirm) {
              uni.navigateTo({
                url: `/pages/web-view/web-view?url=${encodeURIComponent(result)}`
              })
            }
          }
        })
      } else if (this.isPhoneNumber(result)) {
        // 是电话号码,拨打电话
        uni.showModal({
          title: '拨打电话',
          content: result,
          success: (modalRes) => {
            if (modalRes.confirm) {
              uni.makePhoneCall({
                phoneNumber: result
              })
            }
          }
        })
      } else if (this.isEmail(result)) {
        // 是邮箱地址,发送邮件
        uni.showModal({
          title: '发送邮件',
          content: result,
          success: (modalRes) => {
            if (modalRes.confirm) {
              uni.setClipboardData({
                data: result,
                success: () => {
                  uni.showToast({
                    title: '邮箱地址已复制',
                    icon: 'success'
                  })
                }
              })
            }
          }
        })
      } else {
        // 其他类型,显示结果
        uni.showModal({
          title: '扫码结果',
          content: result,
          showCancel: false
        })
      }
    },
    
    // 判断是否为网址
    isUrl(str) {
      const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/i
      return urlRegex.test(str)
    },
    
    // 判断是否为电话号码
    isPhoneNumber(str) {
      const phoneRegex = /^1[3-9]\d{9}$/
      return phoneRegex.test(str)
    },
    
    // 判断是否为邮箱地址
    isEmail(str) {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      return emailRegex.test(str)
    }
  }
}
</script>

<style>
.container {
  flex: 1;
  padding: 40rpx;
  display: flex;
  flex-direction: column;
}

.header {
  margin-bottom: 60rpx;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
  display: block;
  margin-bottom: 20rpx;
}

.subtitle {
  font-size: 24rpx;
  color: #666;
  display: block;
}

.button-group {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.scan-button {
  width: 100%;
  padding: 24rpx;
  margin-bottom: 30rpx;
  border-radius: 8rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.qr-button {
  background-color: #07C160;
  color: white;
}

.bar-button {
  background-color: #1677FF;
  color: white;
}

.all-button {
  background-color: #FF9500;
  color: white;
}

.button-text {
  font-size: 28rpx;
  font-weight: bold;
}

.result {
  margin-top: 40rpx;
  padding: 30rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
}

.result-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 20rpx;
  display: block;
}

.result-type {
  font-size: 24rpx;
  color: #666;
  margin-bottom: 10rpx;
  display: block;
}

.result-text {
  font-size: 24rpx;
  color: #333;
  word-break: break-all;
}
</style>

案例二:自定义扫码界面

场景:实现自定义的扫码界面,包括扫描动画、引导线等效果。

实现步骤

  1. 创建自定义扫码组件
  2. 使用 plus.barcode API 实现扫码
  3. 添加扫码动画和引导线
  4. 处理扫码结果

代码示例

创建 src/components/CustomScanner.vue

<template>
  <view class="scanner-container">
    <!-- 扫码区域 -->
    <view class="scan-area">
      <view class="scan-frame">
        <!-- 扫描线 -->
        <view class="scan-line" :class="{ 'scanning': isScanning }"></view>
        <!-- 四角边框 -->
        <view class="corner top-left"></view>
        <view class="corner top-right"></view>
        <view class="corner bottom-left"></view>
        <view class="corner bottom-right"></view>
      </view>
      <!-- 提示文字 -->
      <text class="scan-tip">{{ tipText }}</text>
    </view>
    
    <!-- 控制按钮 -->
    <view class="control-bar">
      <view class="control-item" @click="toggleFlash">
        <image :src="flashOn ? '/static/flash-on.png' : '/static/flash-off.png'" class="control-icon" />
        <text class="control-text">{{ flashOn ? '关闭闪光灯' : '打开闪光灯' }}</text>
      </view>
      
      <view class="control-item" @click="chooseImage">
        <image src="/static/image-icon.png" class="control-icon" />
        <text class="control-text">从相册选择</text>
      </view>
      
      <view class="control-item" @click="close">
        <image src="/static/close-icon.png" class="control-icon" />
        <text class="control-text">关闭</text>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  props: {
    // 扫码提示文字
    tipText: {
      type: String,
      default: '请将二维码/条形码对准扫描框'
    },
    // 扫码类型
    scanType: {
      type: Array,
      default: () => ['qr', 'barcode']
    }
  },
  data() {
    return {
      isScanning: false,
      flashOn: false,
      barcode: null,
      scanResult: null
    }
  },
  mounted() {
    this.initScanner()
  },
  beforeDestroy() {
    this.destroyScanner()
  },
  methods: {
    // 初始化扫码器
    initScanner() {
      const that = this
      
      // 获取当前页面
      const page = getCurrentPages()[getCurrentPages().length - 1]
      const currentWebview = page.$getAppWebview()
      
      // 创建扫码区域
      const scanArea = {
        top: '20%',
        left: '10%',
        width: '80%',
        height: '40%'
      }
      
      // 创建扫码对象
      that.barcode = plus.barcode.create('barcode', that.scanType, {
        top: scanArea.top,
        left: scanArea.left,
        width: scanArea.width,
        height: scanArea.height,
        position: 'absolute',
        frameColor: '#007AFF',
        scanbarColor: '#007AFF',
        background: 'rgba(0,0,0,0.3)'
      })
      
      // 添加到当前页面
      currentWebview.append(that.barcode)
      
      // 开始扫描
      that.startScan()
      
      // 监听扫码成功事件
      that.barcode.onmarked = function(type, result) {
        that.isScanning = false
        that.scanResult = {
          scanType: type,
          result: result
        }
        
        // 停止扫描
        that.barcode.stop()
        
        // 触发扫码成功事件
        that.$emit('success', that.scanResult)
      }
      
      // 监听扫码错误事件
      that.barcode.onerror = function(error) {
        console.error('扫码错误:', error)
        that.$emit('error', error)
      }
    },
    
    // 开始扫描
    startScan() {
      if (this.barcode) {
        this.barcode.start()
        this.isScanning = true
      }
    },
    
    // 停止扫描
    stopScan() {
      if (this.barcode) {
        this.barcode.stop()
        this.isScanning = false
      }
    },
    
    // 销毁扫码器
    destroyScanner() {
      if (this.barcode) {
        this.barcode.stop()
        this.barcode.close()
        this.barcode = null
      }
    },
    
    // 切换闪光灯
    toggleFlash() {
      if (this.barcode) {
        this.flashOn = !this.flashOn
        this.barcode.setFlash(this.flashOn)
      }
    },
    
    // 从相册选择图片扫码
    chooseImage() {
      const that = this
      
      uni.chooseImage({
        count: 1,
        sizeType: ['original'],
        sourceType: ['album'],
        success: (res) => {
          const imagePath = res.tempFilePaths[0]
          
          // 识别图片中的条码
          plus.barcode.scan(imagePath, function(type, result) {
            that.scanResult = {
              scanType: type,
              result: result
            }
            that.$emit('success', that.scanResult)
          }, function(error) {
            console.error('识别失败:', error)
            uni.showToast({
              title: '识别失败,请重试',
              icon: 'none'
            })
          }, that.scanType)
        },
        fail: (err) => {
          console.error('选择图片失败:', err)
        }
      })
    },
    
    // 关闭扫码
    close() {
      this.$emit('close')
    }
  }
}
</script>

<style scoped>
.scanner-container {
  position: relative;
  width: 100%;
  height: 100vh;
  background-color: #000;
}

.scan-area {
  position: relative;
  width: 100%;
  height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.scan-frame {
  position: relative;
  width: 600rpx;
  height: 600rpx;
  border: 2rpx solid rgba(0, 122, 255, 0.5);
  background-color: rgba(0, 122, 255, 0.1);
}

.scan-line {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4rpx;
  background-color: #007AFF;
  transform: translateY(-100%);
  transition: transform 2s linear infinite;
}

.scan-line.scanning {
  transform: translateY(100%);
}

.corner {
  position: absolute;
  width: 40rpx;
  height: 40rpx;
  border: 4rpx solid #007AFF;
}

.corner.top-left {
  top: -2rpx;
  left: -2rpx;
  border-right: none;
  border-bottom: none;
}

.corner.top-right {
  top: -2rpx;
  right: -2rpx;
  border-left: none;
  border-bottom: none;
}

.corner.bottom-left {
  bottom: -2rpx;
  left: -2rpx;
  border-right: none;
  border-top: none;
}

.corner.bottom-right {
  bottom: -2rpx;
  right: -2rpx;
  border-left: none;
  border-top: none;
}

.scan-tip {
  position: absolute;
  bottom: 10vh;
  color: white;
  font-size: 28rpx;
  text-align: center;
}

.control-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 30vh;
  background-color: rgba(0, 0, 0, 0.8);
  display: flex;
  align-items: center;
  justify-content: space-around;
}

.control-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.control-icon {
  width: 60rpx;
  height: 60rpx;
  margin-bottom: 10rpx;
}

.control-text {
  color: white;
  font-size: 24rpx;
}
</style>

在页面中使用:

<template>
  <view class="container">
    <view class="header">
      <text class="title">自定义扫码</text>
      <text class="subtitle">点击下方按钮打开自定义扫码界面</text>
    </view>
    
    <button @click="openCustomScanner" class="scan-button">
      <text class="button-text">打开自定义扫码</text>
    </button>
    
    <view class="result" v-if="scanResult">
      <text class="result-title">扫码结果</text>
      <text class="result-type">类型:{{ scanResult.scanType }}</text>
      <text class="result-text">内容:{{ scanResult.result }}</text>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      scanResult: null
    }
  },
  methods: {
    openCustomScanner() {
      uni.navigateTo({
        url: '/pages/scanner/custom-scanner'
      })
    }
  }
}
</script>

<style>
.container {
  flex: 1;
  padding: 40rpx;
  display: flex;
  flex-direction: column;
}

.header {
  margin-bottom: 60rpx;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
  display: block;
  margin-bottom: 20rpx;
}

.subtitle {
  font-size: 24rpx;
  color: #666;
  display: block;
}

.scan-button {
  width: 100%;
  padding: 24rpx;
  background-color: #007AFF;
  color: white;
  border-radius: 8rpx;
  margin-bottom: 40rpx;
}

.button-text {
  font-size: 28rpx;
  font-weight: bold;
}

.result {
  padding: 30rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
}

.result-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 20rpx;
  display: block;
}

.result-type {
  font-size: 24rpx;
  color: #666;
  margin-bottom: 10rpx;
  display: block;
}

.result-text {
  font-size: 24rpx;
  color: #333;
  word-break: break-all;
}
</style>

创建 src/pages/scanner/custom-scanner.vue

<template>
  <custom-scanner 
    @success="onScanSuccess"
    @close="onClose"
  />
</template>

<script>
import CustomScanner from '@/components/CustomScanner.vue'

export default {
  components: {
    CustomScanner
  },
  methods: {
    onScanSuccess(result) {
      console.log('扫码成功:', result)
      
      // 保存扫码结果到全局变量或本地存储
      uni.setStorageSync('scanResult', result)
      
      // 返回上一页
      uni.navigateBack({
        delta: 1
      })
      
      // 显示扫码结果
      uni.showToast({
        title: '扫码成功',
        icon: 'success'
      })
    },
    
    onClose() {
      // 返回上一页
      uni.navigateBack({
        delta: 1
      })
    }
  }
}
</script>

<style>
/* 页面样式 */
</style>

案例三:扫码结果处理与应用

场景:处理扫码结果并应用到具体场景,如商品查询、网址打开等。

实现步骤

  1. 解析扫码结果
  2. 根据结果类型进行不同处理
  3. 实现具体应用场景
  4. 优化用户体验

代码示例

<template>
  <view class="container">
    <view class="header">
      <text class="title">扫码应用</text>
      <text class="subtitle">扫描二维码或条形码体验不同应用场景</text>
    </view>
    
    <view class="scene-list">
      <view class="scene-item" @click="scanForProduct">
        <image src="/static/product-icon.png" class="scene-icon" />
        <text class="scene-title">商品查询</text>
        <text class="scene-desc">扫描商品条形码查询价格和详情</text>
      </view>
      
      <view class="scene-item" @click="scanForURL">
        <image src="/static/url-icon.png" class="scene-icon" />
        <text class="scene-title">打开网址</text>
        <text class="scene-desc">扫描网址二维码直接打开网页</text>
      </view>
      
      <view class="scene-item" @click="scanForContact">
        <image src="/static/contact-icon.png" class="scene-icon" />
        <text class="scene-title">添加联系人</text>
        <text class="scene-desc">扫描名片二维码添加联系人</text>
      </view>
      
      <view class="scene-item" @click="scanForWifi">
        <image src="/static/wifi-icon.png" class="scene-icon" />
        <text class="scene-title">连接 WiFi</text>
        <text class="scene-desc">扫描 WiFi 二维码自动连接网络</text>
      </view>
    </view>
    
    <view class="history" v-if="scanHistory.length > 0">
      <text class="history-title">扫码历史</text>
      <view class="history-list">
        <view class="history-item" v-for="(item, index) in scanHistory" :key="index">
          <text class="history-type">{{ item.type }}</text>
          <text class="history-content">{{ item.content }}</text>
          <text class="history-time">{{ item.time }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      scanHistory: []
    }
  },
  onLoad() {
    // 加载扫码历史
    this.loadScanHistory()
  },
  methods: {
    // 加载扫码历史
    loadScanHistory() {
      const history = uni.getStorageSync('scanHistory') || []
      this.scanHistory = history.slice(0, 5) // 只显示最近5条
    },
    
    // 保存扫码历史
    saveScanHistory(type, content) {
      const history = uni.getStorageSync('scanHistory') || []
      const newItem = {
        type: type,
        content: content,
        time: new Date().toLocaleString()
      }
      history.unshift(newItem) // 添加到开头
      if (history.length > 20) {
        history.pop() // 最多保存20条
      }
      uni.setStorageSync('scanHistory', history)
      this.loadScanHistory() // 重新加载历史
    },
    
    // 扫描商品
    scanForProduct() {
      this.scanCode({
        scanType: ['barCode'],
        title: '扫描商品条形码',
        success: (result) => {
          this.saveScanHistory('商品条码', result.result)
          this.queryProduct(result.result)
        }
      })
    },
    
    // 扫描网址
    scanForURL() {
      this.scanCode({
        scanType: ['qrCode'],
        title: '扫描网址二维码',
        success: (result) => {
          this.saveScanHistory('网址', result.result)
          this.openURL(result.result)
        }
      })
    },
    
    // 扫描联系人
    scanForContact() {
      this.scanCode({
        scanType: ['qrCode'],
        title: '扫描名片二维码',
        success: (result) => {
          this.saveScanHistory('联系人', result.result)
          this.addContact(result.result)
        }
      })
    },
    
    // 扫描 WiFi
    scanForWifi() {
      this.scanCode({
        scanType: ['qrCode'],
        title: '扫描 WiFi 二维码',
        success: (result) => {
          this.saveScanHistory('WiFi', result.result)
          this.connectWifi(result.result)
        }
      })
    },
    
    // 通用扫码方法
    scanCode(options) {
      uni.scanCode({
        scanType: options.scanType,
        success: (res) => {
          console.log('扫码成功:', res)
          options.success(res)
        },
        fail: (err) => {
          console.error('扫码失败:', err)
          uni.showToast({
            title: '扫码失败',
            icon: 'none'
          })
        }
      })
    },
    
    // 查询商品
    queryProduct(barcode) {
      // 调用商品查询 API
      uni.request({
        url: `https://api.example.com/product/query`,
        method: 'GET',
        data: {
          barcode: barcode
        },
        success: (res) => {
          if (res.statusCode === 200 && res.data.success) {
            const product = res.data.data
            // 显示商品信息
            uni.showModal({
              title: product.name,
              content: `价格:¥${product.price}\n产地:${product.origin}\n库存:${product.stock}件`,
              showCancel: false
            })
          } else {
            uni.showToast({
              title: '商品信息查询失败',
              icon: 'none'
            })
          }
        },
        fail: (err) => {
          console.error('查询商品失败:', err)
          uni.showToast({
            title: '网络错误,请稍后重试',
            icon: 'none'
          })
        }
      })
    },
    
    // 打开网址
    openURL(url) {
      // 验证是否为有效网址
      if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = 'http://' + url
      }
      
      // 打开网页
      uni.navigateTo({
        url: `/pages/web-view/web-view?url=${encodeURIComponent(url)}`
      })
    },
    
    // 添加联系人
    addContact(vcard) {
      // 解析 vCard 格式
      try {
        // 简单解析,实际应用中可能需要更复杂的解析
        const nameMatch = vcard.match(/FN:(.*?)(?:\r\n|$)/)
        const phoneMatch = vcard.match(/TEL:(.*?)(?:\r\n|$)/)
        const emailMatch = vcard.match(/EMAIL:(.*?)(?:\r\n|$)/)
        
        const contact = {
          name: nameMatch ? nameMatch[1] : '',
          phone: phoneMatch ? phoneMatch[1] : '',
          email: emailMatch ? emailMatch[1] : ''
        }
        
        // 显示联系人信息并询问是否添加
        uni.showModal({
          title: '添加联系人',
          content: `姓名:${contact.name}\n电话:${contact.phone}\n邮箱:${contact.email}`,
          success: (modalRes) => {
            if (modalRes.confirm) {
              // 调用系统添加联系人 API
              uni.addPhoneContact({
                firstName: contact.name,
                mobilePhoneNumber: contact.phone,
                email: contact.email,
                success: () => {
                  uni.showToast({
                    title: '联系人添加成功',
                    icon: 'success'
                  })
                },
                fail: (err) => {
                  console.error('添加联系人失败:', err)
                  uni.showToast({
                    title: '添加联系人失败',
                    icon: 'none'
                  })
                }
              })
            }
          }
        })
      } catch (error) {
        console.error('解析联系人信息失败:', error)
        uni.showToast({
          title: '解析联系人信息失败',
          icon: 'none'
        })
      }
    },
    
    // 连接 WiFi
    connectWifi(wifiInfo) {
      // 解析 WiFi 信息
      try {
        // WiFi 二维码格式通常为:WIFI:S:<ssid>;T:<type>;P:<password>;;
        const ssidMatch = wifiInfo.match(/WIFI:S:(.*?);/)
        const passwordMatch = wifiInfo.match(/P:(.*?);;/)
        
        if (ssidMatch && passwordMatch) {
          const ssid = ssidMatch[1]
          const password = passwordMatch[1]
          
          // 显示 WiFi 信息并询问是否连接
          uni.showModal({
            title: '连接 WiFi',
            content: `SSID:${ssid}\n密码:${password}`,
            success: (modalRes) => {
              if (modalRes.confirm) {
                // 调用系统连接 WiFi API
                uni.connectWifi({
                  SSID: ssid,
                  password: password,
                  success: () => {
                    uni.showToast({
                      title: 'WiFi 连接成功',
                      icon: 'success'
                    })
                  },
                  fail: (err) => {
                    console.error('WiFi 连接失败:', err)
                    uni.showToast({
                      title: 'WiFi 连接失败',
                      icon: 'none'
                    })
                  }
                })
              }
            }
          })
        } else {
          uni.showToast({
            title: 'WiFi 信息解析失败',
            icon: 'none'
          })
        }
      } catch (error) {
        console.error('解析 WiFi 信息失败:', error)
        uni.showToast({
          title: '解析 WiFi 信息失败',
          icon: 'none'
        })
      }
    }
  }
}
</script>

<style>
.container {
  flex: 1;
  padding: 40rpx;
  display: flex;
  flex-direction: column;
}

.header {
  margin-bottom: 60rpx;
}

.title {
  font-size: 36rpx;
  font-weight: bold;
  color: #333;
  display: block;
  margin-bottom: 20rpx;
}

.subtitle {
  font-size: 24rpx;
  color: #666;
  display: block;
}

.scene-list {
  flex: 1;
}

.scene-item {
  padding: 30rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
  margin-bottom: 30rpx;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.scene-item:active {
  background-color: #e5e5e5;
}

.scene-icon {
  width: 80rpx;
  height: 80rpx;
  margin-right: 30rpx;
}

.scene-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 5rpx;
  display: block;
}

.scene-desc {
  font-size: 24rpx;
  color: #666;
}

.history {
  margin-top: 40rpx;
  padding: 30rpx;
  background-color: #f5f5f5;
  border-radius: 8rpx;
}

.history-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 20rpx;
  display: block;
}

.history-item {
  padding: 15rpx 0;
  border-bottom: 1rpx solid #e5e5e5;
}

.history-item:last-child {
  border-bottom: none;
}

.history-type {
  font-size: 24rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 5rpx;
  display: block;
}

.history-content {
  font-size: 22rpx;
  color: #666;
  margin-bottom: 5rpx;
  display: block;
  word-break: break-all;
}

.history-time {
  font-size: 20rpx;
  color: #999;
  display: block;
}
</style>

扫码功能最佳实践

1. 性能优化

  • 扫码速度:优化扫码速度,减少用户等待时间
  • 识别率:提高条码识别率,支持多种码制
  • 光线适应:适应不同光线环境,提高扫码成功率
  • 防抖处理:添加防抖处理,避免重复扫码
  • 内存管理:及时销毁扫码对象,避免内存泄漏

2. 用户体验

  • 清晰的扫码界面:提供清晰的扫码界面和引导
  • 扫码动画:添加扫描动画,提升用户体验
  • 闪光灯控制:提供闪光灯开关,适应黑暗环境
  • 相册选择:支持从相册选择图片进行扫码
  • 错误提示:提供清晰的错误提示和重试机制

3. 功能扩展

  • 多码制支持:支持多种二维码和条形码格式
  • 批量扫码:支持连续扫码,如商品 inventory 盘点
  • 扫码历史:保存扫码历史记录,方便用户查看
  • 结果分享:支持分享扫码结果到其他应用
  • 自定义解析:支持自定义条码格式的解析

4. 兼容性考虑

  • 平台差异:处理不同平台的扫码 API 差异
  • 权限管理:合理请求相机权限,处理权限被拒绝的情况
  • 设备适配:适配不同屏幕尺寸的设备
  • 网络环境:处理无网络环境下的扫码场景

5. 安全考虑

  • 条码内容验证:验证扫码内容的安全性,避免恶意链接
  • 权限保护:保护用户隐私,合理使用相机权限
  • 数据加密:对扫码获取的敏感信息进行加密处理
  • 防欺骗:防止恶意条码的欺骗攻击

总结回顾

本章节介绍了 uni-app 中的扫码功能实现方法,包括:

  1. 扫码功能基础概念:了解二维码和条形码的基本概念
  2. uni-app 扫码 API:掌握 uni.scanCode API 的使用方法
  3. 基本扫码实现:实现基本的二维码和条形码扫描
  4. 自定义扫码界面:创建自定义的扫码界面,包括扫描动画和引导线
  5. 扫码结果处理:解析和处理扫码结果,实现不同应用场景
  6. 扫码功能最佳实践:学习扫码功能的性能优化、用户体验、功能扩展等最佳实践

通过本章节的学习,您应该能够:

  • 实现基本的二维码和条形码扫描功能
  • 创建自定义的扫码界面,提升用户体验
  • 处理扫码结果,实现不同的应用场景
  • 优化扫码功能的性能和用户体验
  • 考虑扫码功能的兼容性和安全性

扫码功能是移动应用的重要组成部分,它可以为用户提供便捷的信息录入和交互方式。在实际开发中,应根据具体应用场景,选择合适的扫码实现方式,并不断优化用户体验,为用户提供高效、便捷的扫码功能。

« 上一篇 uni-app 第三方登录 下一篇 » uni-app 文件操作