index.vue 3.86 KB
<template>
  <view class="file-upload">
    <view class="btn" @click="selectFile">{{ innerName ? '重新上传' : '上传附件' }}</view>
    <view v-if="innerName" class="file-name">{{ innerName }}</view>
  </view>
</template>

<script>
import { uploadFileApi } from '@/api/base.js'
import upload from '@/utils/upload'
export default {
  name: 'FileUpload',
  props: {
    value: { type: Object, default: () => ({ id: '', name: '' }) },
    showName: { type: Boolean, default: false }
  },
  data() {
    return { innerId: this.value && this.value.id ? String(this.value.id) : '', innerName: this.value && this.value.name ? String(this.value.name) : '' }
  },
  watch: {
    value(v) {
      const id = v && v.id != null ? String(v.id) : ''
      const name = v && v.name != null ? String(v.name) : ''
      this.innerId = id
      this.innerName = name
    }
  },
  methods: {
    async uploadByFormData(fd, name) {
      console.log('uploadByFormData__fd', fd)
      return uploadFileApi(fd).then(d => {
        const data = d && d.data ? d.data : d
        const id = (data && (data.id || data.fileId || (data.data && (data.data.id || data.data.fileId)))) ? String(data.id || data.fileId || data.data.id || data.data.fileId) : ''
        this.applyResult(id, name)
      })
    },
    selectFile() {
      if (typeof uni !== 'undefined' && typeof uni.chooseMessageFile === 'function') {
        uni.chooseMessageFile({ count: 1, type: 'all', success: async (res) => {
          try {
            const f = (res && res.tempFiles && res.tempFiles[0]) ? res.tempFiles[0] : null
            if (!f) return
            const name = f.name || 'file'
            if (f.path) {
              const r = await upload({ url: '/sw/filebox/uploadFile', filePath: f.path, name: 'file' })
              const d = r && r.data ? r.data : r
              const id = (d && (d.id || d.fileId || (d.data && (d.data.id || d.data.fileId)))) ? String(d.id || d.fileId || d.data.id || d.data.fileId) : ''
              this.applyResult(id, name)
            } else if (f.file) {
              const fd = new FormData()
              fd.append('file', f.file, name)
              await this.uploadByFormData(fd, name)
            }
          } catch (e) {
            uni.showToast({ title: '上传失败', icon: 'none' })
          }
        }, fail: () => { uni.showToast({ title: '未选择文件', icon: 'none' }) } })
        return
      }
      // H5 兜底:动态创建原生 input
      try {
        const input = document.createElement('input')
        input.type = 'file'
        input.style.position = 'fixed'
        input.style.left = '-9999px'
        document.body.appendChild(input)
        input.addEventListener('change', this.onFileChange)
        input.click()
        setTimeout(() => { document.body.removeChild(input) }, 1000)
      } catch (e) {
        uni.showToast({ title: '无法打开文件选择', icon: 'none' })
      }
    },
    async onFileChange(e) {
      const files = e && e.target && e.target.files ? e.target.files : []
      const file = files && files[0] ? files[0] : null
      if (!file) return
      const name = file.name || ''
      const fd = new FormData()
      fd.append('file', file, name)
      try {
        await this.uploadByFormData(fd, name)
      } catch (e2) {
        uni.showToast({ title: '上传失败', icon: 'none' })
      } finally {
        if (e && e.target) e.target.value = ''
      }
    },
    applyResult(id, name) {
      this.innerId = id
      this.innerName = name
      const payload = { id, name }
      this.$emit('input', payload)
      this.$emit('update:value', payload)
      this.$emit('change', payload)
    }
  }
}
</script>

<style scoped>
.file-upload { 
  display: flex;
 }
.btn { 
  color: #3D48A3; font-size: 28rpx; margin-right: 20rpx; cursor: pointer; 
  flex-shrink: 0;
}
.file-name { 
  font-size: 28rpx; color: rgba(0,0,0,0.9); 
  line-height: 40rpx;
  }
</style>