如果相册扫描码,显示【未找到QR Code / 条码】就是你相册的码太模糊了,,,

一、需求

微信小程序中用户点击扫描图标【】,打开摄像头或者选择相册图片,进行条形码或二维码的识别,将结果返回到文本框中。

二、代码实现

1. 扫描组件封装
  • 职责单一:仅负责「输入框展示 + 扫码能力调用 + 结果回填」
  • 配置化:通过 properties 暴露扫码类型、相册权限等配置项,由父组件控制,未传则使用默认配置,
  • 事件驱动:通过 triggerEvent 向父组件暴露 change/success/error 事件,支持双向绑定
  • 兼容性:使用微信原生 wx.scanCode({ … }) API,兼容基础库 2.0.0+,
  • 可扩展性:预留自定义图标、输入框禁用等扩展能力
2.完整的扫描组件代码
Component({
  properties: {
    // 输入框占位符
    placeholder: {
      type: String,
      value: '点击扫一扫识别序列号'
    },
    // 扫码类型:['qrCode']/['barCode']/['qrCode','barCode']
    scanType: {
      type: Array,
      value: ['qrCode', 'barCode']
    },
    //父组件传了就用父组件的,没传就用默认值false(支持相机+相册)
    onlyFromCamera: {
      type: Boolean,
      value: false
    },
    // 输入框是否禁用
    disabled: {
      type: Boolean,
      value: false
    },
    // 扫码图标路径
    iconPath: {
      type: String,
      value: '/img/scan-icon.png'
    },
    // 外部传入的初始值(双向绑定)
    value: {
      type: String,
      value: ''
    }
  },

  data: {
    innerValue: '', // 组件内部维护的输入值
    loading: false  // 扫码加载状态
  },

  lifetimes: {
    // 组件初始化时,同步外部value到内部
    attached() {
      this.setData({
        innerValue: this.properties.value
      })
    }
  },

  observers: {
    // 监听外部value变化,同步到内部
    'value': function(newVal) {
      if (newVal !== this.data.innerValue) {
        this.setData({
          innerValue: newVal
        })
      }
    }
  },

  methods: {
    // 输入框手动输入
    handleInput(e) {
      const value = e.detail.value
      this.setData({
        innerValue: value
      })
      // 触发父组件的change事件,同步值
      this.triggerEvent('change', { value })
    },

    // 点击扫码按钮
    async handleScan() {
      // 防止重复点击
      if (this.data.loading || this.properties.disabled) return

      this.setData({
        loading: true
      })

      try {
        // 直接使用父组件传入的onlyFromCamera参数,不做任何额外处理
        const res = await wx.scanCode({
          onlyFromCamera: this.properties.onlyFromCamera,
          scanType: this.properties.scanType
        })

        const value = res.result
        // 扫码成功,更新内部值并同步给父组件
        this.setData({
          innerValue: value
        })
        this.triggerEvent('change', { value })
        this.triggerEvent('success', { value, raw: res })

        wx.showToast({
          title: '识别成功',
          icon: 'success',
          duration: 1500
        })

      } catch (err) {
        // 只处理非用户取消的错误
        if (err.errMsg !== 'scanCode:fail cancel') {
          wx.showToast({
            title: '识别失败,请重试',
            icon: 'error',
            duration: 1500
          })
          this.triggerEvent('error', { err })
        }
      } finally {
        // 无论成功失败,都关闭加载状态
        this.setData({
          loading: false
        })
      }
    },

    // 对外暴露:清空输入框
    clear() {
      this.setData({
        innerValue: ''
      })
      this.triggerEvent('change', { value: '' })
    }
  }
})
<view class="scanner-wrapper">
  <input
    class="scanner-input"
    placeholder="{{placeholder}}"
    value="{{innerValue}}"
    bindinput="handleInput"
    disabled="{{disabled}}"
  />
  <view class="scanner-icon" bindtap="handleScan">
    <view class="loading-spin" wx:if="{{loading}}"></view>
    <image wx:else src="{{iconPath}}" mode="aspectFit" class="icon-img" />
  </view>
</view>
.scanner-wrapper {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  height: 120rpx;
  padding: 0 30rpx;
  background: #f8fafc;
  border: 1rpx solid #e2e8f0;
  border-radius: 24rpx;
  box-sizing: border-box;
}

.scanner-input {
  flex: 1;
  height: 100%;
  font-size: 32rpx;
  color: #64748b;
  margin-right: 20rpx;
}

.scanner-icon {
  width: 60rpx;
  height: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.icon-img {
  width: 48rpx;
  height: 48rpx;
}

.loading-spin {
  width: 40rpx;
  height: 40rpx;
  border: 4rpx solid #3b82f6;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
3.父页面引入扫描组件的使用

在这里插入图片描述

Page({
  data: {
    serial1: '',
    serial2: ''
  },

  onChange1(e) {
    this.setData({ serial1: e.detail.value })
  },

  onChange2(e) {
    this.setData({ serial2: e.detail.value })
  },

  onScanSuccess(e) {
    console.log('扫码成功,原始数据:', e.detail.raw)
    wx.showModal({
      title: '识别结果',
      content: e.detail.value,
      showCancel: false
    })
  },

  onScanError(e) {
    console.error('扫码失败:', e.detail.err)
  },


})
{
	"enablePullDownRefresh": true,
	"backgroundTextStyle": "dark",
	"usingComponents": {
    "serial-scanner": "/components/serial-scanner/serial-scanner"
  }
}

<view class="container">
  <!-- 场景1:二维码-->
  <serial-scanner
    placeholder="扫描二维码"
    scan-type="{{['qrCode']}}"
    only-from-camera="{{true}}"
    value="{{serial1}}"
    bind:change="onChange1"
  />

  <!-- 场景2:仅条形码,支持相册 -->
  <serial-scanner
    placeholder="扫描条形码(支持相册)"
    scan-type="{{['barCode']}}"
    only-from-camera="{{false}}"
    value="{{serial2}}"
    bind:change="onChange2"
    class="mt-20"
  />

  <!-- 场景3:混合模式,带成功/失败回调 -->
  <serial-scanner
    placeholder="扫描条形码 / 二维码(支持相册)"
    scan-type="{{['qrCode','barCode']}}"
    only-from-camera="{{false}}"
    bind:success="onScanSuccess"
    bind:error="onScanError"
    class="mt-20"
  />

</view>
4.最后:开发版本可以正常使用,发正式版必须配置隐私协议才允许使用api

使用的wx.scanCode({ … })
1.相机权限:调用 scope.camera,用于扫码拍摄
2.相册图片读取权限:当 onlyFromCamera: false 时,会读取用户相册里的图片

更多推荐