<template>
  <div class="page page-upload">
    <el-row>
      <el-col :span="24">
        <el-card shadow="never">
          <div slot="header" class="clearfix">
            <strong>上传文档</strong>
          </div>
          <el-row :gutter="40">
            <el-col :span="14" class="part-left">
              <el-form
                ref="form"
                :model="document"
                label-position="top"
                label-width="80px"
              >
                <el-form-item
                  label="文档分类"
                  prop="category_id"
                  :rules="[
                    {
                      required: true,
                      trigger: 'blur',
                      message: '请选择文档分类',
                    },
                  ]"
                >
                  <el-cascader
                    v-model="document.category_id"
                    :options="categoryTrees"
                    :filterable="true"
                    :disabled="loading"
                    :props="{
                      checkStrictly: true,
                      expandTrigger: 'hover',
                      label: 'title',
                      value: 'id',
                    }"
                    placeholder="请选择文档分类"
                  ></el-cascader>
                </el-form-item>
                <el-form-item label="默认售价（魔豆）" prop="price">
                  <el-input-number
                    v-model="document.price"
                    :min="0"
                    :step="1"
                    :disabled="loading"
                  ></el-input-number>
                </el-form-item>
                <el-form-item>
                  <el-upload
                    ref="upload"
                    drag
                    multiple
                    :action="'/api/v1/upload/document'"
                    :headers="{ authorization: `bearer ${token}` }"
                    :show-file-list="false"
                    :disabled="loading || !canIUploadDocument"
                    :auto-upload="false"
                    :on-change="onChange"
                    :file-list="fileList"
                  >
                    <i class="el-icon-upload"></i>
                    <div class="el-upload__text">
                      将文件拖到此处，或<em>点击上传</em>
                    </div>
                  </el-upload>
                  <el-table
                    v-if="fileList.length > 0"
                    :data="fileList"
                    style="width: 100%"
                    max-height="480"
                  >
                    <el-table-column prop="title" label="#" width="50">
                      <template slot-scope="scope">
                        {{ scope.$index + 1 }}
                      </template>
                    </el-table-column>
                    <el-table-column prop="title" label="文件" min-width="180">
                      <template slot-scope="scope">
                        <el-input v-model="scope.row.title" :disabled="loading">
                          <template slot="append">{{
                            scope.row.ext
                          }}</template></el-input
                        >
                        <div v-if="scope.row.error">
                          <el-progress
                            :key="scope.row.name"
                            :percentage="scope.row.percentage"
                            status="exception"
                          ></el-progress>
                          <small class="el-link el-link--danger error-tips">{{
                            scope.row.error
                          }}</small>
                        </div>
                        <el-progress
                          v-else-if="scope.row.percentage > 0"
                          :percentage="scope.row.percentage"
                        ></el-progress>
                      </template>
                    </el-table-column>
                    <el-table-column prop="size" label="大小" width="100">
                      <template slot-scope="scope">
                        {{ formatBytes(scope.row.size) }}
                      </template>
                    </el-table-column>
                    <el-table-column
                      prop="price"
                      label="售价(魔豆)"
                      width="130"
                    >
                      <template slot-scope="scope">
                        <el-input-number
                          v-model="scope.row.price"
                          :min="0"
                          :step="1"
                          :disabled="loading"
                          controls-position="right"
                        ></el-input-number>
                      </template>
                    </el-table-column>
                    <el-table-column label="操作" width="100" fixed="right">
                      <template slot="header">
                        操作 (<el-button
                          type="text"
                          size="mini"
                          :disabled="loading"
                          @click="clearAllFiles"
                          >清空</el-button
                        >)
                      </template>
                      <template slot-scope="scope">
                        <el-button
                          size="mini"
                          type="text"
                          icon="el-icon-delete"
                          :disabled="loading"
                          @click="handleRemove(scope.$index)"
                        >
                          移除
                        </el-button>
                      </template>
                    </el-table-column>
                  </el-table>
                </el-form-item>
                <el-form-item style="margin-bottom: 0">
                  <el-button
                    v-if="canIUploadDocument"
                    type="primary"
                    class="btn-block"
                    :loading="loading"
                    @click="onSubmit"
                  >
                    <span v-if="loading">请勿刷新页面，文档上传中...</span>
                    <span v-else>确定上传</span>
                  </el-button>
                  <el-button
                    v-else
                    type="primary"
                    icon="el-icon-hot-water"
                    class="btn-block"
                    disabled
                  >
                    <span v-if="user.id > 0">您所在用户组暂无权限上传文档</span>
                    <span v-else>您未登录，请先登录</span>
                  </el-button>
                </el-form-item>
              </el-form>
            </el-col>
            <el-col :span="10" class="upload-tips part-right">
              <div><strong>温馨提示</strong></div>
              <div class="help-block">
                <ul>
                  <li>
                    1. 带有
                    <span class="el-link el-link--danger">*</span> 为必填项。
                  </li>
                  <li>
                    <!-- 应该从管理后台的配置中查询 -->
                    2. 允许上传的最大单个文档大小为：<span
                      class="el-link el-link--primary"
                      >{{
                        settings.security.max_document_size.toFixed(2) ||
                        '50.00'
                      }}
                      MB</span
                    >
                    。
                  </li>
                  <li>3. 支持批量上传</li>
                  <!-- <li>
                    4.
                    <span class="el-link el-link--danger">同名覆盖</span>
                    表示相同名称的文档（含扩展名），直接用新文档文件替换，以达到更新文档文件的目的。
                  </li> -->
                  <li>
                    4. 目前支持的文档类型：
                    <div v-if="wordExt.length > 0">
                      <img src="/static/images/word_24.png" alt="Word文档" />
                      {{ wordExt.join('，') }}
                    </div>
                    <div v-if="pptExt.length > 0">
                      <img src="/static/images/ppt_24.png" alt="PPT文档" />
                      {{ pptExt.join('，') }}
                    </div>
                    <div v-if="excelExt.length > 0">
                      <img src="/static/images/excel_24.png" alt="Excel文档" />
                      {{ excelExt.join('，') }}
                    </div>
                    <div v-if="otherExt.length > 0">
                      <img src="/static/images/other_24.png" alt="其他文档" />
                      {{ otherExt.join('，') }}
                    </div>
                    <div v-if="allowExt.includes('.txt')">
                      <img src="/static/images/text_24.png" alt="TXT文档" />
                      .txt
                    </div>
                    <div v-if="allowExt.includes('.pdf')">
                      <img src="/static/images/pdf_24.png" alt="PDF文档" />
                      .pdf
                    </div>
                  </li>
                  <li>
                    5. 上传遇到问题需要帮助？请查看
                    <nuxt-link
                      to="/article/help"
                      class="el-link el-link--primary"
                      >文库帮助</nuxt-link
                    >
                    和
                    <nuxt-link
                      to="/article/feedback"
                      class="el-link el-link--primary"
                      >意见反馈</nuxt-link
                    >
                  </li>
                  <li>
                    6. 为营造绿色网络环境，严禁上传含有淫秽色情及低俗信息等文档
                  </li>
                  <li>
                    7.
                    对于涉嫌侵权和违法违规的文档，本站有权在不提前通知的情况下对文档进行删除，您在本站上传文档，表示认同该条款
                  </li>
                </ul>
              </div>
            </el-col>
          </el-row>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import { formatBytes } from '~/utils/utils'
import { createDocument } from '~/api/document'
import { canIUploadDocument } from '~/api/user'
import { uploadDocument } from '~/api/attachment'
export default {
  data() {
    return {
      canIUploadDocument: false,
      document: {
        category_id: [],
        price: 0,
        overwrite: false,
      },
      maxDocumentSize: 50 * 1024 * 1024,
      fileList: [],
      filesMap: {},
      loading: false,
      percentAge: 0,
      allowExt: [
        '.doc',
        '.docx',
        '.rtf',
        '.wps',
        '.odt',
        '.dot',
        '.ppt',
        '.pptx',
        '.pps',
        '.ppsx',
        '.dps',
        '.odp',
        '.pot',
        '.xls',
        '.xlsx',
        '.csv',
        '.tsv',
        '.et',
        '.ods',
        '.epub',
        '.umd',
        '.chm',
        '.mobi',
        '.txt',
        '.pdf',
      ],
      wordExtEnum: ['.doc', '.docx', '.rtf', '.wps', '.odt', '.dot'],
      pptExtEnum: ['.ppt', '.pptx', '.pps', '.ppsx', '.dps', '.odp', '.pot'],
      excelExtEnum: ['.xls', '.xlsx', '.csv', '.tsv', '.et', '.ods'],
      otherExtEnum: ['.epub', '.umd', '.chm', '.mobi'],
      wordExt: [],
      pptExt: [],
      excelExt: [],
      otherExt: [],
      totalFiles: 0, // 总个数
      totalFailed: 0, // 失败个数
      totalSuccess: 0, // 成功个数
      totalDone: 0, // 完成个数
    }
  },
  head() {
    return {
      title: '上传文档 - ' + this.settings.system.title || 'MOREDOC · 魔豆文库',
      meta: [
        {
          hid: 'keywords',
          name: 'keywords',
          content: `上传文档,${this.settings.system.sitename},${this.settings.system.keywords}`,
        },
        {
          hid: 'description',
          name: 'description',
          content: this.settings.system.description,
        },
      ],
    }
  },
  computed: {
    ...mapGetters('user', ['token', 'user']),
    ...mapGetters('category', ['categoryTrees']),
    ...mapGetters('setting', ['settings']),
  },
  async created() {
    const res = await canIUploadDocument()
    if (res.status === 200) {
      this.canIUploadDocument = true
    }
    try {
      this.maxDocumentSize =
        (this.settings.security.max_document_size || 50) * 1024 * 1024
    } catch (error) {
      console.log(error)
    }

    try {
      this.allowExt =
        this.settings.security.document_allowed_ext || this.allowExt
    } catch (error) {
      console.log(error)
    }
    this.allowExt.map((ext) => {
      if (this.wordExtEnum.includes(ext)) {
        this.wordExt.push(ext)
      } else if (this.pptExtEnum.includes(ext)) {
        this.pptExt.push(ext)
      } else if (this.excelExtEnum.includes(ext)) {
        this.excelExt.push(ext)
      } else if (this.otherExtEnum.includes(ext)) {
        this.otherExt.push(ext)
      }
    })
  },
  methods: {
    formatBytes,
    ...mapActions('user', ['getUser']),
    onChange(file) {
      const name = file.name.toLowerCase()
      const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
      if (!this.allowExt.includes(ext)) {
        this.$message.warning(`${file.name} 不支持的文件格式，忽略该文件`)
        return
      }

      if (file.size > this.maxDocumentSize) {
        this.$message.warning(
          `${file.name} 文件大小${formatBytes(
            file.size
          )} 超过限制（最大${formatBytes(this.maxDocumentSize)}），忽略该文件`
        )
        return
      }

      // 文件不能大于指定的文件大小
      if (
        !this.filesMap[name] &&
        this.allowExt.includes(ext) &&
        file.size <= this.maxDocumentSize
      ) {
        const item = {
          ...file,
          title: file.name.substring(0, file.name.lastIndexOf('.')),
          ext,
          price: this.document.price || 0,
          progressStatus: 'success',
          error: '',
          percentage: 0,
          attachment_id: 0,
        }
        this.filesMap[name] = item
        this.fileList.push(item)
        this.totalFiles = this.fileList.length
      }
    },
    handleRemove(index) {
      this.filesMap[this.fileList[index].name] = null
      this.fileList.splice(index, 1)
    },
    onSubmit() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          // 1. 验证文件是否存在
          if (this.fileList.length === 0) {
            this.$message.error('请先选择要上传的文档')
            return
          }

          this.totalFiles = this.fileList.length
          this.totalDone = 0
          this.loading = true
          try {
            // 取消之前上传的请求，不然一直pending，新请求会没法发送
            window.uploadDocumentCancel.map((c) => c())
            window.uploadDocumentCancel = []
          } catch (error) {}

          // chrome 等浏览器同一域名下最多只能同时发起 6 个请求，所以这里将 fileList 拆分成多个数组，每个数组的长度为 2，以便控制并发，每次只同时上传 2 个文件
          const fileList = this.fileList.reduce((prev, cur, index) => {
            const i = Math.floor(index / 2)
            prev[i] = prev[i] || []
            prev[i].push(cur)
            return prev
          }, [])
          fileList.reduce(async (prev, cur) => {
            await prev
            await Promise.all(
              cur.map(async (file) => {
                await this.uploadDocument(file)
              })
            )
          }, Promise.resolve())
        }
      })
    },
    clearAllFiles() {
      if (this.loading) {
        return
      }
      this.fileList = []
      this.filesMap = {}
      this.$refs.upload.clearFiles()
    },
    async uploadDocument(file) {
      if (file.percentage === 100 && file.attachment_id) {
        // 不用再次上传
        this.createDocument(file)
        this.totalDone++
        return
      }
      file.error = ''
      file.progressStatus = 'success'

      const formData = new FormData()
      formData.append('file', file.raw)

      try {
        const res = await uploadDocument(formData, {
          onUploadProgress: (progressEvent) => {
            file.percentage = parseInt(
              (progressEvent.loaded / progressEvent.total) * 100
            )
          },
          // timeout: 1000 * 6,
        })
        if (res.status === 200) {
          file.attachment_id = res.data.data.id || 0
          this.createDocument(file)
          this.totalSuccess++
        } else {
          file.progressStatus = 'exception'
          file.error = res.data.message || res.statusText
          this.$message.error(`《${file.name}》${file.error}`)
          this.totalFailed++
        }
      } catch (error) {
        file.progressStatus = 'exception'
        file.error = '上传失败或超时，请重试'
        this.$message.error(`《${file.name}》${file.error}`)
        this.totalFailed++
      }

      this.totalDone++
      if (this.totalDone === this.totalFiles) {
        this.loading = false
      }
    },
    async createDocument(doc) {
      const createDocumentRequest = {
        overwrite: this.document.overwrite,
        category_id: this.document.category_id,
        document: [
          {
            title: doc.title,
            price: doc.price,
            attachment_id: doc.attachment_id,
          },
        ],
      }
      const res = await createDocument(createDocumentRequest)
      if (res.status === 200) {
        // 从 fileList 中剔除 attachment_id 与当前文档相同的文档
        this.$message.success(`《${doc.title}》上传成功`)
        this.fileList = this.fileList.filter((item) => {
          return item.attachment_id !== doc.attachment_id && doc.attachment_id
        })

        // 过滤 filesMap 中的文档
        this.filesMap = Object.keys(this.filesMap).reduce((acc, key) => {
          if (this.filesMap[key].attachment_id !== doc.attachment_id) {
            acc[key] = this.filesMap[key]
          }
          return acc
        }, {})
      } else {
        this.$message.error(`《${doc.title}》上传失败 ` + res.data.message)
      }
    },
  },
}
</script>
<style lang="scss">
.page-upload {
  .el-table {
    .el-input-number {
      width: 120px;
    }
  }
  .el-progress {
    position: absolute;
    width: 100%;
    bottom: -1px;
  }
  .error-tips {
    font-size: 12px;
  }
  .upload-tips {
    line-height: 180%;
    border-left: 1px dashed rgb(252, 155, 91);
    ul,
    li {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    li {
      margin-bottom: 10px;
    }
    .el-link {
      top: -2px;
    }
    img {
      position: relative;
      top: 7px;
    }
  }
}
@media screen and (max-width: $mobile-width) {
  .page-upload {
    .part-left {
      width: 100% !important;
      .el-upload {
        display: block;
        .el-upload-dragger {
          width: 100% !important;
        }
      }
    }
    .part-right {
      width: 100% !important;
      margin-top: 20px;
      li {
        margin-bottom: 0;
      }
    }
  }
}
</style>
