|
@@ -3,11 +3,10 @@ import { computed, ref } from 'vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
import { ElMessage } from 'element-plus'
|
|
|
import { verifyOFDSignature, verifyPDFSignature } from '@/api/sysApplication/signature'
|
|
import { verifyOFDSignature, verifyPDFSignature } from '@/api/sysApplication/signature'
|
|
|
|
|
|
|
|
-const fileInputRef = ref(null)
|
|
|
|
|
|
|
+const uploadRef = ref(null)
|
|
|
const currentFile = ref(null)
|
|
const currentFile = ref(null)
|
|
|
const verificationResult = ref(null)
|
|
const verificationResult = ref(null)
|
|
|
const verifying = ref(false)
|
|
const verifying = ref(false)
|
|
|
-const isDragOver = ref(false)
|
|
|
|
|
|
|
|
|
|
const fileExtension = computed(() => {
|
|
const fileExtension = computed(() => {
|
|
|
const name = currentFile.value?.name || ''
|
|
const name = currentFile.value?.name || ''
|
|
@@ -28,20 +27,15 @@ const setFile = file => {
|
|
|
verificationResult.value = null
|
|
verificationResult.value = null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleFileChange = event => {
|
|
|
|
|
- const file = event.target.files?.[0]
|
|
|
|
|
- event.target.value = ''
|
|
|
|
|
- setFile(file)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const handleDrop = event => {
|
|
|
|
|
- isDragOver.value = false
|
|
|
|
|
- setFile(event.dataTransfer.files?.[0])
|
|
|
|
|
|
|
+const handleFileChange = uploadFile => {
|
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
|
|
+ setFile(uploadFile.raw)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const clearFile = () => {
|
|
const clearFile = () => {
|
|
|
currentFile.value = null
|
|
currentFile.value = null
|
|
|
verificationResult.value = null
|
|
verificationResult.value = null
|
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const formatFileSize = bytes => {
|
|
const formatFileSize = bytes => {
|
|
@@ -82,586 +76,150 @@ const verifySignature = async() => {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="verify-page">
|
|
|
|
|
- <header class="page-header">
|
|
|
|
|
|
|
+ <el-card>
|
|
|
|
|
+ <template #header>
|
|
|
<div>
|
|
<div>
|
|
|
- <h1>验证电子签章</h1>
|
|
|
|
|
- <p>上传已签章文件,检查签名完整性及签署信息。</p>
|
|
|
|
|
|
|
+ <div>验证电子签章</div>
|
|
|
|
|
+ <el-text type="info">上传已签章文件,检查签名完整性及签署信息。</el-text>
|
|
|
</div>
|
|
</div>
|
|
|
- </header>
|
|
|
|
|
-
|
|
|
|
|
- <section class="content-card">
|
|
|
|
|
- <div class="section-heading">
|
|
|
|
|
- <div class="step-number">01</div>
|
|
|
|
|
- <div>
|
|
|
|
|
- <h2>选择待验证文件</h2>
|
|
|
|
|
- <p>支持 PDF 和 OFD 格式,系统将根据文件后缀自动选择验签方式</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <input
|
|
|
|
|
- ref="fileInputRef"
|
|
|
|
|
- class="file-input"
|
|
|
|
|
- type="file"
|
|
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <el-row justify="center">
|
|
|
|
|
+ <el-col
|
|
|
|
|
+ :xs="24"
|
|
|
|
|
+ :sm="22"
|
|
|
|
|
+ :md="20"
|
|
|
|
|
+ :lg="18"
|
|
|
|
|
+ :xl="16"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-card shadow="never">
|
|
|
|
|
+ <template #header>选择待验证文件</template>
|
|
|
|
|
+ <el-text type="info">支持 PDF 和 OFD 格式,系统将根据文件后缀自动选择验签方式。</el-text>
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ ref="uploadRef"
|
|
|
|
|
+ drag
|
|
|
|
|
+ action="#"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :show-file-list="false"
|
|
|
|
|
+ :limit="1"
|
|
|
accept=".pdf,.ofd"
|
|
accept=".pdf,.ofd"
|
|
|
- @change="handleFileChange"
|
|
|
|
|
|
|
+ :disabled="verifying"
|
|
|
|
|
+ :on-change="handleFileChange"
|
|
|
>
|
|
>
|
|
|
- <div
|
|
|
|
|
- v-if="!currentFile"
|
|
|
|
|
- class="upload-zone"
|
|
|
|
|
- :class="{ 'is-drag-over': isDragOver }"
|
|
|
|
|
- @click="fileInputRef?.click()"
|
|
|
|
|
- @dragover.prevent="isDragOver = true"
|
|
|
|
|
- @dragleave.prevent="isDragOver = false"
|
|
|
|
|
- @drop.prevent="handleDrop"
|
|
|
|
|
|
|
+ <el-icon><UploadFilled /></el-icon>
|
|
|
|
|
+ <div class="el-upload__text">点击或拖拽 PDF / OFD 文件到此处</div>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ <el-descriptions
|
|
|
|
|
+ v-if="currentFile"
|
|
|
|
|
+ :column="2"
|
|
|
|
|
+ border
|
|
|
>
|
|
>
|
|
|
- <div class="upload-icon">
|
|
|
|
|
- <el-icon><UploadFilled /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="upload-title">点击选择文件,或拖拽文件到此处</div>
|
|
|
|
|
- <div class="upload-hint">支持 .pdf / .ofd 格式</div>
|
|
|
|
|
- <el-button type="primary" plain class="choose-button">选择文件</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div v-else class="file-card">
|
|
|
|
|
- <div class="file-type" :class="fileExtension">{{ fileFormat }}</div>
|
|
|
|
|
- <div class="file-meta">
|
|
|
|
|
- <div class="file-name" :title="currentFile.name">{{ currentFile.name }}</div>
|
|
|
|
|
- <div class="file-subtitle">{{ formatFileSize(currentFile.size) }} · {{ fileFormat }} 文件</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-tag type="success" effect="light">已就绪</el-tag>
|
|
|
|
|
|
|
+ <el-descriptions-item label="文件名">{{ currentFile.name }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="文件大小">{{ formatFileSize(currentFile.size) }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="文件格式">{{ fileFormat }}</el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+ <el-space>
|
|
|
<el-button
|
|
<el-button
|
|
|
- class="remove-button"
|
|
|
|
|
- text
|
|
|
|
|
|
|
+ v-if="currentFile"
|
|
|
type="danger"
|
|
type="danger"
|
|
|
|
|
+ plain
|
|
|
:disabled="verifying"
|
|
:disabled="verifying"
|
|
|
@click="clearFile"
|
|
@click="clearFile"
|
|
|
- >移除</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="action-row">
|
|
|
|
|
- <span class="action-hint">
|
|
|
|
|
- <el-icon><InfoFilled /></el-icon>
|
|
|
|
|
- 验签结果将展示签名有效性、签章数量及签署人信息
|
|
|
|
|
- </span>
|
|
|
|
|
|
|
+ >
|
|
|
|
|
+ 移除文件
|
|
|
|
|
+ </el-button>
|
|
|
<el-button
|
|
<el-button
|
|
|
type="primary"
|
|
type="primary"
|
|
|
:loading="verifying"
|
|
:loading="verifying"
|
|
|
:disabled="!currentFile || verifying"
|
|
:disabled="!currentFile || verifying"
|
|
|
@click="verifySignature"
|
|
@click="verifySignature"
|
|
|
>
|
|
>
|
|
|
- <el-icon v-if="!verifying"><CircleCheck /></el-icon>
|
|
|
|
|
{{ verifying ? '正在验证' : '开始验证' }}
|
|
{{ verifying ? '正在验证' : '开始验证' }}
|
|
|
</el-button>
|
|
</el-button>
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
-
|
|
|
|
|
- <section v-if="verificationResult" class="result-section">
|
|
|
|
|
- <div class="result-banner" :class="isValid ? 'is-valid' : 'is-invalid'">
|
|
|
|
|
- <div class="result-icon">
|
|
|
|
|
- <el-icon><CircleCheckFilled v-if="isValid" /><CircleCloseFilled v-else /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="result-summary">
|
|
|
|
|
- <h2>{{ isValid ? '签名验证通过' : '签名验证未通过' }}</h2>
|
|
|
|
|
- <p>{{ verificationResult.message || '验签完成' }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="result-count">
|
|
|
|
|
- <strong>{{ verificationResult.signatureCount ?? signatures.length }}</strong>
|
|
|
|
|
- <span>个签名</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="result-metrics">
|
|
|
|
|
- <div class="metric-card">
|
|
|
|
|
- <span class="metric-label">文件格式</span>
|
|
|
|
|
- <strong>{{ fileFormat }}</strong>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="metric-card">
|
|
|
|
|
- <span class="metric-label">签名数量</span>
|
|
|
|
|
- <strong>{{ verificationResult.signatureCount ?? signatures.length }}</strong>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="metric-card">
|
|
|
|
|
- <span class="metric-label">信任链检查</span>
|
|
|
|
|
- <el-tag :type="verificationResult.trustChecked ? 'success' : 'info'" effect="light">
|
|
|
|
|
|
|
+ </el-space>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <el-space
|
|
|
|
|
+ v-if="verificationResult"
|
|
|
|
|
+ direction="vertical"
|
|
|
|
|
+ fill
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-alert
|
|
|
|
|
+ :title="isValid ? '签名验证通过' : '签名验证未通过'"
|
|
|
|
|
+ :description="verificationResult.message || '验签完成'"
|
|
|
|
|
+ :type="isValid ? 'success' : 'error'"
|
|
|
|
|
+ show-icon
|
|
|
|
|
+ :closable="false"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <el-descriptions
|
|
|
|
|
+ title="验证结果"
|
|
|
|
|
+ :column="3"
|
|
|
|
|
+ border
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-descriptions-item label="文件格式">{{ fileFormat }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="签名数量">
|
|
|
|
|
+ {{ verificationResult.signatureCount ?? signatures.length }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="信任链检查">
|
|
|
|
|
+ <el-tag :type="verificationResult.trustChecked ? 'success' : 'info'">
|
|
|
{{ verificationResult.trustChecked ? '已检查' : '未检查' }}
|
|
{{ verificationResult.trustChecked ? '已检查' : '未检查' }}
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div class="signatures-card">
|
|
|
|
|
- <div class="signatures-heading">
|
|
|
|
|
- <div>
|
|
|
|
|
- <h2>签名详情</h2>
|
|
|
|
|
- <p>签名完整性与签署者信息</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-tag effect="plain" type="info">{{ signatures.length }} 项</el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+
|
|
|
|
|
+ <el-card shadow="never">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <el-space>
|
|
|
|
|
+ <span>签名详情</span>
|
|
|
|
|
+ <el-tag effect="plain" type="info">{{ signatures.length }} 项</el-tag>
|
|
|
|
|
+ </el-space>
|
|
|
|
|
+ </template>
|
|
|
<el-empty
|
|
<el-empty
|
|
|
v-if="!signatures.length"
|
|
v-if="!signatures.length"
|
|
|
- :image-size="72"
|
|
|
|
|
description="文件中没有签名信息"
|
|
description="文件中没有签名信息"
|
|
|
/>
|
|
/>
|
|
|
- <div v-else class="signature-list">
|
|
|
|
|
- <article
|
|
|
|
|
|
|
+ <el-space
|
|
|
|
|
+ v-else
|
|
|
|
|
+ direction="vertical"
|
|
|
|
|
+ fill
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-card
|
|
|
v-for="(signature, index) in signatures"
|
|
v-for="(signature, index) in signatures"
|
|
|
:key="`${signature.name || 'signature'}-${index}`"
|
|
:key="`${signature.name || 'signature'}-${index}`"
|
|
|
- class="signature-item"
|
|
|
|
|
|
|
+ shadow="never"
|
|
|
>
|
|
>
|
|
|
- <div class="signature-item-heading">
|
|
|
|
|
- <div class="signature-number">{{ String(index + 1).padStart(2, '0') }}</div>
|
|
|
|
|
- <div class="signature-name">
|
|
|
|
|
- <strong>{{ signature.name || `签名 ${index + 1}` }}</strong>
|
|
|
|
|
- <span>{{ formatSigningTime(signature.signingTime) }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-tag :type="signature.valid ? 'success' : 'danger'" effect="light">
|
|
|
|
|
- {{ signature.valid ? '有效' : '无效' }}
|
|
|
|
|
- </el-tag>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="signature-details">
|
|
|
|
|
- <div class="detail-cell">
|
|
|
|
|
- <span>签署人</span>
|
|
|
|
|
- <strong>{{ signature.signerName || '未提供' }}</strong>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="detail-cell">
|
|
|
|
|
- <span>文档覆盖范围</span>
|
|
|
|
|
- <strong>{{ signature.coversWholeDocument ? '覆盖整个文档' : '未覆盖整个文档' }}</strong>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </article>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <el-space>
|
|
|
|
|
+ <span>{{ signature.name || `签名 ${index + 1}` }}</span>
|
|
|
|
|
+ <el-tag :type="signature.valid ? 'success' : 'danger'">
|
|
|
|
|
+ {{ signature.valid ? '有效' : '无效' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-space>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
|
|
+ <el-descriptions-item label="签署时间">
|
|
|
|
|
+ {{ formatSigningTime(signature.signingTime) }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="签署人">
|
|
|
|
|
+ {{ signature.signerName || '未提供' }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="文档覆盖范围">
|
|
|
|
|
+ {{ signature.coversWholeDocument ? '覆盖整个文档' : '未覆盖整个文档' }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item
|
|
|
|
|
+ v-if="signature.message"
|
|
|
|
|
+ label="验证信息"
|
|
|
|
|
+ :span="2"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ signature.message }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-space>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-space>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-card>
|
|
|
</template>
|
|
</template>
|
|
|
-
|
|
|
|
|
-<style scoped lang="scss">
|
|
|
|
|
-.verify-page {
|
|
|
|
|
- max-width: 1080px;
|
|
|
|
|
- margin: 0 auto;
|
|
|
|
|
- padding: 28px 24px 44px;
|
|
|
|
|
- color: #202b3c;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.page-header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- margin-bottom: 24px;
|
|
|
|
|
-
|
|
|
|
|
- h1 {
|
|
|
|
|
- margin: 5px 0 6px;
|
|
|
|
|
- font-size: 26px;
|
|
|
|
|
- font-weight: 650;
|
|
|
|
|
- letter-spacing: -0.4px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- p {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- color: #8792a2;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.eyebrow {
|
|
|
|
|
- color: #4779dc;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
- letter-spacing: 1.5px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.secure-badge {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 7px;
|
|
|
|
|
- padding: 8px 12px;
|
|
|
|
|
- border: 1px solid #dce9df;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- background: #f5fbf6;
|
|
|
|
|
- color: #4b9664;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.content-card,
|
|
|
|
|
-.signatures-card {
|
|
|
|
|
- padding: 22px 24px;
|
|
|
|
|
- border: 1px solid #e8edf3;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- box-shadow: 0 3px 12px rgb(25 50 85 / 3%);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.section-heading,
|
|
|
|
|
-.signatures-heading {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 13px;
|
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
|
-
|
|
|
|
|
- h2 {
|
|
|
|
|
- margin: 0 0 5px;
|
|
|
|
|
- color: #263449;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- p {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- color: #98a2b1;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.step-number {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- width: 34px;
|
|
|
|
|
- height: 34px;
|
|
|
|
|
- flex: 0 0 34px;
|
|
|
|
|
- place-items: center;
|
|
|
|
|
- border-radius: 9px;
|
|
|
|
|
- background: #edf3ff;
|
|
|
|
|
- color: #4779dc;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-input {
|
|
|
|
|
- display: none;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.upload-zone {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-height: 190px;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- border: 1px dashed #c9d5e4;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: #fbfcfe;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- transition: all 0.2s ease;
|
|
|
|
|
-
|
|
|
|
|
- &:hover,
|
|
|
|
|
- &.is-drag-over {
|
|
|
|
|
- border-color: #5788e5;
|
|
|
|
|
- background: #f5f8ff;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.upload-icon {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- width: 42px;
|
|
|
|
|
- height: 42px;
|
|
|
|
|
- place-items: center;
|
|
|
|
|
- margin-bottom: 10px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: #edf3ff;
|
|
|
|
|
- color: #5684dc;
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.upload-title {
|
|
|
|
|
- color: #46556b;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.upload-hint {
|
|
|
|
|
- margin: 6px 0 12px;
|
|
|
|
|
- color: #a0a9b7;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.choose-button {
|
|
|
|
|
- height: 30px;
|
|
|
|
|
- padding: 0 14px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-card {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 13px;
|
|
|
|
|
- min-height: 76px;
|
|
|
|
|
- padding: 13px 15px;
|
|
|
|
|
- border: 1px solid #e7edf5;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: #fbfcfe;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-type {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- width: 42px;
|
|
|
|
|
- height: 48px;
|
|
|
|
|
- flex: 0 0 42px;
|
|
|
|
|
- place-items: center;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- background: #fff0ee;
|
|
|
|
|
- color: #db675c;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
-
|
|
|
|
|
- &.ofd {
|
|
|
|
|
- background: #eff3ff;
|
|
|
|
|
- color: #5877c5;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-meta {
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-name {
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- color: #344258;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.file-subtitle {
|
|
|
|
|
- margin-top: 6px;
|
|
|
|
|
- color: #97a1b0;
|
|
|
|
|
- font-size: 11px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.remove-button {
|
|
|
|
|
- margin-left: 4px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.action-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- gap: 16px;
|
|
|
|
|
- margin-top: 18px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.action-hint {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6px;
|
|
|
|
|
- color: #98a2b1;
|
|
|
|
|
- font-size: 11px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-section {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- gap: 16px;
|
|
|
|
|
- margin-top: 18px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-banner {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 14px;
|
|
|
|
|
- padding: 18px 20px;
|
|
|
|
|
- border: 1px solid;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
-
|
|
|
|
|
- &.is-valid {
|
|
|
|
|
- border-color: #d9ebde;
|
|
|
|
|
- background: #f5fbf6;
|
|
|
|
|
- color: #3b8b59;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.is-invalid {
|
|
|
|
|
- border-color: #f2dbd8;
|
|
|
|
|
- background: #fff8f7;
|
|
|
|
|
- color: #c85e54;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-icon {
|
|
|
|
|
- font-size: 28px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-summary {
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-
|
|
|
|
|
- h2 {
|
|
|
|
|
- margin: 0 0 5px;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- p {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- opacity: 0.82;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-count {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding-left: 18px;
|
|
|
|
|
- border-left: 1px solid currentColor;
|
|
|
|
|
- opacity: 0.85;
|
|
|
|
|
-
|
|
|
|
|
- strong {
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- line-height: 1.2;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- span {
|
|
|
|
|
- margin-top: 3px;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.result-metrics {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.metric-card {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-height: 80px;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: flex-start;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- gap: 9px;
|
|
|
|
|
- padding: 14px 17px;
|
|
|
|
|
- border: 1px solid #e8edf3;
|
|
|
|
|
- border-radius: 9px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
-
|
|
|
|
|
- strong {
|
|
|
|
|
- color: #35445a;
|
|
|
|
|
- font-size: 15px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .el-tag {
|
|
|
|
|
- height: 22px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.metric-label {
|
|
|
|
|
- color: #98a2b1;
|
|
|
|
|
- font-size: 11px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signatures-card {
|
|
|
|
|
- padding-bottom: 10px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signatures-heading {
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- margin-bottom: 17px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-list {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-item {
|
|
|
|
|
- padding: 15px 16px;
|
|
|
|
|
- border: 1px solid #edf0f4;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- background: #fcfdff;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-item-heading {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 11px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-number {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- width: 30px;
|
|
|
|
|
- height: 30px;
|
|
|
|
|
- flex: 0 0 30px;
|
|
|
|
|
- place-items: center;
|
|
|
|
|
- border-radius: 7px;
|
|
|
|
|
- background: #edf3ff;
|
|
|
|
|
- color: #4779dc;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-name {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 4px;
|
|
|
|
|
-
|
|
|
|
|
- strong {
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- color: #38465b;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- text-overflow: ellipsis;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- span {
|
|
|
|
|
- color: #9aa4b2;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.signature-details {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: minmax(0, 2fr) minmax(130px, 1fr);
|
|
|
|
|
- gap: 15px;
|
|
|
|
|
- margin: 15px 0 0 41px;
|
|
|
|
|
- padding-top: 13px;
|
|
|
|
|
- border-top: 1px solid #edf0f4;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-cell {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 6px;
|
|
|
|
|
-
|
|
|
|
|
- span {
|
|
|
|
|
- color: #9aa4b2;
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- strong {
|
|
|
|
|
- overflow-wrap: anywhere;
|
|
|
|
|
- color: #58667a;
|
|
|
|
|
- font-size: 11px;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-@media (max-width: 680px) {
|
|
|
|
|
- .verify-page {
|
|
|
|
|
- padding: 20px 14px 32px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .content-card,
|
|
|
|
|
- .signatures-card {
|
|
|
|
|
- padding: 18px 16px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .secure-badge,
|
|
|
|
|
- .action-hint {
|
|
|
|
|
- display: none;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .result-metrics {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .metric-card {
|
|
|
|
|
- min-height: 66px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .signature-details {
|
|
|
|
|
- grid-template-columns: 1fr;
|
|
|
|
|
- margin-left: 0;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|