From db685b04e52efd75213ef7540fdc0bed7dccfd9e Mon Sep 17 00:00:00 2001
From: wangrong <wangrong@shsening.com>
Date: 星期一, 26 十月 2026 21:13:37 +0800
Subject: [PATCH] 更新小程序

---
 cool/utils/qrcode.ts |  199 ++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 163 insertions(+), 36 deletions(-)

diff --git a/cool/utils/qrcode.ts b/cool/utils/qrcode.ts
index 47af253..17f6237 100644
--- a/cool/utils/qrcode.ts
+++ b/cool/utils/qrcode.ts
@@ -1,39 +1,173 @@
 import QRCode from "qrcode";
 
-const QRCODE_KEY = "recycleQrcodeBase64";
+function u32(buf: Uint8Array, offset: number, value: number) {
+	buf[offset] = (value >>> 24) & 255;
+	buf[offset + 1] = (value >>> 16) & 255;
+	buf[offset + 2] = (value >>> 8) & 255;
+	buf[offset + 3] = value & 255;
+}
 
-export function generateQrcode(text: string): Promise<string> {
-	return new Promise((resolve) => {
-		QRCode.toDataURL(
-			text,
-			{
-				width: 300,
-				margin: 2,
-			},
-			(err, url) => {
-				if (err) {
-					console.error("鉂� 浜岀淮鐮佺敓鎴愬け璐�", err);
-					resolve("");
-					return;
-				}
-				resolve(url);
-			},
-		);
+function crc32(data: Uint8Array) {
+	let c = 0xffffffff;
+	for (let i = 0; i < data.length; i++) {
+		c ^= data[i];
+		for (let k = 0; k < 8; k++) {
+			c = (c >>> 1) ^ (c & 1 ? 0xedb88320 : 0);
+		}
+	}
+	return (c ^ 0xffffffff) >>> 0;
+}
+
+function adler32(data: Uint8Array) {
+	let a = 1;
+	let b = 0;
+	for (let i = 0; i < data.length; i++) {
+		a = (a + data[i]) % 65521;
+		b = (b + a) % 65521;
+	}
+	return ((b << 16) | a) >>> 0;
+}
+
+function chunk(type: string, data: Uint8Array) {
+	const out = new Uint8Array(12 + data.length);
+	u32(out, 0, data.length);
+	out[4] = type.charCodeAt(0);
+	out[5] = type.charCodeAt(1);
+	out[6] = type.charCodeAt(2);
+	out[7] = type.charCodeAt(3);
+	out.set(data, 8);
+	u32(out, 8 + data.length, crc32(out.subarray(4, 8 + data.length)));
+	return out;
+}
+
+function concat(parts: Uint8Array[]) {
+	const len = parts.reduce((sum, part) => sum + part.length, 0);
+	const out = new Uint8Array(len);
+	let offset = 0;
+	for (const part of parts) {
+		out.set(part, offset);
+		offset += part.length;
+	}
+	return out;
+}
+
+function zlibStore(raw: Uint8Array) {
+	const blocks: Uint8Array[] = [];
+	let offset = 0;
+	while (offset < raw.length) {
+		const len = Math.min(65535, raw.length - offset);
+		const last = offset + len >= raw.length;
+		const block = new Uint8Array(5 + len);
+		block[0] = last ? 1 : 0;
+		block[1] = len & 255;
+		block[2] = (len >>> 8) & 255;
+		const nlen = (~len) & 0xffff;
+		block[3] = nlen & 255;
+		block[4] = (nlen >>> 8) & 255;
+		block.set(raw.subarray(offset, offset + len), 5);
+		blocks.push(block);
+		offset += len;
+	}
+	const bodyLen = blocks.reduce((sum, block) => sum + block.length, 0);
+	const out = new Uint8Array(2 + bodyLen + 4);
+	out[0] = 0x78;
+	out[1] = 0x01;
+	let cursor = 2;
+	for (const block of blocks) {
+		out.set(block, cursor);
+		cursor += block.length;
+	}
+	u32(out, cursor, adler32(raw));
+	return out;
+}
+
+function bytesToBase64(bytes: Uint8Array) {
+	const api = typeof uni === "undefined" ? undefined : uni;
+	if (api && typeof api.arrayBufferToBase64 === "function") {
+		const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
+		return api.arrayBufferToBase64(buffer as ArrayBuffer);
+	}
+	const table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	let out = "";
+	for (let i = 0; i < bytes.length; i += 3) {
+		const a = bytes[i];
+		const b = i + 1 < bytes.length ? bytes[i + 1] : 0;
+		const c = i + 2 < bytes.length ? bytes[i + 2] : 0;
+		const n = (a << 16) | (b << 8) | c;
+		out += table[(n >> 18) & 63];
+		out += table[(n >> 12) & 63];
+		out += i + 1 < bytes.length ? table[(n >> 6) & 63] : "=";
+		out += i + 2 < bytes.length ? table[n & 63] : "=";
+	}
+	return out;
+}
+
+/** 涓嶄緷璧栨祻瑙堝櫒 canvas锛岀洿鎺ョ敾鍑哄彲鍦ㄥ皬绋嬪簭 image 涓樉绀虹殑 PNG銆� */
+export function createQrPng(text: string) {
+	const qr = QRCode.create(text, { errorCorrectionLevel: "M" });
+	const modules = qr.modules;
+	const count = modules.size;
+	const margin = 4;
+	const scale = 8;
+	const size = (count + margin * 2) * scale;
+	const raw = new Uint8Array(size * (size + 1));
+	let cursor = 0;
+	for (let y = 0; y < size; y++) {
+		raw[cursor++] = 0;
+		const row = Math.floor(y / scale) - margin;
+		for (let x = 0; x < size; x++) {
+			const col = Math.floor(x / scale) - margin;
+			const dark =
+				row >= 0 && col >= 0 && row < count && col < count && !!modules.get(row, col);
+			raw[cursor++] = dark ? 0 : 255;
+		}
+	}
+	const ihdr = new Uint8Array(13);
+	u32(ihdr, 0, size);
+	u32(ihdr, 4, size);
+	ihdr[8] = 8;
+	const signature = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);
+	return concat([
+		signature,
+		chunk("IHDR", ihdr),
+		chunk("IDAT", zlibStore(raw)),
+		chunk("IEND", new Uint8Array(0)),
+	]);
+}
+
+function fileName(text: string) {
+	let hash = 0;
+	for (let i = 0; i < text.length; i++) {
+		hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
+	}
+	return hash.toString(16);
+}
+
+function savePng(bytes: Uint8Array, text: string) {
+	const base64 = bytesToBase64(bytes);
+	const dataUrl = `data:image/png;base64,${base64}`;
+	const root = (globalThis as any).wx?.env?.USER_DATA_PATH;
+	if (!root || typeof uni === "undefined" || typeof uni.getFileSystemManager !== "function") {
+		return Promise.resolve(dataUrl);
+	}
+	const filePath = `${root}/recycle-qr-${fileName(text)}.png`;
+	return new Promise<string>((resolve) => {
+		uni.getFileSystemManager().writeFile({
+			filePath,
+			data: base64,
+			encoding: "base64",
+			success: () => resolve(filePath),
+			fail: () => resolve(dataUrl),
+		});
 	});
 }
 
-export function generateRecycleQrcode(text: string): Promise<string> {
-	return new Promise((resolve) => {
-		const cached = uni.getStorageSync(QRCODE_KEY);
-		if (cached) return resolve(cached);
+export function generateQrcode(text: string) {
+	return savePng(createQrPng(text), text);
+}
 
-		generateQrcode(text).then((url) => {
-			if (url) {
-				uni.setStorageSync(QRCODE_KEY, url);
-			}
-			resolve(url);
-		});
-	});
+export function generateRecycleQrcode(text: string) {
+	return generateQrcode(text);
 }
 
 /**
@@ -43,10 +177,3 @@
 export function clearQrcodeCache(key: string) {
 	uni.removeStorageSync(key);
 }
-
-/**
- * 娓呴櫎鎵�鏈変簩缁寸爜缂撳瓨
- */
-export function clearAllQrcodeCache() {
-	uni.clearStorageSync();
-}
\ No newline at end of file

--
Gitblit v1.9.1