<template>
  <div class="gy-import-questions" v-loading="is_uploading">
    <div style="display: flex; justify-content: space-between; flex-wrap: wrap">
      <div class="gy-form inline" style="--fix: 80px">
        <div class="gy-form-item">
          <div class="gy-label middle">试题分类：</div>
          <QuestionClassifySelector
            v-model="classifyId"
            :mountedLoad="true"
          ></QuestionClassifySelector>
        </div>
        <div class="gy-form-item">
          <el-button
            round
            type="text"
            @click="downloadModelFile"
            style="padding: 0"
            >下载模板</el-button
          >
          <div
            class="gy-label middle"
            style="height: 32px; white-space: nowrap; color: #ed0000"
          >
            （下载模板后按要求录入内容）
          </div>
        </div>
      </div>
      <div>
        <el-button round type="primary" @click="uploadFile">导入</el-button>
        <el-button
          round
          type="primary"
          @click="filterMistakeAndUpload"
          v-if="passList.length > 0 && errorList.length > 0"
          >过滤错误并上传</el-button
        >
        <el-button round type="primary" plain @click="errorList = passList = []"
          >清理</el-button
        >
        <input
          type="file"
          name="fileUploader"
          id="fileUploader"
          ref="fileUploader"
          accept=".xlsx"
          @change="getFileContent"
        />
      </div>
    </div>
    <div v-if="errorList.length === 0">
      <el-empty description="请先导入试题"></el-empty>
    </div>
    <ul class="error-info-list" v-else>
      <li class="error-item header">
        <div class="column">表名</div>
        <div class="column">行号</div>
        <div class="column">题目</div>
        <div class="column">错误信息</div>
      </li>
      <div style="overflow-y: auto; max-height: 50vh">
        <li v-for="(item, ind) in errorList" :key="ind" class="error-item">
          <div class="column">{{ item.question.sheetName }}</div>
          <div class="column">{{ item.question.rowNum }}</div>
          <div class="column">{{ item.question.title }}</div>
          <ul class="column">
            <li v-for="(err, ind) in item.error" :key="ind">
              {{ err }}
            </li>
          </ul>
        </li>
      </div>
    </ul>
  </div>
</template>

<script>
import * as XLSX from 'xlsx'
import QuestionClassifySelector from './QuestionClassifySelector.vue'
import { importQuestionsApi } from '@/api/assessment-evaluation/questions'
export default {
  components: { QuestionClassifySelector },
  data: () => ({
    errorList: [],
    passList: [],
    classifyId: null,
    is_uploading: false
  }),
  methods: {
    uploadFile () {
      if (this.classifyId == null) return this.$message.error('请先选择科目')
      this.$refs.fileUploader.click()
    },
    uploadToServer () {
      const data = this.passList
      const classifyId = this.classifyId
      this.is_uploading = true
      importQuestionsApi(classifyId, data)
        .then((res) => {
          this.$message.success('导入成功')
          this.passList = []
          this.errorList = []
          this.$router.go(-1)
        })
        .catch((err) => {
          console.info(err)
        })
        .finally((_) => {
          this.is_uploading = false
        })
    },
    filterMistakeAndUpload () {
      this.$confirm(
        `文件中共包含 ${
          this.errorList.length + this.passList.length
        } 道题，其中${this.errorList.length}道题不符合规则，本次将导入 ${
          this.passList.length
        } 道题，确认导入吗？`,
        '提示',
        { type: 'info' }
      )
        .then((res) => {
          this.uploadToServer()
        })
        .catch((_) => {})
    },
    getFileContent (e) {
      this._toJSON(e.target.files[0])
        .then((res) => {
          const { pass, errors } = this.checkFormat(res)
          this.passList = pass
          this.errorList = errors
          if (errors.length === 0) {
            this.$confirm(
              `本次将导入 ${pass.length} 道题，确认导入吗？`,
              '提示',
              { type: 'info' }
            )
              .then((res) => {
                this.uploadToServer()
              })
              .catch((_) => {})
          }
          e.target.value = ''
        })
        .catch((err) => {
          this.$message.error(err.message)
        })
    },
    checkFormat (questionList) {
      const pass = []
      const errors = []
      const types = ['单选题', '多选题', '判断题', '填空题', '简答题']
      const difficultys = ['易', '较易', '中等', '较难', '难']
      questionList.forEach((item) => {
        const errorInfos = []
        let tempOptions
        let tempAnswer

        if (item.title.length > 255) {
          errorInfos.push('题目长度不能超过255个字符')
        }
        if (!item.title) {
          errorInfos.push('题目不能为空')
        }
        if (!item.type) {
          errorInfos.push('试题类型不能为空')
        } else {
          if (!types.includes(item.type)) {
            errorInfos.push(
              `【${item.type}】不是标准的试题类型（${types.join(',')}）`
            )
          }
        }
        if (!item.difficulty) {
          errorInfos.push('难易程度不能为空')
        } else {
          if (!difficultys.includes(item.difficulty)) {
            errorInfos.push(
              `【${item.difficulty}】不是标准的难易程度（${difficultys.join(
                ','
              )}）`
            )
          }
        }
        if (!this.isJSON(item.answer) || !item.answer) {
          errorInfos.push('答案格式不符或为空')
        } else {
          tempAnswer = JSON.parse(item.answer)
          if (!Array.isArray(tempAnswer)) {
            errorInfos.push('答案应是一个数组')
          } else {
            if (tempAnswer.length === 0 && item.type !== '填空题') {
              errorInfos.push('没有答案')
            }
          }
          if (['单选题', '判断题', '简答题'].includes(item.type)) {
            if (tempAnswer.length > 1) {
              errorInfos.push(`题型【${item.type}】只能有一个答案`)
            }
          }
        }
        if (!this.isJSON(item.options) || !item.options) {
          errorInfos.push('选项格式不符或为空')
        } else {
          tempOptions = JSON.parse(item.options)
          if (!Array.isArray(tempOptions)) {
            errorInfos.push('选项应是一个数组')
          } else {
            if (!tempOptions.length === 0) {
              errorInfos.push('没有选项')
            }
          }
        }
        if (
          ['单选题', '判断题', '多选题'].includes(item.type) &&
          tempAnswer &&
          tempOptions
        ) {
          if (tempOptions.length < tempAnswer.length) {
            errorInfos.push('试题答案数量不能多于试题选项数量')
          }
        }
        if (
          ['判断题'].includes(item.type) &&
          tempAnswer.length > 0 &&
          ![0, 1].includes(tempAnswer[0])
        ) {
          errorInfos.push('判断题答案只能为0-错误、1-正确')
        }
        if (errorInfos.length > 0) {
          errors.push({
            question: item,
            error: errorInfos
          })
        } else {
          pass.push(item)
        }
      })
      return { pass, errors }
    },
    _toJSON (file) {
      return new Promise((resolve, reject) => {
        if (!/^.+\.xlsx$/.test(file.name)) {
          reject(new Error('不是Excel表格文件'))
        }
        const reader = new FileReader()
        reader.onload = (event) => {
          const data = event.target.result
          const workbook = XLSX.read(data, {
            type: 'binary'
          })
          let resultRes = []
          workbook.SheetNames.forEach(function (sheetName) {
            let sheetData = XLSX.utils.sheet_to_json(
              workbook.Sheets[sheetName],
              {
                header: [
                  'title',
                  'difficulty',
                  'knowledge',
                  'options',
                  'answer',
                  'analysis'
                ],
                // 表格为空时默认值为空字符串
                defval: ''
              }
            )

            sheetData.forEach((item, ind) => {
              item.sheetName = sheetName
              item.rowNum = ind + 1
              item.type = sheetName
            })

            if (sheetData.length > 0) {
              const rowValues = Object.values(sheetData[0])
              if (rowValues.includes('题目') && rowValues.includes('知识点')) {
                sheetData = sheetData.slice(1)
              }
            }
            resultRes = resultRes.concat(sheetData)
          })
          resolve(resultRes)
        }
        reader.readAsBinaryString(file)
      })
    },
    isJSON (str) {
      try {
        const obj = JSON.parse(str)
        if (typeof obj === 'object' && obj) {
          return true
        } else {
          return false
        }
      } catch (e) {
        return false
      }
    },
    // 根据试题分类导出模板
    downloadModelFile () {
      const dataSheets = [
        { sheetName: '单选题', data: [['题目', '难易程度（易、较易、中等、较难、难）', '知识点', '选项', '答案（选择题：大写A-Z对应选项）', '答案解析'], ['单选题例子，导入前请先删除', '较易', '语文', '["选项1","选项2","选项3"]', '["A"]', '答案解析XXX']] },
        { sheetName: '多选题', data: [['题目', '难易程度（易、较易、中等、较难、难）', '知识点', '选项', '答案（选择题：大写A-Z对应选项）', '答案解析'], ['多选题例子，导入前请先删除', '易', '语文', '["选项1","选项2","选项3"]', '["A","B","C"]', '答案解析XXX']] },
        { sheetName: '判断题', data: [['题目', '难易程度（易、较易、中等、较难、难）', '知识点', '选项', '答案（判断题：1正确0错误）', '答案解析'], ['判断题例子，导入前请先删除', '中等', '数学', '["正确","错误"]', '[1]', '答案解析XXX']] },
        { sheetName: '填空题', data: [['题目', '难易程度（易、较易、中等、较难、难）', '知识点', '选项', '答案（保留[],填空题无需填写答案，选项即是答案）', '答案解析'], ['填空题例子，导入前请先删除', '较难', '英语', '["空1答案","空2答案","空3答案"]', '[]', '答案解析XXX']] },
        {
          sheetName: '简答题',
          data: [
            [
              '题目',
              '难易程度（易、较易、中等、较难、难）',
              '知识点',
              // '选项（选项即是关键词）',
              '选项（保留[],无需填写选项）',
              '答案',
              '答案解析'
            ],
            [
              '简答题例子，导入前请先删除',
              '难',
              '数学',
              // '["关键词1","关键词2","关键词3"]'
              '[]',
              '["简答题标准答案只有纯文本"]',
              '答案解析XXX'
            ]
          ]
        }
      ]
      const sheets = []
      dataSheets.forEach((sheetItem) => {
        const colWidths = []
        const data = sheetItem.data
        data.forEach((row, index) => {
          // 遍历列
          row.forEach((col, ind) => {
            if (colWidths[ind] == null) colWidths[ind] = []
            colWidths[ind].push(this.getCellWidth(col))
          })
        })
        const ws = XLSX.utils.aoa_to_sheet(data)
        ws['!cols'] = []
        colWidths.forEach((widths, index) => {
          ws['!cols'].push({ wch: Math.max(...widths) })
        })
        sheets.push({
          name: sheetItem.sheetName,
          ws
        })
      })

      // const ws = XLSX.utils.json_to_sheet(data)

      const wb = XLSX.utils.book_new()
      sheets.forEach((wsItem) => {
        XLSX.utils.book_append_sheet(wb, wsItem.ws, wsItem.name)
      })
      XLSX.writeFile(
        wb,
        `试题导入模板${new Date().format('yyyy-MM-dd hh-mm-ss')}.xlsx`
      )
    },
    getCellWidth (value) {
      // 判断是否为null或undefined
      if (value == null) {
        return 10
      } else if (/.*[\u4e00-\u9fa5]+.*$/.test(value)) {
        // 中文的长度
        const chineseLength = value.match(/[\u4e00-\u9fa5]/g).length
        // 其他不是中文的长度
        const otherLength = value.length - chineseLength
        return chineseLength * 2.1 + otherLength * 1.1
      } else {
        return value.toString().length * 1.1
        /* 另一种方案
        value = value.toString()
        return value.replace(/[\u0391-\uFFE5]/g, 'aa').length
        */
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.error-info-list {
  margin-top: 10px;
  display: flex;
  border: 2px solid $--color-primary;
  border-radius: 4px;

  &,
  ul {
    padding: 0;
    list-style: none;
    flex-direction: column;
  }

  .error-item {
    display: flex;
    border-top: 1px solid #eee;

    &.header {
      color: #fff;
      border-top: none;
      background: $--color-primary;

      .column {
        border: none;
      }
    }

    .column {
      padding: 5px;
      border-right: 1px solid #eee;
      flex-grow: 1;
      flex-basis: 0;
      line-height: normal;
      display: flex;
      align-items: center;
      justify-content: center;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;

      &:nth-child(1),
      &:nth-child(2) {
        width: 60px;
        flex-basis: 60px;
        flex-grow: 0;
      }

      // &:nth-child(4)
      // {
      //   width: 100px;
      //   flex-basis: 100px;
      //   flex-grow: 0;
      // }
      &:last-child {
        border-right: none;
      }
    }
  }
}

#fileUploader {
  display: none;
}
</style>
