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

相关推荐
学不会•11 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS1 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年5 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder5 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript