vite+vue3+typescript+elementPlus前端实现电子证书查询系统

实现背景:之前电子证书的实现是后端实现的,主要采用GD库技术,在底图上添加文字水印和图片水印实现的。这里采用前端技术实现电子证书的呈现以及点击证书下载,优点是:后端给前端传递的是一组数据,不需要传证书的图片,交互所需数据流大大减少了。后端不需要生成证书,就不需要额外开辟存储证书的空间,当用户量很大时,节省开支。

前端技术栈:vite+vue3+typescript+elementPlus

证书查询首页实现,代码如下:

html 复制代码
<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-form
          ref="ruleFormRef"
          :model="ruleForm"
          :rules="rules"
          label-width="auto"
          class="demo-ruleForm"
          :size="formSize"
          :label-position="labelPosition"
          status-icon
        >
          <el-form-item label="姓&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;名" 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: string
  idNo: string
  certificateNo: 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 form
  await formEl.validate()

  // If validation passes, call createItem with the form data
  const { 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>

证书查询首页实现,效果呈现如下:

电子证书查询结果实现,代码如下:

html 复制代码
<template>
  <div class="main">
    <div
      class="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) return

  try {
    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、后端负责数据,前端负责呈现,实现更加灵活

相关推荐
孟无岐5 分钟前
【Laya】Browser 使用说明
typescript·游戏引擎·游戏程序·laya
Nan_Shu_61417 分钟前
学习: Threejs (2)
前端·javascript·学习
G_G#25 分钟前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界40 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路1 小时前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug1 小时前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121381 小时前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架