wuzhen updat

This commit is contained in:
wuzhen committed 2025-07-30 23:24:43 +08:00
1 parent 3e4642c624
commit e4e4679da9
173 files changed
+37924 -325

No files matched your search

+461
View File
@@ -0,0 +1,461 @@
<template>
<div class="poster-preview">
<!-- 海报画布容器 -->
<div
ref="posterRef"
class="poster-canvas"
:style="canvasStyle"
>
<!-- 企业信息叠加 -->
<div class="info-overlay">
<!-- 顶部企业标题 -->
<div class="company-header">
<h1 class="company-name">{{ posterData.enterpriseName }}</h1>
<div class="company-tagline">诚聘英才 · 共创未来</div>
</div>
<!-- 中间内容区域 - 根据海报类型显示不同内容 -->
<!-- 企业海报:展示企业文化和优势 -->
<div v-if="posterData.posterType === '1'" class="position-info">
<div class="position-title">加入我们 · 共创辉煌</div>
<div v-if="enterpriseDetail" class="position-details">
<br />
<br />
<br />
<br />
<br />
<br />
<div class="detail-item" v-if="enterpriseDetail.mainBusiness">
<span class="icon">🏢</span>
<span>{{ enterpriseDetail.mainBusiness }}</span>
</div>
<div class="detail-item" v-if="enterpriseDetail.enterpriseScale">
<span class="icon">👥</span>
<span>{{ getScaleLabel(enterpriseDetail.enterpriseScale) }}</span>
</div>
<div class="detail-item" v-if="enterpriseDetail.address">
<span class="icon">📍</span>
<span>{{ enterpriseDetail.address }}</span>
</div>
</div>
</div>
<!-- 岗位海报:展示具体岗位信息 -->
<div v-else-if="posterData.posterType === '2'" class="position-info">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div class="position-title">
{{ posterData.positionName || positionDetail?.positionName || '招聘岗位' }}
</div>
<div v-if="positionDetail && Object.keys(positionDetail).length > 0" class="position-details">
<div class="position-details-grid">
<div class="detail-item" v-if="positionDetail.workAddress">
<span class="icon">📍</span>
<span>{{ positionDetail.workAddress }}</span>
</div>
<div class="detail-item" v-if="positionDetail.salary">
<span class="icon">💰</span>
<span>{{ positionDetail.salary }}</span>
</div>
<div class="detail-item" v-if="positionDetail.workTime">
<span class="icon">⏰</span>
<span>{{ positionDetail.workTime }}</span>
</div>
<div class="detail-item" v-if="positionDetail.requiredSkills">
<span class="icon">🎯</span>
<span>{{ positionDetail.requiredSkills }}</span>
</div>
</div>
</div>
<!-- 如果没有岗位详情,显示提示 -->
<div v-else class="position-details">
<div class="detail-item">
<span class="icon">💼</span>
<span>岗位详情加载中或暂无数据</span>
</div>
</div>
</div>
<!-- 底部联系信息 - 根据海报类型显示不同内容 -->
<div class="company-footer">
<div v-if="enterpriseDetail" class="company-info">
<!-- 企业海报:重点显示联系方式 -->
<template v-if="posterData.posterType === '1'">
<div class="info-row" v-if="enterpriseDetail.contactPhone">
<span class="label">📞 联系电话:</span>
<span class="value">{{ enterpriseDetail.contactPhone }}</span>
</div>
<div class="info-row" v-if="enterpriseDetail.email">
<span class="label">📧 电子邮箱:</span>
<span class="value">{{ enterpriseDetail.email }}</span>
</div>
<div class="info-row" v-if="enterpriseDetail.address">
<span class="label">🏢 公司地址:</span>
<span class="value">{{ enterpriseDetail.address }}</span>
</div>
</template>
<!-- 岗位海报:显示企业背景信息 -->
<template v-else-if="posterData.posterType === '2'">
<div class="info-row" v-if="enterpriseDetail.mainBusiness">
<span class="label">主营业务:</span>
<span class="value">{{ enterpriseDetail.mainBusiness }}</span>
</div>
<div class="info-row" v-if="enterpriseDetail.enterpriseScale">
<span class="label">企业规模:</span>
<span class="value">{{ getScaleLabel(enterpriseDetail.enterpriseScale) }}</span>
</div>
<div class="info-row" v-if="enterpriseDetail.contactPhone">
<span class="label">联系电话:</span>
<span class="value">{{ enterpriseDetail.contactPhone }}</span>
</div>
<div class="info-row" v-if="enterpriseDetail.email">
<span class="label">电子邮箱:</span>
<span class="value">{{ enterpriseDetail.email }}</span>
</div>
</template>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<el-button type="primary" icon="Download" @click="exportToPDF" :loading="exporting">
导出PDF
</el-button>
<el-button icon="Picture" @click="exportToImage" :loading="exporting">
导出图片
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'
import { ElMessage } from 'element-plus'
// 定义接口
interface PosterData {
posterPath: string
enterpriseName: string
positionName?: string
posterType: string
}
interface EnterpriseDetail {
mainBusiness?: string
enterpriseScale?: string
contactPhone?: string
email?: string
address?: string
}
interface PositionDetail {
positionName?: string
workAddress?: string
salary?: string
workTime?: string
requiredSkills?: string
}
// Props定义
const props = defineProps<{
posterData: PosterData
enterpriseDetail?: EnterpriseDetail
positionDetail?: PositionDetail
}>()
// 响应式数据
const posterRef = ref<HTMLElement>()
const exporting = ref(false)
// 计算样式
const canvasStyle = computed(() => ({
backgroundImage: `url(${props.posterData.posterPath})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat'
}))
// 企业规模字典
const scaleDict = {
'1': '50人以下',
'2': '50-200人',
'3': '200-500人',
'4': '500-1000人',
'5': '1000人以上'
}
// 获取规模标签
const getScaleLabel = (value: string) => {
return scaleDict[value as keyof typeof scaleDict] || value
}
// 导出为PDF
const exportToPDF = async () => {
if (!posterRef.value) return
try {
exporting.value = true
const canvas = await html2canvas(posterRef.value, {
backgroundColor: null,
scale: 2,
useCORS: true,
allowTaint: true,
foreignObjectRendering: false
})
const imgData = canvas.toDataURL('image/png')
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4'
})
const imgWidth = 190
const imgHeight = (canvas.height * imgWidth) / canvas.width
pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight)
const fileName = `${props.posterData.enterpriseName}_招聘海报_${new Date().getTime()}.pdf`
pdf.save(fileName)
ElMessage.success('PDF导出成功')
} catch (error) {
console.error('PDF导出失败:', error)
ElMessage.error('PDF导出失败,请重试')
} finally {
exporting.value = false
}
}
// 导出为图片
const exportToImage = async () => {
if (!posterRef.value) return
try {
exporting.value = true
const canvas = await html2canvas(posterRef.value, {
backgroundColor: null,
scale: 2,
useCORS: true,
allowTaint: true
})
const link = document.createElement('a')
link.download = `${props.posterData.enterpriseName}_招聘海报_${new Date().getTime()}.png`
link.href = canvas.toDataURL('image/png')
link.click()
ElMessage.success('图片导出成功')
} catch (error) {
console.error('图片导出失败:', error)
ElMessage.error('图片导出失败,请重试')
} finally {
exporting.value = false
}
}
</script>
<style scoped lang="scss">
.poster-preview {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.poster-canvas {
position: relative;
width: 600px;
height: 800px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.info-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 40px;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0.3) 0%,
rgba(0, 0, 0, 0.1) 50%,
rgba(0, 0, 0, 0.4) 100%
);
}
.company-header {
text-align: center;
margin-bottom: 20px;
}
.company-name {
font-size: 48px;
font-weight: 700;
margin: 0 0 10px 0;
letter-spacing: 2px;
background: linear-gradient(135deg, #ffd700, #ffed4e);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.8));
}
.company-tagline {
font-size: 18px;
font-weight: 300;
letter-spacing: 4px;
opacity: 0.9;
}
.position-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.position-title {
font-size: 36px;
font-weight: 600;
margin-bottom: 30px;
background: linear-gradient(135deg, #ff6b6b, #ffd93d);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.8));
}
.position-details {
display: flex;
flex-direction: column;
gap: 12px;
font-size: 14px;
max-width: 500px;
margin: 0 auto;
}
.position-details-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
width: 100%;
}
.detail-item {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background: rgba(255, 255, 255, 0.1);
padding: 8px 15px;
border-radius: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
font-size: 14px;
min-height: 36px;
}
.icon {
font-size: 16px;
margin-right: 3px;
flex-shrink: 0;
}
.company-footer {
margin-top: 20px;
}
.company-info {
background: rgba(0, 0, 0, 0.6);
padding: 25px;
border-radius: 15px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.info-row {
display: flex;
margin-bottom: 12px;
font-size: 16px;
}
.info-row:last-child {
margin-bottom: 0;
}
.label {
font-weight: 600;
min-width: 80px;
color: #ffd700;
}
.value {
flex: 1;
opacity: 0.9;
}
.action-buttons {
display: flex;
gap: 15px;
margin-top: 20px;
}
@media (max-width: 768px) {
.poster-canvas {
width: 90vw;
height: calc(90vw * 4 / 3);
max-width: 450px;
max-height: 600px;
}
.info-overlay {
padding: 20px;
}
.company-name {
font-size: 32px;
}
.position-title {
font-size: 24px;
}
.detail-item {
font-size: 14px;
padding: 8px 15px;
}
}
</style>