使用uniapp的app-nvue页面并设置独立路由
因为在app端自定义组件brcode只支持app-nvue,如果页面中用的是app-nvue,可将其设为easycom组件,设为组件在传入prop时会更加的友好。
若使用的是app-nvue页面可使用uni.$emit进行页面通信或者使用uni.navigateTo的eventChannel进行页面通信,不过需要注意的是eventChannel在app-nvue中是不支持的,可使用条件编译,app端使用vuex。
微信小程序端
使用uni-app组件 camera https://uniapp.dcloud.io/component/camera
app端
使用uni-app组件 Barcode https://uniapp.dcloud.io/component/barcode
代码示例
<template>
<view>
<!-- #ifdef MP-WEIXIN -->
<view>
<u-sticky>
<view v-if="showCamera">
<camera
device-position="back"
mode="scanCode"
flash="off"
@error="cameraError"
class="cam-container"
@scancode="scancode"
></camera>
</view>
<u-alert-tips v-if="showTips" type="warning" :title="tips" class="u-m-b-10">
</u-alert-tips>
<u-alert-tips v-if="showResult" type="primary" :title="scanResult" class="u-m-b-10">
</u-alert-tips>
</u-sticky>
<!-- <button type="primary" @click="takePhoto">拍照</button> -->
<uni-list class="content" :border="false">
<uni-list-item
v-for="(codeItem, index) in qrCodeList"
:key="index"
class="codeList"
:title="codeItem.cntrNum"
:note="codeItem.cntrTypeName"
>
<view slot="footer">
<icon v-if="showClose" type="cancel" size="26" @click="deleteQrCode(index)"/>
</view>
</uni-list-item>
</uni-list>
<!-- <image mode="widthFix" :src="src" /> -->
</view>
<!-- #endif -->
<!-- app端 -->
<!-- #ifdef APP-PLUS -->
<view>
<app-scan :showTips="showTips" :showResult="showResult" :showCamera="showCamera" :tips="tips" :scanResult="scanResult"></app-scan>
</view>
<!-- #endif -->
</view>
</template>
<script>
/**
* @description 载具二维码扫描组件 因目前需要扫描页面均为vue页面 未来可重构为nvue页面
* @eventProp {Array} targetQrcodeList 需扫码列表
* @eventProp {Array} initQrcodeList 初始载具码
* @eventProp {number} index 回调索引
* @eventProp {bool} showTips 显示tips
* @eventProp {bool} showResult 显示扫码结果
* @eventProp {bool} showCamera 显示扫码框
* @eventProp {bool} showClose 可删除码
*/
import appScan from './app-scan.nvue';
import { arrayGroupBy } from '@/common/util/common.js';
export default {
name: 'Scan',
components: {
'app-scan': appScan,
},
data() {
return {
qrCodeList: [],
targetQrcodeList: [],
eventChannel: {},
index: '',
showTips: false,
showCamera: false,
showResult: false,
showClose: true
};
},
computed: {
// 显示的tips 需要扫码的载具
tips() {
let result = '可扫码项:';
const groupList = arrayGroupBy(this.targetQrcodeList,qitem=> {
return qitem.cntrTypeId;
});
groupList.forEach((gItem) => {
if (gItem.length) {
result += `${gItem[0].cntrTypeName} x ${gItem.length};`;
}
});
return result;
},
// 扫码结果提示
scanResult() {
const groupList = arrayGroupBy(this.qrCodeList,qitem=> {
return qitem.cntrTypeId;
});
let result = '已扫:';
groupList.forEach((gItem) => {
if (gItem.length) {
result += `${gItem[0].cntrTypeName} x /${gItem.length};`;
}
});
return result;
},
},
onLoad(option) {
// #ifdef APP-PLUS
const data = getApp().vuex_scan.params || {};
const { targetQrcodeList = [], initQrcodeList = [], includeList, index = '', showTips = true, showCamera = true, showResult = true, showClose = true } = data;
this.targetQrcodeList = targetQrcodeList;
this.qrCodeList = initQrcodeList;
this.showClose = showClose;
this.index = index;
this.showTips = showTips;
this.showCamera = showCamera;
this.showResult = showResult;
this.showClose = showClose;
// #endif
// #ifndef APP-PLUS
// 非app端
this.eventChannel = this.getOpenerEventChannel();
const _this = this;
// 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
this.eventChannel.on('acceptDataFromOpenerPage', function (data) {
const { targetQrcodeList = [], initQrcodeList = [], index = '', showTips = true, showCamera = true, showResult = true, showClose = true } = data;
_this.targetQrcodeList = targetQrcodeList;
_this.qrCodeList = initQrcodeList;
_this.index = index;
_this.showTips = showTips;
_this.showCamera = showCamera;
_this.showResult = showResult;
_this.showClose = showClose;
});
// #endif
},
onUnload() {
this.eventChannel.off('acceptDataFromOpenerPage');
},
methods: {
scancode(scanRes) {
const { detail } = scanRes;
try {
// 二维码内容 可随实际情况调整
const result = JSON.parse(detail.result);
// 判断二维码是否为目标列表内
if(this.targetQrcodeList && this.targetQrcodeList.length) {
const isTargetCode = this.targetQrcodeList.some(tItem=> {
return tItem.cntrNum === result.cntrNum;
});
if(!isTargetCode) {
this.$u.toast('扫描的二维码不在需扫码项内~');
return;
}
}
// 判断是否已扫过
const isInclude = this.qrCodeList.findIndex((codeItem) => {
return codeItem.cntrNum === result.cntrNum;
});
if (
!(isInclude !== -1) &&
(detail.type === 'QR_CODE' || detail.type === 'qrcode')
) {
if (result) {
this.qrCodeList.push(result);
}
} else if (isInclude !== -1) {
this.$u.toast('二维码已添加~');
} else {
this.$u.toast('错误码~');
}
} catch (error) {
this.$u.toast('扫描二维码错误~');
}
},
deleteQrCode(index) {
this.qrCodeList.splice(index, 1);
},
cameraError(e) {
console.log(e);
},
// 根据id获取已扫二维码
getCntrNumByid(id) {
return this.qrCodeList.filter((item) => {
return item.cntrTypeId === id;
});
},
buttonClick(options) {
if (options.content.type === 'back') {
// 返回按钮
// this.$emit('onClose');
this.eventChannel.emit('onCencel');
uni.navigateBack();
} else if (options.content.type === 'ok') {
// 确认按钮
this.eventChannel.emit('scanOk', {
result: this.qrCodeList,
index: this.index,
});
uni.navigateBack();
}
},
},
};
</script>
<style>
.cam-container {
width: 100%;
height: 300px;
margin-bottom: 10rpx;
}
</style>
- app-scan.nvue
<template>
<view>
<view class="camera-cont">
<barcode
id="1"
class="barcode"
ref="barcode"
background="rgb(0,0,0)"
frameColor="#1C86EE"
scanbarColor="#1C86EE"
@marked="success1"
@error="fail1"
v-if="showCamera"
></barcode>
<view v-if="showTips">
<text class="tips target">{{tips}}</text>
</view>
<view v-if="showResult">
<text class="tips result">{{scanResult}}</text>
</view>
</view>
<!-- <button class="btn" @click="toStart">{{tips}}</button>
<button class="btn" @click="tocancel">取消扫码识别</button>
<button class="btn" @click="toFlash">开启闪光灯</button>
<button class="btn" @click="toscan">预览</button> -->
</view>
</template>
<script>
export default {
mounted() {
this.$refs.barcode.start({
conserve: false,
sound: 'default',
vibrate: false,
});
},
props: {
showTips: {
type: Boolean,
default: true,
},
showCamera: {
type: Boolean,
default: true,
},
showResult: {
type: Boolean,
default: true,
},
tips: {
type: String,
default: () => {
return '';
},
},
scanResult: {
type: String,
default: () => {
return '';
},
},
},
data() {
return {};
},
methods: {
success1(e) {
console.log('success1:' + JSON.stringify(e));
},
fail1(e) {
console.log('fail1:' + JSON.stringify(e));
},
toStart: function () {
this.$refs.barcode.start({
conserve: false,
sound: 'default',
vibrate: false,
});
},
tocancel: function () {
this.$refs.barcode.cancel();
},
toFlash: function () {
this.$refs.barcode.setFlash(true);
},
toscan: function () {
console.log('scan:');
const barcodeModule = uni.requireNativePlugin('barcodeScan');
barcodeModule.scan('/static/barcode1.png', (e) => {
console.log('scan_error:' + JSON.stringify(e));
});
},
},
};
</script>
<style>
.barcode {
width: 750rpx;
height: 500rpx;
background-color: #808080;
}
.btn {
top: 20rpx;
width: 730rpx;
margin-left: 10rpx;
margin-top: 10rpx;
background-color: #458b00;
border-radius: 10rpx;
}
.camera-cont {
width: 750rpx;
height: 650rpx;
}
.tips {
font-size: 28rpx;
/* color: #ff9900; */
display: flex;
align-items: center;
padding: 16rpx 30rpx;
border-radius: 8rpx;
transition: all 0.3s linear;
border: 1px solid #fff;
margin: 5rpx 0;
}
.target {
border-color: #fcbd71;
background-color: #fdf6ec;
}
.result {
border-color: #a0cfff;
background-color: #ecf5ff;
}
</style>