|
@@ -0,0 +1,431 @@
|
|
|
+<template>
|
|
|
+ <div class="email-detail">
|
|
|
+ <!-- 顶部导航栏 -->
|
|
|
+ <van-nav-bar
|
|
|
+ class="email-detail-bar"
|
|
|
+ left-arrow
|
|
|
+ @click-left="goBack"
|
|
|
+ :title="email.subject || '无主题'"
|
|
|
+ :border="false"
|
|
|
+ >
|
|
|
+ <template #right>
|
|
|
+ <van-icon name="delete-o" size="18" @click="handleDelete" />
|
|
|
+ </template>
|
|
|
+ </van-nav-bar>
|
|
|
+
|
|
|
+ <!-- 邮件信息头部 -->
|
|
|
+ <div class="email-header">
|
|
|
+ <div class="subject">{{ email.subject || '无主题' }}</div>
|
|
|
+
|
|
|
+ <div class="sender-info">
|
|
|
+ <van-image round width="40" height="40" :src="senderAvatar" fit="cover" />
|
|
|
+ <div class="sender-details">
|
|
|
+ <div class="sender-name">{{ email.sender || 'unknown@example.com' }}</div>
|
|
|
+ <div class="sender-to">发送至:{{ email.to || 'me' }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="email-time">{{ formatDate(email.time) }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 邮件正文 -->
|
|
|
+ <div class="email-content">
|
|
|
+ <div v-html="email.content"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 附件区域 -->
|
|
|
+ <div v-if="email.attachments && email.attachments.length > 0" class="attachments-section">
|
|
|
+ <div class="section-title">附件 ({{ email.attachments.length }})</div>
|
|
|
+ <div class="attachments-list">
|
|
|
+ <div
|
|
|
+ v-for="(attachment, index) in email.attachments"
|
|
|
+ :key="index"
|
|
|
+ class="attachment-item"
|
|
|
+ @click="previewAttachment(attachment)"
|
|
|
+ >
|
|
|
+ <div class="attachment-icon">
|
|
|
+ <van-icon :name="getFileIcon(attachment.type)" size="24" />
|
|
|
+ </div>
|
|
|
+ <div class="attachment-info">
|
|
|
+ <div class="attachment-name">{{ attachment.name }}</div>
|
|
|
+ <div class="attachment-size">{{ formatFileSize(attachment.size) }}</div>
|
|
|
+ </div>
|
|
|
+ <van-icon name="down" class="download-icon" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+ import { ref } from 'vue'
|
|
|
+ import { getCurrentInstance } from 'vue'
|
|
|
+ import { formatDate } from '@/utils/utils'
|
|
|
+ const emit = defineEmits(['toDetail'])
|
|
|
+ const { proxy } = getCurrentInstance()
|
|
|
+ defineProps({
|
|
|
+ data: {
|
|
|
+ type: Object,
|
|
|
+ default: () => {}
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 模拟邮件数据
|
|
|
+ const email = ref({
|
|
|
+ id: '123456',
|
|
|
+ subject: '项目进度报告 - 2023年第二季度',
|
|
|
+ sender: 'project-manager@company.com',
|
|
|
+ senderName: '项目经理',
|
|
|
+ to: 'yukai@gat.jl',
|
|
|
+ time: '2025-03-16 12:23:45',
|
|
|
+ content: `<p>尊敬的于凯:</p>
|
|
|
+ <p>附件是本季度的项目进度报告,请查收。</p>
|
|
|
+ <p>报告中包含了所有项目的当前状态、完成百分比以及遇到的问题。</p>
|
|
|
+ <p>如有任何疑问,请随时与我联系。</p>
|
|
|
+ <p>此致,</p>
|
|
|
+ <p>项目管理部</p>`,
|
|
|
+ starred: false,
|
|
|
+ attachments: [
|
|
|
+ { name: '2023Q2项目进度报告.pdf', type: 'pdf', size: 2456000 },
|
|
|
+ { name: '财务数据.xlsx', type: 'excel', size: 1200000 },
|
|
|
+ { name: '会议记录.docx', type: 'word', size: 350000 },
|
|
|
+ { name: '演示文稿.pptx', type: 'ppt', size: 4500000 },
|
|
|
+ { name: '项目照片.jpg', type: 'image', size: 1800000 },
|
|
|
+ { name: '演示视频.mp4', type: 'video', size: 15000000 }
|
|
|
+ ]
|
|
|
+ })
|
|
|
+
|
|
|
+ const senderAvatar = ref('https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg')
|
|
|
+
|
|
|
+ // 返回上一页
|
|
|
+ const goBack = () => {
|
|
|
+ emit('toDetail', false)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式化文件大小
|
|
|
+ const formatFileSize = bytes => {
|
|
|
+ if (bytes === 0) return '0 B'
|
|
|
+ const k = 1024
|
|
|
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据文件类型获取图标
|
|
|
+ const getFileIcon = type => {
|
|
|
+ const iconMap = {
|
|
|
+ pdf: 'description',
|
|
|
+ word: 'description',
|
|
|
+ excel: 'description',
|
|
|
+ ppt: 'description',
|
|
|
+ image: 'photo-o',
|
|
|
+ video: 'video-o',
|
|
|
+ audio: 'music-o',
|
|
|
+ zip: 'description',
|
|
|
+ text: 'description'
|
|
|
+ }
|
|
|
+ return iconMap[type] || 'description'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理删除
|
|
|
+ const handleDelete = () => {
|
|
|
+ proxy.$showToast('删除邮件')
|
|
|
+ emit('toDetail', false)
|
|
|
+ }
|
|
|
+
|
|
|
+ const getBinaryData = id => {
|
|
|
+ return id
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览附件
|
|
|
+ const previewAttachment = attachment => {
|
|
|
+ // 创建一个Blob对象来处理二进制数据
|
|
|
+ // 注意:这里假设你已经有了二进制数据,实际使用时需要从API获取
|
|
|
+ const binaryData = getBinaryData(attachment.id) // 这个函数需要你实现,用于获取二进制数据
|
|
|
+
|
|
|
+ // 根据文件类型选择不同的预览方式
|
|
|
+ switch (attachment.type) {
|
|
|
+ case 'image':
|
|
|
+ previewImage(attachment, binaryData)
|
|
|
+ break
|
|
|
+ case 'pdf':
|
|
|
+ previewPDF(attachment, binaryData)
|
|
|
+ break
|
|
|
+ case 'word':
|
|
|
+ case 'excel':
|
|
|
+ case 'ppt':
|
|
|
+ previewOffice(attachment, binaryData)
|
|
|
+ break
|
|
|
+ case 'text':
|
|
|
+ previewText(attachment, binaryData)
|
|
|
+ break
|
|
|
+ case 'video':
|
|
|
+ previewVideo(attachment, binaryData)
|
|
|
+ break
|
|
|
+ case 'audio':
|
|
|
+ previewAudio(attachment, binaryData)
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ // 对于不支持预览的文件类型,提供下载选项
|
|
|
+ downloadFile(attachment, binaryData)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览图片
|
|
|
+ const previewImage = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData], { type: 'image/jpeg' }) // 根据实际类型调整
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ // 使用vant的ImagePreview组件
|
|
|
+ proxy.$showImagePreview([url])
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览PDF
|
|
|
+ const previewPDF = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData], { type: 'application/pdf' })
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ // 打开新窗口或使用iframe嵌入PDF查看器
|
|
|
+ window.open(url, '_blank')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览Office文档
|
|
|
+ const previewOffice = (attachment, binaryData) => {
|
|
|
+ // 可以使用Microsoft Office Online或Google Docs等在线服务
|
|
|
+ // 这里简化为下载或使用第三方库
|
|
|
+ const mimeTypes = {
|
|
|
+ word: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
|
+ excel: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
|
+ ppt: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|
|
+ }
|
|
|
+
|
|
|
+ const blob = new Blob([binaryData], { type: mimeTypes[attachment.type] })
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ // 可以使用第三方库如mammoth.js(Word)、SheetJS(Excel)等
|
|
|
+ // 或者使用在线预览服务
|
|
|
+ proxy.$showToast(`使用在线服务预览: ${attachment.name}`)
|
|
|
+ window.open(
|
|
|
+ `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`,
|
|
|
+ '_blank'
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览文本文件
|
|
|
+ const previewText = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData], { type: 'text/plain' })
|
|
|
+ const reader = new FileReader()
|
|
|
+
|
|
|
+ reader.onload = e => {
|
|
|
+ const content = e.target.result
|
|
|
+ // 显示文本内容,可以使用弹窗或模态框
|
|
|
+ proxy.$dialog.alert({
|
|
|
+ title: attachment.name,
|
|
|
+ message: content
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ reader.readAsText(blob)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览视频
|
|
|
+ const previewVideo = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData], { type: 'video/mp4' }) // 根据实际类型调整
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ // 使用视频播放器组件或打开新窗口
|
|
|
+ proxy.$dialog.alert({
|
|
|
+ title: attachment.name,
|
|
|
+ message: `<video controls style="width:100%"><source src="${url}" type="video/mp4"></video>`,
|
|
|
+ allowHtml: true
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 预览音频
|
|
|
+ const previewAudio = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData], { type: 'audio/mpeg' }) // 根据实际类型调整
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ // 使用音频播放器组件或打开新窗口
|
|
|
+ proxy.$dialog.alert({
|
|
|
+ title: attachment.name,
|
|
|
+ message: `<audio controls style="width:100%"><source src="${url}" type="audio/mpeg"></audio>`,
|
|
|
+ allowHtml: true
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下载文件
|
|
|
+ const downloadFile = (attachment, binaryData) => {
|
|
|
+ const blob = new Blob([binaryData])
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
+
|
|
|
+ const a = document.createElement('a')
|
|
|
+ a.href = url
|
|
|
+ a.download = attachment.name
|
|
|
+ document.body.appendChild(a)
|
|
|
+ a.click()
|
|
|
+ document.body.removeChild(a)
|
|
|
+
|
|
|
+ // 清理URL对象
|
|
|
+ setTimeout(() => URL.revokeObjectURL(url), 100)
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .email-detail {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100%;
|
|
|
+ overflow-y: auto;
|
|
|
+ overflow-x: hidden;
|
|
|
+ background-color: #f7f8fa;
|
|
|
+ scrollbar-width: none; /* Firefox */
|
|
|
+ -ms-overflow-style: none; /* IE and Edge */
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ display: none; /* Chrome, Safari and Opera */
|
|
|
+ }
|
|
|
+ .email-detail-bar {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ width: 100%;
|
|
|
+ z-index: 100;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .email-header {
|
|
|
+ padding: 16px;
|
|
|
+ background-color: #fff;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .subject {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ color: #323233;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sender-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .sender-details {
|
|
|
+ flex: 1;
|
|
|
+ margin-left: 12px;
|
|
|
+
|
|
|
+ .sender-name {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #323233;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sender-to {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #969799;
|
|
|
+ margin-top: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .email-time {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #969799;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .email-content {
|
|
|
+ padding: 16px;
|
|
|
+ background-color: #fff;
|
|
|
+ flex: 1;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #323233;
|
|
|
+ font-size: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .attachments-section {
|
|
|
+ margin-top: 8px;
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 16px;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ color: #323233;
|
|
|
+ }
|
|
|
+
|
|
|
+ .attachments-list {
|
|
|
+ .attachment-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px;
|
|
|
+ background-color: #f7f8fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .attachment-icon {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background-color: #e8f3ff;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-right: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .attachment-info {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .attachment-name {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #323233;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ max-width: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .attachment-size {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #969799;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .download-icon {
|
|
|
+ color: #1989fa;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-bar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ padding: 12px 0;
|
|
|
+ background-color: #fff;
|
|
|
+ border-top: 1px solid #ebedf0;
|
|
|
+
|
|
|
+ .action-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 12px;
|
|
|
+
|
|
|
+ span {
|
|
|
+ font-size: 12px;
|
|
|
+ margin-top: 4px;
|
|
|
+ color: #646566;
|
|
|
+ }
|
|
|
+
|
|
|
+ .van-icon {
|
|
|
+ color: #323233;
|
|
|
+ }
|
|
|
+
|
|
|
+ .starred {
|
|
|
+ color: #ff9800;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|