实现背景:之前电子证书的实现是后端实现的,主要采用GD库技术,在底图上添加文字水印和图片水印实现的。这里采用前端技术实现电子证书的呈现以及点击证书下载,优点是:后端给前端传递的是一组数据,不需要传证书的图片,交互所需数据流大大减少了。后端不需要生成证书,就不需要额外开辟存储证书的空间,当用户量很大时,节省开支。
前端技术栈:vite+vue3+typescript+elementPlus
证书查询首页实现,代码如下:
<template><el-row class="header"><el-col :span="24"><el-text>电子证书查询系统</el-text></el-col></el-row><el-row class="main"><el-col :span="24"><el-card style="max-width: 680px" shadow="always"><template #header><div class="card-header"><span>证书查询系统</span></div></template><el-formref="ruleFormRef":model="ruleForm":rules="rules"label-width="auto"class="demo-ruleForm":size="formSize":label-position="labelPosition"status-icon><el-form-item label="姓 名" prop="name"><el-input v-model="ruleForm.name" placeholder="请输入姓名" /></el-form-item><el-form-item label="身份证号" prop="idNo"><el-input v-model="ruleForm.idNo" placeholder="请输入身份证号" /></el-form-item><el-form-item label="证书编号" prop="certificateNo"><el-input v-model="ruleForm.certificateNo" placeholder="请输入证书编号" /></el-form-item><el-form-item><el-button type="primary" @click="submitForm(ruleFormRef)"> 查询 </el-button></el-form-item></el-form></el-card></el-col></el-row>
</template><script lang="ts" setup name="CertificateIndex">
import { reactive, ref } from 'vue'
import type { ComponentSize, FormInstance, FormRules, FormProps } from 'element-plus'
import { ElMessage } from 'element-plus'
import { createItem } from '../services/crudService'
import { useRouter } from 'vue-router'const router = useRouter()interface RuleForm {name: stringidNo: stringcertificateNo: string
}const formSize = ref<ComponentSize>('large')
const labelPosition = ref<FormProps['labelPosition']>('left')
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({name: '',idNo: '',certificateNo: ''
})const rules = reactive<FormRules<RuleForm>>({name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],idNo: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],certificateNo: [{ required: true, message: '请输入证书编号', trigger: 'blur' }]
})const submitForm = async (formEl: FormInstance | undefined) => {if (!formEl) return// Validate the formawait formEl.validate()// If validation passes, call createItem with the form dataconst { data } = await createItem(ruleForm)if (!data.id) {ElMessage({message: '暂无此人相关证书!',type: 'warning'})return}router.push({ name: 'CertificateDetail', query: { data: JSON.stringify(data) } })
}
</script><style scoped style="scss">
.header {background-color: #1174c2;width: 100%;height: 50px;.el-col {text-align: center;vertical-align: center;padding: 0.5rem 0;.el-text {font-size: 1.5rem;color: #fff;}}
}.main {margin-top: 100px;.el-col {.el-card {margin: 0 auto;.card-header {text-align: center;vertical-align: center;font-size: 1.5rem;background-color: #1174c2;color: #fff;width: 100%;padding: 0.8rem 0;}.el-form {.el-form-item {margin: 2rem auto;}.el-button {font-size: 1.5rem;padding: 1.5rem 0;width: 100%;background-color: #1174c2;}}}}
}
</style>
证书查询首页实现,效果呈现如下:
电子证书查询结果实现,代码如下:
<template><div class="main"><divclass="card-header p-2 w-full bg-[#1174c2] text-[#fff] text-center text-xl fixed top-0 left-0 w-full z-50"><span>电子证书查询结果</span></div><el-card shadow="always" class="mt-20"><div class="content" ref="contentToCapture"><div class="logo w-28 h-10 mt-4"></div><div class="text-center mt-20 mb-6 text-lg dirBlod font-bold">内部审核员证书</div><div class="mb-4 main"><img:src="crossOriginImageSrc"alt="Cross-origin image"style="width: 88px; height: 118px"fit="cover"/><div class="text-base mt-6">{{ form.name }}</div></div><div class="id text-base mb-4 dirBlod text-center">ID: {{ form.idNo }}</div><div class="text text-base"><div class="mb-4 dirBlod text-center">兹证明其参加了 {{ form.course }}</div><div class="ml-4 dirBlod">内部审核员培训课程并经考核合格,特发此证。</div></div><div class="footer mt-20"><div class="text-xs"><div class="dirBlod leading-6">发证日期 {{ form.authorizationDate }}</div><div class="dirBlod leading-6">编号 {{ form.certificateNo }}</div><div class="dirBlod leading-6">查询 {{ form.url }}</div></div><div class="text-base dirBlod gz-bg"><div class="gz-bg-img"></div>xx教育培训有限公司</div></div></div><div @click="captureAndDownload" class="text-center mt-5 text-blue-600 cursor-pointer">证书下载</div></el-card></div>
</template><script lang="ts" setup name="CertificateDetail">
import { ref, reactive, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import html2canvas from 'html2canvas'
import { saveAs } from 'file-saver'
// 后端基本路径
const url = '/dev-api'
const route = useRoute()
const form = reactive(JSON.parse(route.query.data as string))
const crossOriginImageSrc = ref(url + form.path) // 示例跨域图片
const contentToCapture = ref<HTMLDivElement>()async function captureAndDownload() {if (!contentToCapture.value) returntry {const canvas = await html2canvas(contentToCapture.value, {useCORS: true // 允许跨域请求})const imgDataUrl = canvas.toDataURL('image/png')const uniqueBlobUrl = URL.createObjectURL(new Blob([await fetch(imgDataUrl).then((res) => res.blob())], { type: 'image/png' }))saveAs(uniqueBlobUrl, 'screenshot.png')} catch (error) {console.error('Error capturing screenshot:', error)}
}
</script>
<style scoped lang="scss">
.main {display: flex;flex-direction: column;justify-content: center;align-items: center;
}
.card-header {height: 50px;
}
.el-card {margin-top: 60px;margin-bottom: 60px;width: 620px;
}.content {position: relative;background: url(@/assets/images/bg.png) no-repeat;background-size: 100% 100%;height: 880px;padding: 106px;font-family: 'dirBlod', sans-serif;.logo {background: url(@/assets/images/logo.png) no-repeat;background-size: 100% 100%;}
}.footer {display: flex;justify-content: space-between;align-items: center;
}
.dirBlod {font-family: 'dirBlod', sans-serif;
}
.gz-bg {position: relative;.gz-bg-img {position: absolute;top: -280%;left: 20%;width: 120px;height: 120px;background: url(@/assets/images/seal.png) no-repeat;background-size: 100% 100%;}
}
</style>
电子证书查询结果实现,效果呈现如下:
小结:
1、节省了存储电子证书图片的空间;
2、后端负责数据,前端负责呈现,实现更加灵活