Files
tyapi-frontend/src/pages/certification/components/ContractPreview.vue
2025-11-24 16:06:44 +08:00

281 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-card class="step-card">
<template #header>
<div class="card-header">
<div class="header-icon">
<el-icon class="text-blue-600">
<DocumentTextIcon />
</el-icon>
</div>
<div class="header-content">
<h2 class="header-title">合同预览</h2>
<p class="header-subtitle">请仔细阅读合同内容确认无误后再进行签署</p>
</div>
</div>
</template>
<div class="contract-preview-section">
<div v-if="contractUrl" class="pdf-viewer-wrapper">
<vue-pdf-embed
ref="pdfRef"
:source="contractUrl"
:page="page"
:scale="pdfScale"
@loaded="handleLoaded"
class="pdf-embed"
/>
<div class="pdf-controls">
<el-button size="large" :disabled="page <= 1" @click="page--"> 上一页 </el-button>
<span class="pdf-page-info"> {{ page }} / {{ pageCount }} </span>
<el-button size="large" :disabled="page >= pageCount" @click="page++"> 下一页 </el-button>
<el-divider direction="vertical" />
<el-button size="large" @click="fullscreen = true"> 全屏预览 </el-button>
</div>
</div>
<el-button type="primary" @click="emit('start-sign')" :loading="props.loading">
<el-icon class="mr-2"><DocumentTextIcon /></el-icon>
开始签署
</el-button>
</div>
<!-- 极简全屏预览弹窗无顶部栏 -->
<el-dialog
v-model="fullscreen"
fullscreen
append-to-body
:close-on-click-modal="true"
class="fullscreen-dialog"
:show-close="false"
lock-scroll
:header="false"
>
<div class="fullscreen-pdf-simple">
<vue-pdf-embed
:source="contractUrl"
:page="page"
:scale="fullscreenScale"
class="fullscreen-pdf-embed"
/>
<div class="fullscreen-pdf-controls">
<el-button size="large" :disabled="page <= 1" @click="page--">上一页</el-button>
<span class="pdf-page-info"> {{ page }} / {{ pageCount }} </span>
<el-button size="large" :disabled="page >= pageCount" @click="page++">下一页</el-button>
<el-button type="primary" size="large" @click="fullscreen = false"> 关闭预览 </el-button>
</div>
</div>
</el-dialog>
</el-card>
</template>
<script setup>
import { DocumentTextIcon } from '@heroicons/vue/24/outline'
import VuePdfEmbed from 'vue-pdf-embed'
const props = defineProps({
contractUrl: String,
loading: Boolean
})
const emit = defineEmits(['start-sign'])
const pdfRef = ref(null)
const page = ref(1)
const pageCount = ref(0)
const pdfScale = ref(1.3)
const fullscreen = ref(false)
const fullscreenScale = ref(1.7)
function handleLoaded(pdf) {
pageCount.value = pdf.numPages
}
</script>
<style scoped>
.step-card {
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
border: none;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.card-header {
display: flex;
align-items: center;
gap: 16px;
padding: 8px 0;
}
.header-icon {
width: 48px;
height: 48px;
border-radius: 12px;
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
}
.header-content {
flex: 1;
}
.header-title {
font-size: 24px;
font-weight: 700;
color: #1e293b;
margin: 0 0 4px 0;
}
.header-subtitle {
font-size: 14px;
color: #64748b;
margin: 0;
font-weight: 500;
}
.contract-preview-section {
padding: 32px 0;
text-align: center;
}
.pdf-viewer-wrapper {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 16px;
}
.pdf-embed {
width: 100%;
max-width: 600px;
min-height: 600px;
max-height: 600px;
border: 1px solid #e5e7eb;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
background: #fafbfc;
overflow-y: auto;
overflow-x: auto;
scrollbar-width: thin;
scrollbar-color: #cbd5e1 #f1f5f9;
}
.pdf-embed::-webkit-scrollbar {
width: 8px;
}
.pdf-embed::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.pdf-embed::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
.pdf-controls {
margin: 24px 0 0 0;
display: flex;
align-items: center;
gap: 24px;
justify-content: center;
font-size: 16px;
}
.pdf-btn {
border-radius: 12px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
color: #fff;
font-weight: 600;
border: none;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
transition:
background 0.2s,
color 0.2s,
box-shadow 0.2s;
min-width: 110px;
height: 44px;
font-size: 16px;
}
.pdf-btn:disabled {
background: #e2e8f0;
color: #94a3b8;
box-shadow: none;
}
.pdf-btn:hover:not(:disabled) {
background: #2563eb;
color: #fff;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.18);
}
.pdf-page-info {
color: #64748b;
font-size: 16px;
font-weight: 500;
}
.contract-sign-btn {
min-width: 220px;
height: 52px;
font-size: 18px;
font-weight: 700;
border-radius: 14px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
border: none;
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.18);
transition:
background 0.2s,
color 0.2s,
box-shadow 0.2s;
margin-top: 32px;
}
.contract-sign-btn:hover:not(:disabled) {
background: #2563eb;
color: #fff;
box-shadow: 0 12px 32px rgba(59, 130, 246, 0.22);
}
.contract-sign-btn:disabled {
background: #e2e8f0;
color: #94a3b8;
box-shadow: none;
}
.fullscreen-dialog >>> .el-dialog__body {
padding: 0;
}
.fullscreen-pdf-simple {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #fafbfc;
overflow: auto;
}
.fullscreen-pdf-embed {
width: 100vw;
max-width: 1000px;
height: calc(100vh - 116px);
border: none;
border-radius: 0;
background: #fff;
overflow: auto;
box-shadow: none;
}
.fullscreen-pdf-controls {
width: 100%;
height: 68px;
background-color: #e2e8f0;
display: flex;
align-items: center;
gap: 24px;
justify-content: center;
font-size: 18px;
}
.fullscreen-close-btn {
min-width: 120px;
height: 44px;
font-size: 16px;
font-weight: 700;
border-radius: 12px;
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
border: none;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.18);
margin-left: 32px;
transition:
background 0.2s,
color 0.2s,
box-shadow 0.2s;
}
.fullscreen-close-btn:hover {
background: #2563eb;
color: #fff;
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.22);
}
</style>