300 lines
8.1 KiB
Plaintext
300 lines
8.1 KiB
Plaintext
|
<template>
|
|||
|
<!-- #ifndef APP || WEB -->
|
|||
|
<canvas :style="styles" v-if="use2d" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
|
|||
|
<canvas :style="styles" v-else :canvas-id="canvasId" :id="canvasId"></canvas>
|
|||
|
<!-- #endif -->
|
|||
|
<!-- #ifdef APP -->
|
|||
|
<view class="l-qrcode" ref="drawableRef" :style="[styles]">
|
|||
|
<image class="l-qrcode__icon" v-if="icon" :src="icon" :style="[iconStyle]"></image>
|
|||
|
</view>
|
|||
|
<!-- #endif -->
|
|||
|
<!-- #ifdef WEB -->
|
|||
|
<view class="l-qrcode" ref="drawableRef" :style="[styles]"></view>
|
|||
|
<!-- #endif -->
|
|||
|
</template>
|
|||
|
<script lang="uts" setup>
|
|||
|
/**
|
|||
|
* QRCode 二维码组件
|
|||
|
* @description 用于生成二维码图形,支持自定义图标和样式配置
|
|||
|
* <br>插件类型:LQrcodeComponentPublicInstance
|
|||
|
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-qrcode
|
|||
|
*
|
|||
|
* @property {string} value 二维码内容(支持文本/URL等)
|
|||
|
* @property {string} icon 中心图标路径(支持本地/网络路径)
|
|||
|
* @property {number | string} size 二维码尺寸
|
|||
|
* @property {number | string} iconSize 中心图标尺寸
|
|||
|
* @property {number} marginSize 二维码外边距(默认:0)
|
|||
|
* @property {string} color 二维码颜色(默认:#000000)
|
|||
|
* @property {string} bgColor 背景颜色(默认:#ffffff)
|
|||
|
* @property {boolean} bordered 显示边框(默认:false)
|
|||
|
* @property {'L' | 'M' | 'Q' | 'H'} errorLevel 容错等级(默认:'H')
|
|||
|
* @value L 可恢复7%的数据
|
|||
|
* @value M 可恢复15%的数据
|
|||
|
* @value Q 可恢复25%的数据
|
|||
|
* @value H 可恢复30%的数据
|
|||
|
* @property {boolean} useCanvasToTempFilePath 使用canvas生成临时路径(H5端可能需要)
|
|||
|
* @property {boolean} use2d 启用2D上下文渲染(性能优化,默认:false)
|
|||
|
*/
|
|||
|
import { type PropType, nextTick } from 'vue'
|
|||
|
// #ifndef APP
|
|||
|
import { createImage } from '@/uni_modules/lime-shared/createImage'
|
|||
|
import { getCanvas, isCanvas2d } from './useCanvas'
|
|||
|
import { QRCodeCanvas } from './qrcode.js';
|
|||
|
import { QRCodePropsTypes , ImageSettings } from './type'
|
|||
|
// #endif
|
|||
|
// #ifdef APP-ANDROID || APP-HARMONY
|
|||
|
import { QRCodeCanvas, type QRCodePropsTypes , type ImageSettings } from '@/uni_modules/lime-qrcodegen'
|
|||
|
// #endif
|
|||
|
// #ifdef APP-IOS
|
|||
|
import { QRCodeCanvas, type QRCodePropsTypes , type ImageSettings } from './ios'
|
|||
|
// #endif
|
|||
|
// import { addUnit } from '@/uni_modules/lime-shared/addUnit'
|
|||
|
// import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
|||
|
// import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
|
|||
|
import { addUnit, unitConvert } from './utils'
|
|||
|
import { LQrcodeFailCallback, LQrcodeCompleteCallback, LQrcodeSuccessCallback} from './type'
|
|||
|
|
|||
|
const name = 'l-qrcode'
|
|||
|
const props = defineProps({
|
|||
|
value: {
|
|||
|
type: String
|
|||
|
},
|
|||
|
icon: {
|
|||
|
type: String
|
|||
|
},
|
|||
|
// #ifdef APP-ANDROID
|
|||
|
size: {
|
|||
|
type: Object,
|
|||
|
default: 160
|
|||
|
},
|
|||
|
iconSize: {
|
|||
|
type: Object,
|
|||
|
default: 40
|
|||
|
},
|
|||
|
// #endif
|
|||
|
// #ifndef APP-ANDROID
|
|||
|
size: {
|
|||
|
type: [Number, String],
|
|||
|
default: 160
|
|||
|
},
|
|||
|
iconSize: {
|
|||
|
type: [Number, String],
|
|||
|
default: 40
|
|||
|
},
|
|||
|
// #endif
|
|||
|
marginSize: {
|
|||
|
type: Number,
|
|||
|
default: 0
|
|||
|
},
|
|||
|
color: {
|
|||
|
type: String,
|
|||
|
default: '#000'
|
|||
|
},
|
|||
|
bgColor: {
|
|||
|
type: String,
|
|||
|
default: 'transparent'
|
|||
|
},
|
|||
|
bordered: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
errorLevel: {
|
|||
|
type: String as PropType<'L' | 'M' | 'Q' | 'H'>,
|
|||
|
default: 'M' // 'L' | 'M' | 'Q' | 'H'
|
|||
|
},
|
|||
|
useCanvasToTempFilePath: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
use2d: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
}
|
|||
|
// status: {
|
|||
|
// type: String as PropType<'active'|'expired'|'loading'>,
|
|||
|
// default: 'active' // active | expired | loading
|
|||
|
// }
|
|||
|
})
|
|||
|
const emits = defineEmits(['success'])
|
|||
|
const context = getCurrentInstance();
|
|||
|
const canvasId = `l-qrcode${context!.uid}`
|
|||
|
const styles = computed<Map<string, any>>(():Map<string, any>=>{
|
|||
|
const style = new Map<string, any>()
|
|||
|
const size = addUnit(props.size);
|
|||
|
if(size!=null){
|
|||
|
style.set('width', size)
|
|||
|
style.set('height', size)
|
|||
|
}
|
|||
|
style.set('background', props.bgColor)
|
|||
|
return style
|
|||
|
})
|
|||
|
// #ifdef APP
|
|||
|
const iconStyle = computed<Map<string, any>>(():Map<string, any>=>{
|
|||
|
const style = new Map<string, any>()
|
|||
|
const size = addUnit(props.iconSize);
|
|||
|
// if(size!=null){
|
|||
|
style.set('width', size)
|
|||
|
style.set('height', size)
|
|||
|
// }
|
|||
|
return style
|
|||
|
})
|
|||
|
// #endif
|
|||
|
const drawableRef = ref<UniElement|null>(null);
|
|||
|
// #ifndef APP
|
|||
|
let canvas:HTMLCanvasElement|null = null
|
|||
|
// #endif
|
|||
|
let qrcode:QRCodeCanvas|null = null
|
|||
|
|
|||
|
const canvasToTempFilePath = (options: UTSJSONObject)=>{
|
|||
|
const format = options.getString('format') ?? 'png';
|
|||
|
const fail = options.get('fail') as LQrcodeFailCallback | null;
|
|||
|
const complete = options.get('complete') as LQrcodeCompleteCallback | null;
|
|||
|
const success = options.get('success') as LQrcodeSuccessCallback | null;
|
|||
|
// #ifdef APP
|
|||
|
const newOptions = {
|
|||
|
format,
|
|||
|
fail,
|
|||
|
complete,
|
|||
|
success,
|
|||
|
} as TakeSnapshotOptions
|
|||
|
drawableRef.value!.takeSnapshot(newOptions)
|
|||
|
// #endif
|
|||
|
// #ifdef WEB
|
|||
|
success?.({
|
|||
|
tempFilePath: canvas?.toDataURL('image/'+format)
|
|||
|
})
|
|||
|
// #endif
|
|||
|
|
|||
|
}
|
|||
|
const render = ()=>{
|
|||
|
const param:QRCodePropsTypes = {
|
|||
|
value: props.value,
|
|||
|
size: unitConvert(props.size),
|
|||
|
fgColor: props.color,
|
|||
|
level: ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M',
|
|||
|
marginSize: props.marginSize,
|
|||
|
includeMargin: props.bordered,
|
|||
|
imageSettings: null,
|
|||
|
} as QRCodePropsTypes
|
|||
|
// #ifdef APP-HARMONY
|
|||
|
param.value = props.value
|
|||
|
param.size = unitConvert(props.size)
|
|||
|
param.fgColor = props.color
|
|||
|
param.level = ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M'
|
|||
|
param.marginSize = props.marginSize
|
|||
|
param.includeMargin = props.bordered
|
|||
|
param.imageSettings = null
|
|||
|
// #endif
|
|||
|
if(props.icon != null){
|
|||
|
// if(toBoolean(props.iconSize) && toBoolean(props.icon)){
|
|||
|
const size = unitConvert(props.iconSize)
|
|||
|
param.imageSettings = {
|
|||
|
src: props.icon,
|
|||
|
width: size,
|
|||
|
height: size,
|
|||
|
excavate: true
|
|||
|
} as ImageSettings
|
|||
|
|
|||
|
// #ifdef APP-HARMONY
|
|||
|
param.imageSettings.src = props.icon
|
|||
|
param.imageSettings.width = size
|
|||
|
param.imageSettings.height = size
|
|||
|
param.imageSettings.excavate = true
|
|||
|
// #endif
|
|||
|
}
|
|||
|
qrcode?.render(param)
|
|||
|
if(props.useCanvasToTempFilePath){
|
|||
|
setTimeout(()=>{
|
|||
|
canvasToTempFilePath({
|
|||
|
success: (res: TakeSnapshotSuccess)=>{
|
|||
|
emits('success', res.tempFilePath)
|
|||
|
}
|
|||
|
})
|
|||
|
},100)
|
|||
|
}
|
|||
|
}
|
|||
|
defineExpose({
|
|||
|
canvasToTempFilePath
|
|||
|
})
|
|||
|
onMounted(()=>{
|
|||
|
nextTick(()=>{
|
|||
|
// #ifdef APP
|
|||
|
requestAnimationFrame(()=> {
|
|||
|
drawableRef.value?.getBoundingClientRectAsync()?.then(res => {
|
|||
|
const ctx = drawableRef.value!.getDrawableContext();
|
|||
|
qrcode = new QRCodeCanvas(ctx!)
|
|||
|
watchEffect(()=>{
|
|||
|
render()
|
|||
|
})
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
// #endif
|
|||
|
// #ifdef WEB
|
|||
|
canvas = document.createElement('canvas')
|
|||
|
canvas!.style.width = '100%'
|
|||
|
canvas!.style.height = '100%'
|
|||
|
drawableRef.value!.appendChild(canvas!)
|
|||
|
qrcode = new QRCodeCanvas(canvas, {
|
|||
|
pixelRatio: uni.getSystemInfoSync().pixelRatio,
|
|||
|
createImage: () => {
|
|||
|
const image = new Image();
|
|||
|
// @ts-ignore
|
|||
|
image.crossOrigin = 'anonymous';
|
|||
|
return image;
|
|||
|
}
|
|||
|
})
|
|||
|
watchEffect(()=>{
|
|||
|
render()
|
|||
|
})
|
|||
|
// #endif
|
|||
|
// #ifndef APP || WEB
|
|||
|
getCanvas(canvasId, { context }).then(res => {
|
|||
|
canvas = res;
|
|||
|
qrcode = new QRCodeCanvas(res, {
|
|||
|
path2D: false,
|
|||
|
pixelRatio: isCanvas2d && props.use2d ? uni.getSystemInfoSync().pixelRatio : 1,
|
|||
|
createImage
|
|||
|
})
|
|||
|
watchEffect(()=>{
|
|||
|
render()
|
|||
|
})
|
|||
|
})
|
|||
|
// #endif
|
|||
|
|
|||
|
})
|
|||
|
|
|||
|
})
|
|||
|
onUnmounted(()=>{
|
|||
|
// #ifdef WEB
|
|||
|
canvas?.remove();
|
|||
|
// #endif
|
|||
|
qrcode = null;
|
|||
|
})
|
|||
|
</script>
|
|||
|
<style lang="scss">
|
|||
|
.l-qrcode {
|
|||
|
position: relative;
|
|||
|
background-color: aqua;
|
|||
|
justify-content: center;
|
|||
|
align-items: center;
|
|||
|
|
|||
|
&-mask {
|
|||
|
position: absolute;
|
|||
|
// inset: 0;
|
|||
|
// inset-block-start: 0;
|
|||
|
// inset-inline-start: 0;
|
|||
|
z-index: 10;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
justify-content: center;
|
|||
|
align-items: center;
|
|||
|
// width: 100%;
|
|||
|
// height: 100%;
|
|||
|
color: rgba(0, 0, 0, 0.88);
|
|||
|
line-height: 1.5714285714285714;
|
|||
|
background: rgba(255, 255, 255, 0.96);
|
|||
|
text-align: center;
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|