| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- <template>
- <div class="attachment-upload">
- <!-- 上传按钮 -->
- <el-upload
- action="#"
- :file-list="uploadFileList"
- :before-upload="beforeUpload"
- :http-request="handleUpload"
- :show-file-list="false"
- :multiple="true"
- :disabled="!canUpload"
- >
- <el-button type="primary" :disabled="!canUpload">
- <el-icon><UploadFilled /></el-icon>
- 添加附件
- </el-button>
- </el-upload>
- <!-- 已上传附件列表 -->
- <div v-if="localFileList.length > 0" class="attachment-list">
- <div v-for="file in localFileList" :key="file.id" class="attachment-item">
- <div class="attachment-row-top">
- <el-icon :size="18" class="file-icon"><Document /></el-icon>
- <span class="file-name">{{ file.fileName }}</span>
- </div>
- <div class="attachment-row-bottom">
- <span class="upload-time" v-if="file.createTime">{{ formatDate(file.createTime) }}</span>
- <div class="attachment-actions">
- <el-button type="primary" link size="small" @click="handlePreview(file)">
- <el-icon><View /></el-icon>
- 预览
- </el-button>
- <el-button type="primary" link size="small" @click="handleDownload(file)">
- <el-icon><Download /></el-icon>
- 下载
- </el-button>
- <el-button type="danger" link size="small" @click="handleDelete(file)">
- <el-icon><Delete /></el-icon>
- 删除
- </el-button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted } from 'vue'
- import { UploadFilled, Document, View, Download, Delete } from '@element-plus/icons-vue'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { TaskOrderFileApi } from '@/api/pressure2/taskOrderFile'
- import { buildFileUrl } from '@/utils'
- import { formatDate } from '@/utils/formatTime'
- import request from '@/config/axios'
- const props = defineProps({
- /** 任务单ID */
- orderId: {
- type: String,
- default: ''
- },
- /** 检验设备单ID */
- orderItemId: {
- type: String,
- default: ''
- },
- /** 设备类型 200锅炉 300管道 */
- equipMainType: {
- type: Number,
- default: 0
- },
- /** 是否可上传 */
- canUpload: {
- type: Boolean,
- default: true
- }
- })
- // 本地文件列表
- const localFileList = ref<Recordable[]>([])
- // 上传组件显示用的空列表
- const uploadFileList = ref<Recordable[]>([])
- // 页面加载时自动获取附件列表
- onMounted(() => {
- fetchFileList()
- })
- const beforeUpload = (file: File) => {
- const maxSize = 50 * 1024 * 1024
- if (file.size > maxSize) {
- ElMessage.error('文件大小不能超过50MB')
- return false
- }
- return true
- }
- // 处理文件上传
- const handleUpload = async (options: Recordable) => {
- const { file } = options
- const formData = new FormData()
- formData.append('file', file)
- formData.append('orderId', props.orderId || '')
- formData.append('orderItemId', props.orderItemId || '')
- formData.append('equipMainType', String(props.equipMainType))
- try {
- const res = await TaskOrderFileApi.uploadAndCreate(formData)
- if (res) {
- ElMessage.success('文件上传成功')
- fetchFileList()
- }
- } catch (error: any) {
- ElMessage.error(error?.message || '文件上传失败')
- }
- }
- // 预览附件
- const handlePreview = (file: Recordable) => {
- const url = buildFileUrl(file.fileUrl)
- window.open(url, '_blank')
- }
- // 下载附件 - 使用 blob 方式确保触发下载而不是浏览器打开
- const handleDownload = async (file: Recordable) => {
- try {
- const url = buildFileUrl(file.fileUrl)
- const blob = await request.download({ url })
- const blobUrl = URL.createObjectURL(blob as unknown as Blob)
- const link = document.createElement('a')
- link.href = blobUrl
- link.download = file.fileName || '下载文件'
- document.body.appendChild(link)
- link.click()
- document.body.removeChild(link)
- URL.revokeObjectURL(blobUrl)
- } catch (error: any) {
- ElMessage.error('下载失败')
- }
- }
- // 删除附件
- const handleDelete = async (file: Recordable) => {
- try {
- await ElMessageBox.confirm('确认删除该附件?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- await TaskOrderFileApi.deleteFile(file.id)
- ElMessage.success('删除成功')
- fetchFileList()
- } catch (error: any) {
- if (error !== 'cancel') {
- ElMessage.error(error?.message || '删除失败')
- }
- }
- }
- // 刷新附件列表
- const fetchFileList = async () => {
- if (!props.orderItemId) return
- try {
- const res = await TaskOrderFileApi.getPage({
- orderItemId: props.orderItemId,
- pageNo: 1,
- pageSize: 100
- })
- if (res?.list) {
- localFileList.value = res.list
- }
- } catch (error: any) {
- console.error('获取附件列表失败:', error)
- }
- }
- </script>
- <style lang="scss" scoped>
- .attachment-upload {
- .attachment-list {
- margin-top: 12px;
- .attachment-item {
- display: flex;
- flex-direction: column;
- padding: 10px 12px;
- margin-bottom: 8px;
- border: 1px solid var(--el-border-color-light);
- border-radius: 4px;
- background-color: var(--el-fill-color-lighter);
- transition: background-color 0.2s;
- &:hover {
- background-color: var(--el-fill-color-light);
- }
- .attachment-row-top {
- display: flex;
- align-items: flex-start;
- gap: 8px;
- margin-bottom: 6px;
- .file-icon {
- flex-shrink: 0;
- margin-top: 2px;
- color: var(--el-color-primary);
- }
- .file-name {
- flex: 1;
- font-size: 13px;
- line-height: 1.5;
- word-break: break-all;
- color: var(--el-text-color-primary);
- }
- }
- .attachment-row-bottom {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding-left: 26px;
- .upload-time {
- font-size: 12px;
- color: var(--el-text-color-secondary);
- }
- .attachment-actions {
- display: flex;
- align-items: center;
- gap: 4px;
- margin-left: auto;
- }
- }
- }
- }
- }
- </style>
|