From c7b4b9a2f4c0f05aeb60a9e3f5fba5d9a3676f3f Mon Sep 17 00:00:00 2001 From: gaoluyang <2820782392@qq.com> Date: 星期一, 18 八月 2025 16:22:42 +0800 Subject: [PATCH] 中强恒兴设备管理页面添加 --- src/components/QRCodeGenerator/index.vue | 541 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 541 insertions(+), 0 deletions(-) diff --git a/src/components/QRCodeGenerator/index.vue b/src/components/QRCodeGenerator/index.vue new file mode 100644 index 0000000..1708130 --- /dev/null +++ b/src/components/QRCodeGenerator/index.vue @@ -0,0 +1,541 @@ +<template> + <div class="qr-code-generator"> + <!-- 浜岀淮鐮佺敓鎴愯〃鍗� --> + <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="qr-form"> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鏍囪瘑绫诲瀷" prop="type"> + <el-select v-model="form.type" placeholder="璇烽�夋嫨鏍囪瘑绫诲瀷" style="width: 100%"> + <el-option label="浜岀淮鐮�" value="qrcode"></el-option> + <el-option label="闃蹭吉鐮�" value="security"></el-option> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鍐呭" prop="content"> + <el-input + v-model="form.content" + placeholder="璇疯緭鍏ヨ缂栫爜鐨勫唴瀹�" + :type="form.type === 'security' ? 'textarea' : 'text'" + :rows="form.type === 'security' ? 3 : 1" + ></el-input> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="灏哄" prop="size"> + <el-input-number + v-model="form.size" + :min="100" + :max="500" + :step="50" + style="width: 100%" + ></el-input-number> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="杈硅窛" prop="margin"> + <el-input-number + v-model="form.margin" + :min="0" + :max="10" + :step="1" + style="width: 100%" + ></el-input-number> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="鍓嶆櫙鑹�" prop="foregroundColor"> + <el-color-picker v-model="form.foregroundColor" style="width: 100%"></el-color-picker> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鑳屾櫙鑹�" prop="backgroundColor"> + <el-color-picker v-model="form.backgroundColor" style="width: 100%"></el-color-picker> + </el-form-item> + </el-col> + </el-row> + + <el-row :gutter="20"> + <el-col :span="24"> + <el-form-item> + <el-button type="primary" @click="generateCode" :loading="generating"> + 鐢熸垚{{ form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�' }} + </el-button> + <el-button @click="resetForm">閲嶇疆</el-button> + </el-form-item> + </el-col> + </el-row> + </el-form> + + <!-- 鐢熸垚鐨勭爜鏄剧ず鍖哄煙 --> + <div v-if="generatedCodeUrl" class="code-display"> + <el-divider content-position="center"> + {{ form.type === 'qrcode' ? '鐢熸垚鐨勪簩缁寸爜' : '鐢熸垚鐨勯槻浼爜' }} + </el-divider> + + <div class="code-container"> + <div class="code-image"> + <img :src="generatedCodeUrl" :alt="form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'" /> + </div> + + <div class="code-info"> + <p><strong>鍐呭锛�</strong>{{ form.content }}</p> + <p><strong>绫诲瀷锛�</strong>{{ form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�' }}</p> + <p><strong>灏哄锛�</strong>{{ form.size }}x{{ form.size }}px</p> + <p><strong>鐢熸垚鏃堕棿锛�</strong>{{ generateTime }}</p> + </div> + </div> + + <div class="code-actions"> + <el-button type="success" @click="downloadCode" icon="Download"> + 涓嬭浇鍥剧墖 + </el-button> + <el-button type="primary" @click="copyToClipboard" icon="CopyDocument"> + 澶嶅埗鍐呭 + </el-button> + <el-button @click="printCode" icon="Printer"> + 鎵撳嵃 + </el-button> + </div> + </div> + + <!-- 鎵归噺鐢熸垚瀵硅瘽妗� --> + <el-dialog v-model="batchDialogVisible" title="鎵归噺鐢熸垚" width="600px"> + <el-form :model="batchForm" label-width="120px"> + <el-form-item label="鐢熸垚鏁伴噺"> + <el-input-number v-model="batchForm.quantity" :min="1" :max="100" style="width: 100%"></el-input-number> + </el-form-item> + <el-form-item label="鍓嶇紑"> + <el-input v-model="batchForm.prefix" placeholder="璇疯緭鍏ュ墠缂�锛屽锛歅ROD_"></el-input> + </el-form-item> + <el-form-item label="璧峰缂栧彿"> + <el-input-number v-model="batchForm.startNumber" :min="1" style="width: 100%"></el-input-number> + </el-form-item> + </el-form> + + <template #footer> + <div class="dialog-footer"> + <el-button @click="batchDialogVisible = false">鍙栨秷</el-button> + <el-button type="primary" @click="generateBatchCodes">寮�濮嬬敓鎴�</el-button> + </div> + </template> + </el-dialog> + + <!-- 鎵归噺鐢熸垚缁撴灉 --> + <div v-if="batchCodes.length > 0" class="batch-results"> + <el-divider content-position="center">鎵归噺鐢熸垚缁撴灉</el-divider> + + <div class="batch-grid"> + <div + v-for="(code, index) in batchCodes" + :key="index" + class="batch-item" + > + <img :src="code.url" :alt="code.content" /> + <p class="batch-content">{{ code.content }}</p> + <el-button size="small" @click="downloadSingleCode(code)">涓嬭浇</el-button> + </div> + </div> + + <div class="batch-actions"> + <el-button type="success" @click="downloadAllCodes">涓嬭浇鍏ㄩ儴</el-button> + <el-button @click="clearBatchCodes">娓呯┖缁撴灉</el-button> + </div> + </div> + </div> +</template> + +<script setup> +import { ref, reactive, computed, onMounted } from 'vue' +import QRCode from 'qrcode' +import { ElMessage, ElMessageBox } from 'element-plus' +import { Download, CopyDocument, Printer } from '@element-plus/icons-vue' + +// 瀹氫箟缁勪欢鍚嶇О +defineOptions({ + name: 'QRCodeGenerator' +}) + +// 琛ㄥ崟鏁版嵁 +const form = reactive({ + type: 'qrcode', + content: '', + size: 200, + margin: 2, + foregroundColor: '#000000', + backgroundColor: '#FFFFFF' +}) + +// 琛ㄥ崟楠岃瘉瑙勫垯 +const rules = { + type: [{ required: true, message: '璇烽�夋嫨鏍囪瘑绫诲瀷', trigger: 'change' }], + content: [{ required: true, message: '璇疯緭鍏ュ唴瀹�', trigger: 'blur' }] +} + +// 鍝嶅簲寮忔暟鎹� +const formRef = ref() +const generating = ref(false) +const generatedCodeUrl = ref('') +const generateTime = ref('') +const batchDialogVisible = ref(false) +const batchForm = reactive({ + quantity: 10, + prefix: '', + startNumber: 1 +}) +const batchCodes = ref([]) + +// 鐢熸垚浜岀淮鐮佹垨闃蹭吉鐮� +const generateCode = async () => { + try { + await formRef.value.validate() + + if (!form.content.trim()) { + ElMessage.warning('璇疯緭鍏ヨ缂栫爜鐨勫唴瀹�') + return + } + + generating.value = true + + if (form.type === 'qrcode') { + // 鐢熸垚浜岀淮鐮� + generatedCodeUrl.value = await QRCode.toDataURL(form.content, { + width: form.size, + margin: form.margin, + color: { + dark: form.foregroundColor, + light: form.backgroundColor + }, + errorCorrectionLevel: 'M' + }) + } else { + // 鐢熸垚闃蹭吉鐮侊紙浣跨敤浜岀淮鐮佹妧鏈紝浣嗗唴瀹规牸寮忎笉鍚岋級 + const securityContent = generateSecurityCode(form.content) + generatedCodeUrl.value = await QRCode.toDataURL(securityContent, { + width: form.size, + margin: form.margin, + color: { + dark: form.foregroundColor, + light: form.backgroundColor + }, + errorCorrectionLevel: 'H' // 闃蹭吉鐮佷娇鐢ㄦ渶楂樼籂閿欑骇鍒� + }) + } + + generateTime.value = new Date().toLocaleString() + ElMessage.success('鐢熸垚鎴愬姛锛�') + + } catch (error) { + console.error('鐢熸垚澶辫触:', error) + ElMessage.error('鐢熸垚澶辫触锛�' + error.message) + } finally { + generating.value = false + } +} + +// 鐢熸垚闃蹭吉鐮佸唴瀹� +const generateSecurityCode = (content) => { + const timestamp = Date.now() + const random = Math.random().toString(36).substr(2, 8) + return `SEC_${content}_${timestamp}_${random}` +} + +// 涓嬭浇鐢熸垚鐨勭爜 +const downloadCode = () => { + if (!generatedCodeUrl.value) { + ElMessage.warning('璇峰厛鐢熸垚鐮�') + return + } + + const a = document.createElement('a') + a.href = generatedCodeUrl.value + a.download = `${form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'}_${new Date().getTime()}.png` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + ElMessage.success('涓嬭浇鎴愬姛锛�') +} + +// 澶嶅埗鍐呭鍒板壀璐存澘 +const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(form.content) + ElMessage.success('鍐呭宸插鍒跺埌鍓创鏉�') + } catch (error) { + // 闄嶇骇鏂规 + const textArea = document.createElement('textarea') + textArea.value = form.content + document.body.appendChild(textArea) + textArea.select() + document.execCommand('copy') + document.body.removeChild(textArea) + ElMessage.success('鍐呭宸插鍒跺埌鍓创鏉�') + } +} + +// 鎵撳嵃鐮� +const printCode = () => { + if (!generatedCodeUrl.value) { + ElMessage.warning('璇峰厛鐢熸垚鐮�') + return + } + + const printWindow = window.open('', '_blank') + printWindow.document.write(` + <html> + <head> + <title>鎵撳嵃${form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'}</title> + <style> + body { text-align: center; padding: 20px; } + img { max-width: 100%; height: auto; } + .info { margin: 20px 0; } + </style> + </head> + <body> + <h2>${form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'}</h2> + <img src="${generatedCodeUrl.value}" alt="${form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'}" /> + <div class="info"> + <p><strong>鍐呭锛�</strong>${form.content}</p> + <p><strong>鐢熸垚鏃堕棿锛�</strong>${generateTime.value}</p> + </div> + </body> + </html> + `) + printWindow.document.close() + printWindow.print() +} + +// 閲嶇疆琛ㄥ崟 +const resetForm = () => { + formRef.value.resetFields() + generatedCodeUrl.value = '' + generateTime.value = '' + batchCodes.value = [] +} + +// 鎵归噺鐢熸垚 +const generateBatchCodes = async () => { + if (!batchForm.prefix.trim()) { + ElMessage.warning('璇疯緭鍏ュ墠缂�') + return + } + + batchCodes.value = [] + generating.value = true + + try { + for (let i = 0; i < batchForm.quantity; i++) { + const number = batchForm.startNumber + i + const content = `${batchForm.prefix}${number.toString().padStart(6, '0')}` + + let codeUrl + if (form.type === 'qrcode') { + codeUrl = await QRCode.toDataURL(content, { + width: form.size, + margin: form.margin, + color: { + dark: form.foregroundColor, + light: form.backgroundColor + } + }) + } else { + const securityContent = generateSecurityCode(content) + codeUrl = await QRCode.toDataURL(securityContent, { + width: form.size, + margin: form.margin, + color: { + dark: form.foregroundColor, + light: form.backgroundColor + } + }) + } + + batchCodes.value.push({ + content, + url: codeUrl + }) + } + + ElMessage.success(`鎵归噺鐢熸垚瀹屾垚锛屽叡鐢熸垚 ${batchForm.quantity} 涓爜`) + batchDialogVisible.value = false + + } catch (error) { + console.error('鎵归噺鐢熸垚澶辫触:', error) + ElMessage.error('鎵归噺鐢熸垚澶辫触锛�' + error.message) + } finally { + generating.value = false + } +} + +// 涓嬭浇鍗曚釜鎵归噺鐢熸垚鐨勭爜 +const downloadSingleCode = (code) => { + const a = document.createElement('a') + a.href = code.url + a.download = `${code.content}.png` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) +} + +// 涓嬭浇鎵�鏈夋壒閲忕敓鎴愮殑鐮� +const downloadAllCodes = async () => { + if (batchCodes.value.length === 0) { + ElMessage.warning('娌℃湁鍙笅杞界殑鐮�') + return + } + + try { + // 浣跨敤JSZip鎵撳寘涓嬭浇 + const JSZip = await import('jszip') + const zip = new JSZip.default() + + batchCodes.value.forEach((code, index) => { + // 灏哹ase64杞崲涓篵lob + const base64Data = code.url.split(',')[1] + const byteCharacters = atob(base64Data) + const byteNumbers = new Array(byteCharacters.length) + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i) + } + const byteArray = new Uint8Array(byteNumbers) + + zip.file(`${code.content}.png`, byteArray) + }) + + const content = await zip.generateAsync({ type: 'blob' }) + const a = document.createElement('a') + a.href = URL.createObjectURL(content) + a.download = `鎵归噺${form.type === 'qrcode' ? '浜岀淮鐮�' : '闃蹭吉鐮�'}_${new Date().getTime()}.zip` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(a.href) + + ElMessage.success('鎵归噺涓嬭浇瀹屾垚锛�') + } catch (error) { + console.error('鎵归噺涓嬭浇澶辫触:', error) + ElMessage.error('鎵归噺涓嬭浇澶辫触锛岃閫愪釜涓嬭浇') + } +} + +// 娓呯┖鎵归噺鐢熸垚缁撴灉 +const clearBatchCodes = () => { + batchCodes.value = [] +} + +// 鏆撮湶鏂规硶缁欑埗缁勪欢 +defineExpose({ + generateCode, + downloadCode, + resetForm, + form +}) +</script> + +<style scoped> +.qr-code-generator { + padding: 20px; +} + +.qr-form { + background: #f8f9fa; + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; +} + +.code-display { + margin-top: 30px; +} + +.code-container { + display: flex; + justify-content: center; + align-items: flex-start; + gap: 40px; + margin: 20px 0; +} + +.code-image img { + border: 2px solid #e0e0e0; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +.code-info { + text-align: left; + min-width: 200px; +} + +.code-info p { + margin: 8px 0; + color: #666; +} + +.code-actions { + text-align: center; + margin: 20px 0; +} + +.code-actions .el-button { + margin: 0 10px; +} + +.batch-results { + margin-top: 30px; +} + +.batch-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 20px; + margin: 20px 0; +} + +.batch-item { + text-align: center; + padding: 15px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background: #fff; +} + +.batch-item img { + width: 100px; + height: 100px; + margin-bottom: 10px; +} + +.batch-content { + font-size: 12px; + color: #666; + margin: 10px 0; + word-break: break-all; +} + +.batch-actions { + text-align: center; + margin: 20px 0; +} + +.batch-actions .el-button { + margin: 0 10px; +} + +@media (max-width: 768px) { + .code-container { + flex-direction: column; + align-items: center; + } + + .batch-grid { + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + } +} +</style> -- Gitblit v1.9.3