<template>
  <view class="ml-upload" :class="[className || '', {'is-loading': loading}]">
    <!-- 上传图片 -->
    <view v-if="mediatype === 'image' || platformEnv.isDDMP" class="ml-upload-image-list">
      <view class="ml-upload-image-item" v-for="(item, index) in fileList" :key="index">
        <img class="ml-upload-image-img" mode="aspectFill" :src="getThumbnailUrl(item)" @tap="previewImageEvent(item, index)">
        <view v-if="!isFromReadonly" class="ml-upload-image-del-btn" @tap="removeEvent(item)">
          <ml-icon name="close" />
        </view>
      </view>
      <template v-if="!isFromReadonly && !isLimit">
        <view v-if="hasForm" class="ml-upload-image-form-add-btn" @tap="chooseEvent">
          <ml-icon v-if="loading" name="loading" roll />
          <ml-icon v-else name="add-circle" />
        </view>
        <view v-else class="ml-upload-image-add">
          <view class="ml-upload-image-add-btn" @tap="chooseEvent">
            <view class="ml-upload-image-add-icon">
              <ml-icon v-if="loading" name="loading" roll />
              <ml-icon v-else name="add" />
              <view v-if="loading" class="ml-upload-image-add-loading-text">上传中...</view>
            </view>
          </view>
        </view>
      </template>
    </view>
    <!-- 上传文件 -->
    <view v-else class="ml-upload-file-list">
      <template v-if="!isFromReadonly && !isLimit">
        <view v-if="hasForm" class="ml-upload-file-form-add-btn" @tap="chooseEvent">
          <ml-icon v-if="loading" name="loading" roll />
          <ml-icon v-else name="add-circle" />
        </view>
        <view v-else class="ml-upload-file-add">
          <view class="ml-upload-file-add-btn" @tap="chooseEvent">
            <text class="ml-upload-file-add-icon">
              <ml-icon v-if="loading" name="loading" roll />
              <ml-icon v-else name="add" />
            </text>
            <text v-if="loading">上传中...</text>
            <text v-else>{{ uploadButtonText || '选择文件' }}</text>
          </view>
        </view>
      </template>
      <view class="ml-upload-file-item" v-for="(item, index) in fileList" :key="index">
        <view class="ml-upload-file-left">
          <view class="ml-upload-file-icon">
            <ml-icon :name="getFileIcon(item)" />
          </view>
        </view>
        <view class="ml-upload-file-info">
          <view class="ml-upload-file-name">{{ item[nameField] }}</view>
          <view class="ml-upload-file-desc">
            <ml-button v-if="preview && previewTypes.includes(item[typeField])" status="primary" type="text" size="small" @click="previewDocs(item)">预览</ml-button>
            <ml-button status="primary" type="text" size="small" @click="downloadEvent(item)">下载</ml-button>
          </view>
        </view>
        <view v-if="!isFromReadonly" class="ml-upload-file-right">
          <view class="ml-upload-file-del-btn" @tap="removeEvent(item)">
            <ml-icon name="close" />
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'
import globalStore from '../../../config/store'
import MaliUtils from '../../../utils'

function normalizeChooseAndUploadFileRes (res: any, fileType?: any) {
  res.tempFiles.forEach((item: any) => {
    if (!item.name) {
      item.name = item.path.substring(item.path.lastIndexOf('/') + 1)
    }
    if (fileType) {
      item.fileType = fileType
    }
  })
  return res
}

function compressImages (tempFiles: any, opts: any) {
  return Promise.all(
    tempFiles.map((item: any) => {
      return new Promise(resolve => {
        uni.compressImage({
          src: item.path,
          quality: opts.quality || 80,
          success (compressRes) {
            resolve({
              ...item,
              path: compressRes.tempFilePath
            })
          },
          fail () {
            resolve(item)
          }
        })
      })
    })
  )
}

function uploadImageFiles (opts: any) {
  return new Promise((resolve, reject) => {
    uni.chooseImage({
      count: opts.count || 9,
      sourceType: opts.sourceType,
      // #ifdef MP-WEIXIN
      sizeType: opts.sizeType,
      // #endif
      success (chooseRes) {
        const rest = normalizeChooseAndUploadFileRes(chooseRes, 'image')
        // 如果是图片类型，支持压缩比例
        if (opts.quality) {
          compressImages(rest.tempFiles, opts).then((compressFiles) => {
            resolve({
              ...rest,
              tempFiles: compressFiles
            })
          })
        } else {
          resolve(rest)
        }
      },
      fail (res) {
        reject(res)
      }
    })
  })
}

function uploadAllFiles (opts: any) {
  return new Promise((resolve, reject) => {
    let chooseFile: any = uni.chooseFile
    if (typeof wx !== 'undefined' && typeof wx.chooseMessageFile === 'function') {
      chooseFile = wx.chooseMessageFile
    }
    if (typeof chooseFile !== 'function') {
      const err = {}
      return reject(err)
    }
    chooseFile({
      type: 'all',
      count: opts.count || 9,
      success (res: any) {
        resolve(normalizeChooseAndUploadFileRes(res))
      },
      fail (res: any) {
        reject(res)
      }
    })
  })
}

export default defineComponent({
  name: 'MlUpload',
  props: {
    modelValue: {
      type: Array as PropType<any[]>,
      default: () => ([])
    },
    urlList: {
      type: Array as PropType<any[]>,
      default: () => ([])
    },
    preview: {
      type: Boolean,
      default: true
    },
    simpleValue: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: true
    },
    readonly: {
      type: Boolean,
      default: false
    },
    limit: Number,
    limitCount: Number,
    limitSize: {
      type: Number,
      default: Number(globalStore.upload?.limitSize || 20480)
    },
    className: String,
    mediatype: {
      type: String,
      default: 'all'
    },
    uploadButtonText: String,
    params: {
      type: Object as any,
      default: () => ({})
    },
    fileProps: {
      type: Object as any,
      default: () => ({})
    },
    imageQuality: Number,
    sizeType: {
      type: Array as PropType<any[]>,
      default: () => ['original', 'compressed']
    },
    sourceType: {
      type: Array as PropType<any[]>,
      default: () => ['album', 'camera']
    },
    getFileUrl: Function,
    getThumbnailFileUrl: Function,
    uploadFile: Function,
    removeFile: Function,
    downloadFile: Function
  },
  inject: {
    currVM: {
      from: '$pageVM',
      default: null
    },
    form: {
      from: 'mlForm',
      default: null
    }
  },
  data () {
    return {
      loading: false,
      fileList: [] as any[],
      docsTypes: ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx'],
      imgTypes: ['png', 'jpg', 'gif'],
      platformEnv: MaliUtils.getPlatformEnv(),
      isModelUpdate: false,
      previewStatus: false
    }
  },
  computed: {
    hasForm () {
      return !!this.form
    },
    limitFileLen () {
      return this.limitCount || this.limit || 9
    },
    isLimit () {
      if (this.multiple) {
        return this.fileList.length >= this.limitFileLen
      }
      return this.fileList.length >= 1
    },
    isFromReadonly () {
      const form: any = this.form
      if (this.readonly) {
        return true
      }
      return form ? form.readonly : false
    },
    previewTypes () {
      return this.docsTypes.concat(this.imgTypes)
    },
    imageList () {
      return this.fileList ? this.fileList.filter(item => this.imgTypes.includes(item[this.typeField])) : []
    },
    fieldMaps () {
      return { ...globalStore.upload?.fileProps, ...this.fileProps }
    },
    urlField () {
      return this.fieldMaps.fileUrl || 'fileUrl'
    },
    typeField () {
      return this.fieldMaps.fileType || 'fileType'
    },
    nameField () {
      return this.fieldMaps.fileName || 'fileName'
    }
  },
  watch: {
    modelValue () {
      if (!this.isModelUpdate) {
        this.loadList()
      }
      this.isModelUpdate = false
    },
    urlList () {
      this.loadList()
    }
  },
  created () {
    this.loadList()
  },
  methods: {
    getFileIcon (item: any) {
      switch (item[this.typeField]) {
        case 'png':
        case 'jpg':
          return 'file-image'
        case 'doc':
        case 'docx':
          return 'file-word'
        case 'xls':
        case 'xlsx':
          return 'file-excel'
        case 'ppt':
        case 'pptx':
          return 'file-ppt'
        case 'pdf':
          return 'file-pdf'
      }
      return 'file-txt'
    },
    loadList () {
      if (this.simpleValue) {
        this.fileList = (this.urlList && this.urlList.length ? this.urlList : (this.modelValue || [])).map(url => {
          const name = MaliUtils.getFileName(url)
          const obj = {
            id: null,
            [this.nameField]: name,
            [this.typeField]: MaliUtils.getFileSuffix(name),
            [this.urlField]: url
          }
          return {
            id: null,
            [this.nameField]: name,
            [this.typeField]: MaliUtils.getFileSuffix(name),
            [this.urlField]: url,
            tempPath: this.getUrl(obj)
          }
        })
        if (this.urlList && this.urlList.length) {
          this.isModelUpdate = true
          this.updateModel()
        }
      } else {
        this.fileList = (this.modelValue || []).map(item => {
          item.tempPath = this.getUrl(item)
          return item
        })
      }
    },
    updateModel () {
      const urlList = this.fileList.map(item => item[this.urlField])
      const value = this.simpleValue ? urlList : this.fileList
      this.$emit('update:urlList', urlList)
      this.$emit('update:modelValue', value)
      this.$emit('change', { value })
    },
    removeEvent (item: any) {
      this.fileList = this.fileList.filter(obj => obj[this.urlField] !== item[this.urlField])
      const removeMethod = this.removeFile ? this.removeFile : (globalStore.upload ? globalStore.upload.removeFile : null)
      if (!item) {
        console.error('删除出错！！！')
      }
      if (item && !item.id) {
        if (removeMethod) {
          removeMethod.call(this.currVM, {
            item,
            params: this.params
          })
        }
      }
      this.isModelUpdate = true
      this.updateModel()
    },
    getThumbnailUrl (item: any) {
      const getThumbnailMethod = this.getThumbnailFileUrl ? this.getThumbnailFileUrl : (globalStore.upload ? globalStore.upload.getThumbnailFileUrl : null)
      if (getThumbnailMethod) {
        return getThumbnailMethod.call(this.currVM, {
          item,
          params: this.params
        })
      }
      return this.getUrl(item)
    },
    getUrl (item: any) {
      const getFileMethod = this.getFileUrl ? this.getFileUrl : (globalStore.upload ? globalStore.upload.getFileUrl : null)
      if (getFileMethod) {
        return getFileMethod.call(this.currVM, {
          item,
          params: this.params
        })
      }
      return item[this.urlField]
    },
    previewImageEvent (item: any, index: any) {
      if (!this.preview) {
        return
      }
      if (this.previewStatus) {
        return
      }
      this.previewStatus = true
      Promise.all(
        this.fileList.map(fileItem => {
          return new Promise((resolve) => {
            uni.downloadFile({
              url: this.getUrl(fileItem),
              success: (downRes) => {
                resolve({
                  url: downRes.tempFilePath
                })
              },
              fail () {
                resolve({
                  url: ''
                })
              }
            })
          })
        })
      ).then(res => {
        uni.previewImage({
          current: index,
          urls: res.map((item: any) => item.url)
        })
        this.previewStatus = false
      })
    },
    previewImg (item: any) {
      uni.downloadFile({
        url: this.getUrl(item),
        success: (downRes) => {
          uni.previewImage({
            urls: [
              downRes.tempFilePath
            ],
            longPressActions: {
              itemList: ['保存图片']
            }
          })
        },
        fail (e) {
          console.log(e)
        }
      })
    },
    previewDocs (item: any) {
      // 仅支持文档和图片
      if (this.previewTypes.includes(item[this.typeField])) {
        uni.downloadFile({
          url: this.getUrl(item),
          success: (downRes) => {
            if (this.docsTypes.includes(item[this.typeField])) {
              uni.openDocument({
                filePath: downRes.tempFilePath,
                showMenu: true,
                success () {},
                fail: () => {
                  uni.showModal({
                    content: '打开失败',
                    showCancel: false
                  })
                }
              })
            } else if (this.imgTypes.includes(item[this.typeField])) {
              this.previewImg(item)
            }
          },
          fail () {
            uni.showModal({
              content: '下载失败',
              showCancel: false
            })
          }
        })
      }
    },
    downloadEvent (item: any) {
      const downloadFileMethod = this.downloadFile ? this.downloadFile : (globalStore.upload ? globalStore.upload.downloadFile : null)
      if (downloadFileMethod) {
        return downloadFileMethod.call(this.currVM, {
          item,
          params: this.params
        })
      }

      // #ifdef MP-DINGTALK
      uni.showModal({
        content: '当前环境不支持下载',
        showCancel: false
      })
      // #endif

      // #ifndef MP-DINGTALK
      uni.downloadFile({
        url: this.getUrl(item),
        success: (downRes) => {
          uni.saveFile({
            tempFilePath: downRes.tempFilePath,
            success (res) {
              uni.showToast({
                icon: 'none',
                mask: true,
                title: '文件已保存：' + res.savedFilePath, // 保存路径
                duration: 4000
              })
            },
            fail (e) {
              console.log(e)
            }
          })
        },
        fail () {
          uni.showModal({
            content: '下载失败',
            showCancel: false
          })
        }
      })
      // #endif
    },
    choose () {
      if (this.loading) {
        return
      }
      let chooseMethod = uploadAllFiles
      // 钉钉暂时只支持图片
      if (this.mediatype === 'image' || this.platformEnv.isDDMP || this.platformEnv.isQWMP) {
        chooseMethod = uploadImageFiles
      }
      const uploadMethod = this.uploadFile ? this.uploadFile : (globalStore.upload ? globalStore.upload.uploadFile : null)
      chooseMethod({
        sizeType: this.sizeType,
        sourceType: this.sourceType,
        count: this.limitFileLen - this.fileList.length,
        quality: this.imageQuality
      }).then((res: any) => {
        const { tempFiles } = res
        this.loading = true
        Promise.all(
          tempFiles.map((file: any) => {
            const fileSuffix = MaliUtils.getFileSuffix(file.name)
            if (uploadMethod) {
              return Promise.resolve(
                uploadMethod.call(this.currVM, {
                  file,
                  params: this.params
                })
              ).then(res => {
                return {
                  ...res,
                  isNew: true,
                  [this.nameField]: res[this.nameField] || file.name,
                  [this.typeField]: res[this.typeField] || fileSuffix,
                  tempPath: file.path
                }
              })
            }
            return {
              isNew: true,
              [this.nameField]: file.name,
              [this.typeField]: file.extname,
              [this.urlField]: file.path,
              tempPath: file.path
            }
          })
        ).then(list => {
          this.fileList = [...this.fileList, ...list].slice(-this.limitFileLen)
          this.isModelUpdate = true
          this.updateModel()
          this.loading = false
        }).catch((e) => {
          console.log(e)
          this.loading = false
        })
      }).catch(e => {
        console.log(e)
      })
    },
    chooseEvent () {
      this.choose()
    }
  }
})
</script>

<style lang="scss">
.ml-upload-image-list {
  position: relative;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  .ml-upload-image-item,
  .ml-upload-image-add {
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    width: 140rpx;
    height: 140rpx;
    text-align: center;
  }
  .ml-upload-image-del-btn {
    position: absolute;
    right: 0;
    top: 0;
    width: 36rpx;
    height: 36rpx;
    line-height: 36rpx;
    font-size: 28rpx;
    text-align: center;
    border-radius: 50%;
    color: #ffffff;
    background: #c8c8c9;
  }
  .ml-upload-image-add-btn {
    width: 120rpx;
    height: 120rpx;
    font-size: 60rpx;
    color: #c8c8c9;
    border: 1px dashed #c8c8c9;
    border-radius: $ml-border-radius;
  }
  .ml-upload-image-add-icon {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  .ml-upload-image-add-loading-text {
    font-size: 24rpx;
  }
  .ml-upload-image-img {
    width: 116rpx;
    height: 116rpx;
    line-height: 116rpx;
    border-radius: $ml-border-radius;
  }
}
.ml-upload-file-list {
  position: relative;
  .ml-upload-file-add,
  .ml-upload-file-item {
    margin: 10rpx 0;
  }
  .ml-upload-file-item {
    display: flex;
    flex-direction: row;
  }
  .ml-upload-file-item {
    padding-bottom: 10rpx;
    border-bottom: 1px solid #f1f1f1;
  }
  .ml-upload-file-left,
  .ml-upload-file-right {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  .ml-upload-file-add-btn {
    height: 70rpx;
    line-height: 70rpx;
    text-align: center;
    color: #8a8686;
    border: 1px dashed #c8c8c9;
    border-radius: $ml-border-radius;
  }
  .ml-upload-file-add-icon {
    margin-right: 10rpx;
  }
  .ml-upload-file-del-btn {
    width: 40rpx;
    height: 40rpx;
    line-height: 40rpx;
    text-align: center;
    font-size: 28rpx;
    color: #ffffff;
    background: #c8c8c9;
    border-radius: 50%;
  }
  .ml-upload-file-left {
    width: 80rpx;
  }
  .ml-upload-file-icon {
    font-size: 60rpx;
    color: #666666;
  }
  .ml-upload-file-info {
    flex-grow: 1;
    overflow: hidden;
    .ml-upload-file-name {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  .ml-upload-file-right {
    width: 60rpx;
  }
}
.ml-upload-image-form-add-btn,
.ml-upload-file-form-add-btn {
  position: absolute;
  top: -74rpx;
  right: 20rpx;
  font-size: 44rpx;
}
</style>
