Files
YGChatCS/components/Qrcode/index.vue
2025-07-30 20:55:26 +08:00

272 lines
5.4 KiB
Vue

<template xlang="wxml" minapp="mpvue">
<view class="tki-qrcode">
<!-- #ifndef MP-ALIPAY -->
<canvas
class="tki-qrcode-canvas"
:canvas-id="cid"
:style="{ width: cpSize + 'px', height: cpSize + 'px' }"
/>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<canvas
:id="cid"
:width="cpSize"
:height="cpSize"
class="tki-qrcode-canvas"
/>
<!-- #endif -->
<image
v-show="show"
:src="result"
:style="{ width: cpSize + 'px', height: cpSize + 'px' }"
/>
</view>
</template>
<script setup>
import { ref, computed, watch, onMounted, getCurrentInstance } from "vue";
import QRCode from "./qrcode.js";
let qrcode;
// 定义props
const props = defineProps({
cid: {
type: String,
default: "tki-qrcode-canvas",
},
size: {
type: Number,
default: 200,
},
unit: {
type: String,
default: "upx",
},
show: {
type: Boolean,
default: true,
},
val: {
type: String,
default: "",
},
background: {
type: String,
default: "#ffffff",
},
foreground: {
type: String,
default: "#000000",
},
pdground: {
type: String,
default: "#000000",
},
icon: {
type: String,
default: "",
},
iconSize: {
type: Number,
default: 40,
},
lv: {
type: Number,
default: 3,
},
onval: {
type: Boolean,
default: false,
},
loadMake: {
type: Boolean,
default: false,
},
usingComponents: {
type: Boolean,
default: true,
},
showLoading: {
type: Boolean,
default: true,
},
loadingText: {
type: String,
default: "二维码生成中",
},
});
// 定义emits
const emit = defineEmits(["result"]);
// 获取当前实例
const instance = getCurrentInstance()
// 创建一个安全的上下文对象
const getContext = () => {
return instance?.proxy || instance?.ctx || {}
};
// 响应式数据
const result = ref("");
// 计算属性
const cpSize = computed(() => {
if (props.unit === "upx") {
return uni.upx2px(props.size);
} else {
return props.size;
}
});
// 工具函数
const _empty = (v) => {
let tp = typeof v;
let rt = false;
if (tp === "number" && String(v) === "") {
rt = true;
} else if (tp === "undefined") {
rt = true;
} else if (tp === "object") {
if (JSON.stringify(v) === "{}" || JSON.stringify(v) === "[]" || v === null)
rt = true;
} else if (tp === "string") {
if (
v === "" ||
v === "undefined" ||
v === "null" ||
v === "{}" ||
v === "[]"
)
rt = true;
} else if (tp === "function") {
rt = false;
}
return rt;
};
// 方法定义
const _makeCode = () => {
console.log('开始生成二维码,内容:', props.val, '大小:', cpSize.value)
if (!_empty(props.val)) {
try {
qrcode = new QRCode({
context: getContext(), // 上下文环境
canvasId: props.cid, // canvas-id
usingComponents: props.usingComponents, // 是否是自定义组件
showLoading: props.showLoading, // 是否显示loading
loadingText: props.loadingText, // loading文字
text: props.val, // 生成内容
size: cpSize.value, // 二维码大小
background: props.background, // 背景色
foreground: props.foreground, // 前景色
pdground: props.pdground, // 定位角点颜色
correctLevel: props.lv, // 容错级别
image: props.icon, // 二维码图标
imageSize: props.iconSize, // 二维码图标大小
cbResult: function (res) {
// 生成二维码的回调
console.log('二维码生成完成:', res)
_result(res);
},
});
} catch (error) {
console.error('二维码生成失败:', error)
}
} else {
console.log('二维码内容为空')
uni.showToast({
title: "二维码内容不能为空",
icon: "none",
duration: 2000,
});
}
};
const _clearCode = () => {
_result("");
if (qrcode) {
qrcode.clear();
}
};
const _saveCode = () => {
if (result.value !== "") {
uni.saveImageToPhotosAlbum({
filePath: result.value,
success: function () {
uni.showToast({
title: "二维码保存成功",
icon: "success",
duration: 2000,
});
},
});
}
};
const _result = (res) => {
result.value = res;
emit("result", res);
};
// 监听器
watch(
() => props.size,
(newVal, oldVal) => {
if (newVal !== oldVal && !_empty(newVal)) {
if (!_empty(props.val)) {
setTimeout(() => {
_makeCode();
}, 100);
}
}
}
);
watch(
() => props.val,
(newVal, oldVal) => {
if (props.onval) {
if (newVal !== oldVal && !_empty(newVal)) {
setTimeout(() => {
_makeCode();
}, 0);
}
}
}
);
// 生命周期
onMounted(() => {
if (props.loadMake) {
if (!_empty(props.val)) {
setTimeout(() => {
_makeCode();
}, 0);
}
}
});
// 暴露方法给父组件
defineExpose({
_makeCode,
_clearCode,
_saveCode,
});
</script>
<style>
.tki-qrcode {
position: relative;
}
.tki-qrcode-canvas {
position: fixed;
top: -99999upx;
left: -99999upx;
z-index: -99999;
}
</style>