微信小程序扫码组件,支持二维码或条形码(工程化封装)
·
如果相册扫描码,显示【未找到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 时,会读取用户相册里的图片
更多推荐


所有评论(0)