This commit is contained in:
2025-08-21 11:23:54 +08:00
commit abf7f3c799
530 changed files with 60239 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
// export * from '../qrcode'
export * from '@/uni_modules/lime-qrcodegen/utssdk/interface';
import { type QRCodePropsTypes , type ImageSettings, type QRCodeCallback } from '@/uni_modules/lime-qrcodegen/utssdk/interface'
import { qrcodegen } from './qrcodegen'
const Ecc = qrcodegen.QrCode.Ecc
const QrCode = qrcodegen.QrCode
type Modules = boolean[][];
type Excavation = { x : number; y : number; w : number; h : number };
type ImageSettingExcavation = {
x: number
y: number
h: number
w: number
excavation: Excavation
}
const ERROR_LEVEL_MAP = {
L: Ecc.LOW,
M: Ecc.MEDIUM,
Q: Ecc.QUARTILE,
H: Ecc.HIGH,
};
const DEFAULT_SIZE:number = 128;
const DEFAULT_LEVEL:string = 'L';
// const DEFAULT_BGCOLOR:string = '#FFFFFF';
const DEFAULT_FGCOLOR:string = '#000000';
const DEFAULT_INCLUDEMARGIN:boolean = false;
const SPEC_MARGIN_SIZE : number = 4;
const DEFAULT_MARGIN_SIZE : number = 0;
// This is *very* rough estimate of max amount of QRCode allowed to be covered.
// It is "wrong" in a lot of ways (area is a terrible way to estimate, it
// really should be number of modules covered), but if for some reason we don't
// get an explicit height or width, I'd rather default to something than throw.
const DEFAULT_IMG_SCALE = 0.1;
// We could just do this in generatePath, except that we want to support
// non-Path2D canvas, so we need to keep it an explicit step.
function excavateModules(modules : Modules, excavation : Excavation) : Modules {
const ox = excavation.x
const oy = excavation.y
const oh = excavation.h
const ow = excavation.w
return modules.slice().map((row, y):boolean[] => {
if (y < oy || y >= oy + oh) {
return row;
}
return row.map((cell, x):boolean => {
if (x < ox || x >= ox + ow) {
return cell;
}
return false;
});
});
}
function getImageSettings(
cells : Modules,
size : number,
margin : number,
imageSettings: ImageSettings | null
) : ImageSettingExcavation | null {
if (imageSettings == null) {
return null;
}
// const result : UTSJSONObject = {}
const numCells = cells.length + margin * 2;
const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE);
const scale = numCells / size;
const width = imageSettings.width
const height = imageSettings.height
const ox = imageSettings.x
const oy = imageSettings.y
const excavate = imageSettings.excavate
const w = (width > 0 ? width : defaultSize) * scale;
const h = (height > 0 ? height: defaultSize) * scale;
const x = ox == null ? cells.length / 2 - w / 2 : ox * scale;
const y = oy == null ? cells.length / 2 - h / 2 : oy * scale;
let excavation: Excavation = {
x: 0,
y: 0,
w: 0,
h: 0,
};
if (excavate) {
let floorX = Math.floor(x);
let floorY = Math.floor(y);
let ceilW = Math.ceil(w + x - floorX);
let ceilH = Math.ceil(h + y - floorY);
// excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH };
excavation.x = floorX
excavation.y = floorY
excavation.w = ceilW
excavation.h = ceilH
}
return { x, y, h, w, excavation } as ImageSettingExcavation;
}
function getMarginSize(includeMargin : boolean, marginSize: number|null) : number {
if (marginSize != null) {
return Math.floor(marginSize);
}
return includeMargin ? SPEC_MARGIN_SIZE : DEFAULT_MARGIN_SIZE;
}
export class QRCodeCanvas {
ctx : DrawableContext
constructor(ctx : DrawableContext) {
this.ctx = ctx
}
render(props : QRCodePropsTypes, cb : QRCodeCallback | null=null) {
const ctx = this.ctx
const value = props.value
const size = props.size ?? DEFAULT_SIZE
const level = props.level ?? DEFAULT_LEVEL
const fgColor = props.fgColor ?? DEFAULT_FGCOLOR
const includeMargin = props.includeMargin || DEFAULT_INCLUDEMARGIN
const marginSize = props.marginSize
const imageSettings = props.imageSettings
if (value == null || value == '') {
return;
}
let cells = QrCode.encodeText(value, ERROR_LEVEL_MAP[level] as Ecc).getModules();
const margin = getMarginSize(includeMargin, marginSize);
const numCells = cells.length + margin * 2;
const scale = (size / numCells);
const calculatedImageSettings = getImageSettings(
cells,
size,
margin,
imageSettings
)
const haveImageToRender = calculatedImageSettings != null
const excavation: Excavation | null = haveImageToRender ? calculatedImageSettings?.excavation : null
if(haveImageToRender && excavation != null) {
cells = excavateModules(cells, excavation);
}
ctx.reset()
// ctx.clearRect(0, 0, size, size)
// ctx.fillStyle = bgColor;
// ctx.fillRect(0, 0, numCells, numCells);
ctx.fillStyle = fgColor;
cells.forEach(function (row, rdx) {
row.forEach(function (cell, cdx) {
if (cell) {
ctx.fillRect((cdx + margin) * scale, (rdx + margin) * scale, scale, scale);
}
});
});
ctx.update()
if(cb != null){
cb(cells)
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,300 @@
<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>

View File

@@ -0,0 +1,249 @@
<template>
<view class="l-qrcode" :style="[styles]">
<!-- #ifndef APP-NVUE -->
<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-NVUE -->
<web-view
ref="qrcodeRef"
@pagefinish="onFinished"
@error="onError"
@onPostMessage="onMessage"
:style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view>
<!-- #endif -->
<!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)">
<l-loading v-if="props.status == 'loading'"></l-loading>
<view class="l-qrcode-expired" v-if="props.status == 'expired'">
<slot></slot>
</view>
</view> -->
</view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* 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 { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue';
import QRCodeProps from './props'
// #ifndef APP-NVUE
import { getCanvas, isCanvas2d } from './useCanvas'
import { QRCodeCanvas } from './qrcode.js';
// #endif
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
import { createImage } from '@/uni_modules/lime-shared/createImage'
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64'
import { debounce } from '@/uni_modules/lime-shared/debounce'
const name = 'l-qrcode'
export default defineComponent({
name,
props: QRCodeProps,
emits: ['success'],
setup(props, {emit}) {
const context = getCurrentInstance();
const canvasId = `l-qrcode${context.uid}`
const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`)
let qrcode = null
let canvas = null
const qrCodeProps = computed(() => {
const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props
const imageSettings = {
src: icon,
x: undefined,
y: undefined,
height: unitConvert(iconSize),
width: unitConvert(iconSize),
excavate: true,
}
return {
value,
size: unitConvert(size),
level: errorLevel,
bgColor,
fgColor: color,
imageSettings: icon ? imageSettings : undefined,
includeMargin: bordered,
marginSize: marginSize ?? 0
}
})
// #ifdef APP-NVUE
const stacks = new Map()
// #endif
const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => {
if(!canvas) return
// #ifndef APP-NVUE
const copyArgs = Object.assign({
canvasId,
canvas: null
}, args)
if (isCanvas2d && props.use2d) {
// copyArgs.canvas = canvas
const tempFilePath = canvas.toDataURL();
copyArgs.success({
tempFilePath
})
return
}
if ('toTempFilePath' in canvas) {
canvas.toTempFilePath(copyArgs)
} else {
uni.canvasToTempFilePath(copyArgs, context);
}
// #endif
// #ifdef APP-NVUE
if(!stacks.size) {
const flie = 'file-' + Math.random();
const stack = {args, time: +new Date()}
stacks.set(`${flie}`, stack)
canvas.toDataURL(flie)
setTimeout(() => {
const stack = stacks.get(flie)
if(stack && 'fail' in stack.args) {
stack.args.fail({
error: '超时'
})
stacks.delete(flie)
}
},5000)
}
// #endif
})
const useCanvasToTempFilePath = () => {
if(props.useCanvasToTempFilePath) {
canvasToTempFilePath({
success(res: UniNamespace.CanvasToTempFilePathRes) {
emit('success', res.tempFilePath)
}
})
}
}
// #ifdef APP-NVUE
const onFinished = () => {
const { pixelRatio } = uni.getSystemInfoSync()
canvas = {
toDataURL(flie: string) {
const ref: any = context.refs['qrcodeRef'];
if(ref) {
ref?.evalJS(`toDataURL('${flie}')`)
}
}
};
qrcode = {
async render(props: any) {
const ref: any = context.refs['qrcodeRef'];
const { src } = props.imageSettings || { };
if(!ref) return
if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) {
props.imageSettings.src = await pathToBase64(src)
}
const _props = JSON.stringify(Object.assign({}, props, {pixelRatio}));
ref?.evalJS(`render(${_props})`);
}
}
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
}
const onError = () => {
console.warn('lime-qrcode 加载失败')
}
const onMessage = (e: any) => {
const {detail:{data: [res]}} = e
if(res.event == 'toDataURL') {
const {file, image, msg} = res.data;
const stack = stacks.get(file)
if(stack && image && 'success' in stack.args) {
stack.args.success({tempFilePath: image})
stacks.delete(file)
} else if(stack && 'fails' in stack.args) {
stack.args.fail({error: msg})
stacks.delete(file)
}
}
}
// #endif
const propsWatch = watch(props, () => {
if (qrcode) {
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
}
})
onMounted(() => {
// #ifndef APP-NVUE
getCanvas(canvasId, { context }).then(res => {
canvas = res;
qrcode = new QRCodeCanvas(res, {
path2D: false,
pixelRatio: isCanvas2d && props.use2d ? uni.getSystemInfoSync().pixelRatio : 1,
createImage
})
qrcode.render(qrCodeProps.value)
useCanvasToTempFilePath()
})
// #endif
})
onUnmounted(() => {
propsWatch && propsWatch()
})
return {
canvasId,
styles,
props,
canvasToTempFilePath,
// #ifdef APP-NVUE
onFinished,
onError,
onMessage
// #endif
}
}
})
</script>
<style lang="scss">
.l-qrcode {
position: relative;
&-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>

View File

@@ -0,0 +1,40 @@
// @ts-nocheck
// import type { PropType } from './vue'
export default {
value: String,
icon: String,
size: {
type: [Number, String],
default: 160
},
iconSize: {
type: [Number, String],
default: 40
},
marginSize: Number,
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: Boolean,
use2d: {
type: Boolean,
default: true
}
// status: {
// type: String as PropType<'active'|'expired'|'loading'>,
// default: 'active' // active | expired | loading
// }
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
// @ts-nocheck
export type ImageSettings = {
width: number
height: number
x?: number
y?: number
excavate: boolean
}
export type QRCodePropsTypes = {
value?: string
size?: number
fgColor?: string
level?: string
marginSize: number
includeMargin: boolean
imageSettings?: ImageSettings
}
export type QRCodeCallback = (cells : boolean[][]) => void
export type Excavation = {
x: number
y: number
h: number
w: number
}
/**
* 成功回调函数定义
*/
export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void
/**
* 失败回调函数定义
*/
export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void
/**
* 完成回调函数定义
*/
export type TakeSnapshotCompleteCallback = (res: any) => void
export type LQrcodeFailCallback = TakeSnapshotFailCallback
export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback
export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback

View File

@@ -0,0 +1,78 @@
// @ts-nocheck
import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue'
import { getRect } from '@/uni_modules/lime-shared/getRect'
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
export const isCanvas2d = canIUseCanvas2d()
export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) {
let { context } = options
// #ifdef MP || VUE2
if (context.proxy) context = context.proxy
// #endif
return getRect('#' + canvasId, context, isCanvas2d).then(res => {
if(res.node){
return res.node
} else {
const ctx = uni.createCanvasContext(canvasId, context)
return {
getContext(type: string) {
if(type == '2d') {
return ctx
}
},
width: res.width,
height: res.height,
}
// #ifdef H5
// canvas.value = context.proxy.$el.querySelector('#'+ canvasId)
// #endif
}
})
}
// #ifndef H5 || APP-NVUE
class Image {
currentSrc: string | null = null
naturalHeight: number = 0
naturalWidth: number = 0
width: number = 0
height: number = 0
tagName: string = 'IMG'
path: any = ''
crossOrigin: any = ''
referrerPolicy: any = ''
onload: () => void
onerror: () => void
constructor() {}
set src(src) {
this.currentSrc = src
uni.getImageInfo({
src,
success: (res) => {
this.path = res.path
this.naturalWidth = this.width = res.width
this.naturalHeight = this.height = res.height
this.onload()
},
fail: () => {
this.onerror()
}
})
}
get src() {
return this.currentSrc
}
}
// #endif
export function createImage(canvas: WechatMiniprogram.Canvas) {
if(canvas && canvas.createImage) {
return canvas.createImage()
} else if(typeof window != 'undefined' && window.Image) {
return new window.Image()
}
// #ifndef H5 || APP-NVUE
return new Image()
// #endif
}

View File

@@ -0,0 +1,35 @@
export function addUnit(value: any|null):string{
if(value == null){
return ''
}
value = `${value}`
return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value
}
export function unitConvert(value: any|null): number{
if(typeof value == 'number'){
return value as number
}
if(typeof value == 'string'){
value = `${value}`
if(/^(-)?\d+(\\.\d+)?$/.test(value)){
return parseFloat(value);
}
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
const results = reg.exec(value);
if (results == null) {
return 0;
}
const unit = results[3];
const v = parseFloat(value);
if (unit == 'rpx') {
const { windowWidth } = uni.getWindowInfo()
return windowWidth / 750 * v;
}
if (unit == 'px') {
return v;
}
}
return 0;
}

View File

@@ -0,0 +1,140 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">QRCode</text>
<text class="demo-block__desc-text">能够将文本转换生成二维码的组件,支持自定义配色和 Logo 配置</text>
<view class="demo-block__body">
<view class="demo-block card">
<text class="demo-block__title-text large">基础</text>
<view class="demo-block__body">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" :use2d="false"></l-qrcode>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">icon</text>
<view class="demo-block__body">
<image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<view style="flex-direction: row; justify-content: space-between">
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
<l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
</view>
<button type="primary" style="margin-top: 20rpx;" @click="onClick">生成图片</button>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">颜色</text>
<view class="demo-block__body">
<view style="flex-direction: row; justify-content: space-between">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgba(82,196,26,1)"></l-qrcode>
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgba(22,119,255,1)" bgColor="rgb(245,245,245)"></l-qrcode>
</view>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">纠错比例</text>
<view class="demo-block__body">
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
<button type="primary" style="margin-top: 20rpx;" @click="onToggle">切换纠错等级:{{levels[index]}}</button>
</view>
</view>
<view class="demo-block card">
<text class="demo-block__title-text large">动态</text>
<view class="demo-block__body">
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
<button type="primary" style="margin-top: 20rpx;" @click="update">更新</button>
</view>
</view>
</view>
</view>
</template>
<script>
// import {ComponentPublicInstance} from 'vue'
export default {
name: 'lime-qrcode',
data() {
return {
text: 'qcoon.com.cn',
image: '',
index: 0,
levels: ['L', 'M', 'Q', 'H']
}
},
methods:{
success(src: string) {
console.log(`src`, src)
},
update() {
this.text = `qcoon.cn?v=${Math.random()}`
},
onToggle() {
this.index++
this.index = this.index % this.levels.length
},
onClick() {
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
el.canvasToTempFilePath({
success:(res: TakeSnapshotSuccess)=>{
this.image = res.tempFilePath
}
})
}
},
mounted() {
}
}
</script>
<style lang="scss">
.demo-block {
margin: 32px 10px 0;
overflow: visible;
&.card{
background-color: white;
padding: 30rpx;
margin-bottom: 20rpx;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,141 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">QRCode 二维码</text>
<text class="demo-block__desc-text">生成二维码</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">基础</text>
<view class="demo-block__body">
<view style="display: flex; gap: 10px">
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
<l-qrcode :use2d="false" ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
<!-- <l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> -->
</view>
<button @click="onClick">生成图片</button>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">颜色</text>
<view class="demo-block__body">
<view style="display: flex; gap: 10px">
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
</view>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">纠错比例</text>
<view class="demo-block__body">
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
<button @click="onToggle">切换纠错等级{{levels[index]}}</button>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">动态</text>
<view class="demo-block__body">
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
<button @click="update">更新</button>
</view>
</view>
</view>
</view>
</template>
<script>
import {ref, defineComponent} from '@/uni_modules/lime-shared/vue'
export default defineComponent({
setup() {
const qrcodeRef = ref(null)
const image = ref(null)
const text = ref('qcoon.com.cn')
const levels = ['L', 'M', 'Q', 'H']
let index = ref(0)
const onToggle = () => {
index.value++
index.value = index.value % levels.length
}
const onClick = () => {
if(qrcodeRef.value) {
qrcodeRef.value.canvasToTempFilePath({
success(res) {
image.value = res.tempFilePath
console.log('success:::', res)
},
fail(err) {
console.log('err:::', err)
}
})
}
}
const success = (res) => {
console.log('res', res)
}
const update = () =>{
text.value = `qcoon.cn?v=${Math.random()}`
}
return {
levels,
index,
image,
text,
qrcodeRef,
onClick,
update,
success,
onToggle,
}
}
})
</script>
<style lang="scss">
.demo-block {
margin: 32px 10px 0;
overflow: visible;
&.card{
background-color: white;
padding: 30rpx;
margin-bottom: 20rpx;
}
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
display: flex;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>