<template>
  <div class="file-upload-container">
    <el-upload
      ref="uploadRef"
      :action="uploadUrl"
      :headers="headers"
      :data="data"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-remove="handleRemove"
      :on-preview="handlePreview"
      :limit="limit"
      :accept="accept"
      :multiple="multiple"
      :disabled="disabled"
    >
      <el-button type="primary" plain icon="Upload" :disabled="disabled">上传附件</el-button>
      <template #tip>
        <div class="el-upload__tip">
          支持{{ acceptText }}上传，单个文件不超过{{ maxSize }}MB，最多上传{{ limit }}个文件
        </div>
      </template>
    </el-upload>

    <!-- 文件预览对话框 -->
    <el-dialog v-model="previewVisible" :title="previewTitle" width="800px" append-to-body>
      <div class="preview-container">
        <img v-if="isImage" :src="previewUrl" class="preview-image" />
        <iframe v-else :src="previewUrl" class="preview-iframe"></iframe>
      </div>
    </el-dialog>
  </div>
</template>

<script setup name="FileUpload">
import { ref, computed, watch } from 'vue'
import { getToken } from '@/utils/auth'

const props = defineProps({
  modelValue: {
    type: Array,
    default: () => []
  },
  action: {
    type: String,
    default: '/common/upload'
  },
  data: {
    type: Object,
    default: () => ({})
  },
  limit: {
    type: Number,
    default: 5
  },
  accept: {
    type: String,
    default: '.jpg,.jpeg,.png,.gif,.bmp,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar'
  },
  multiple: {
    type: Boolean,
    default: true
  },
  disabled: {
    type: Boolean,
    default: false
  },
  maxSize: {
    type: Number,
    default: 10
  }
})

const emit = defineEmits(['update:modelValue', 'change'])

const { proxy } = getCurrentInstance()

const uploadRef = ref(null)
const fileList = ref([])
const previewVisible = ref(false)
const previewUrl = ref('')
const previewTitle = ref('')

const uploadUrl = computed(() => props.action)
const headers = computed(() => ({
  Authorization: 'Bearer ' + getToken()
}))

const acceptText = computed(() => {
  const types = props.accept.split(',')
  return types.join('、')
})

const isImage = computed(() => {
  const ext = previewUrl.value.split('.').pop()?.toLowerCase()
  return ['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(ext)
})

watch(() => props.modelValue, (val) => {
  fileList.value = val || []
}, { immediate: true })

/** 上传前校验 */
function beforeUpload(file) {
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLtMaxSize) {
    proxy.$modal.msgError(`文件大小不能超过 ${props.maxSize}MB!`)
    return false
  }
  return true
}

/** 上传成功 */
function handleSuccess(response, file, fileList) {
  if (response.code === 200) {
    const fileData = {
      uid: file.uid,
      name: file.name,
      url: response.data.url,
      fileName: response.data.fileName
    }
    emit('update:modelValue', [...props.modelValue, fileData])
    emit('change', [...props.modelValue, fileData])
    proxy.$modal.msgSuccess('上传成功')
  } else {
    proxy.$modal.msgError(response.msg || '上传失败')
  }
}

/** 上传失败 */
function handleError(error) {
  proxy.$modal.msgError('上传失败: ' + error.message)
}

/** 文件移除 */
function handleRemove(file, fileList) {
  const newList = props.modelValue.filter(item => item.uid !== file.uid)
  emit('update:modelValue', newList)
  emit('change', newList)
}

/** 文件预览 */
function handlePreview(file) {
  previewUrl.value = file.url || file.response?.data?.url
  previewTitle.value = file.name
  previewVisible.value = true
}

/** 清空文件列表 */
function clearFiles() {
  uploadRef.value?.clearFiles()
}

/** 获取文件列表 */
function getFileList() {
  return props.modelValue
}

defineExpose({
  clearFiles,
  getFileList
})
</script>

<style scoped>
.file-upload-container {
  width: 100%;
}
.preview-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 400px;
}
.preview-image {
  max-width: 100%;
  max-height: 500px;
}
.preview-iframe {
  width: 100%;
  height: 500px;
  border: none;
}
</style>
