traffic-audit-web/src/views/DataImport.vue
@@ -51,15 +51,15 @@
                  <el-button size="mini" type="primary" icon="el-icon-upload2" :loading="cityDirLoading"
                             @click="importCityDir(moduleSel[g.key])">从目录批量导入</el-button>
                </div>
                <div v-else-if="moduleSel[g.key]" class="module-file">
                  <div class="file-name" :title="fileNames[moduleSel[g.key]]">{{ fileNames[moduleSel[g.key]] || '未选择文件' }}</div>
                <div v-else-if="moduleSel[g.key] && !isDirOnlyType(moduleSel[g.key])" class="module-file">
                  <div class="file-name" :title="fileNamesText(moduleSel[g.key])">{{ fileNamesText(moduleSel[g.key]) || '未选择文件' }}</div>
                  <div class="file-btns">
                    <el-upload action="#" :auto-upload="false" :show-file-list="false" accept=".xlsx,.xls"
                    <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple accept=".xlsx,.xls,.et"
                               :ref="'up' + g.key" :on-change="f => onFilePicked(g.key, f)" style="display:inline-block">
                      <el-button size="mini" type="primary" plain icon="el-icon-folder-opened">选择文件</el-button>
                    </el-upload>
                    <el-button size="mini" type="primary" icon="el-icon-upload2" :disabled="!files[moduleSel[g.key]]"
                               @click="importNow(g.key)">立即导入</el-button>
                    <el-button size="mini" type="primary" icon="el-icon-upload2" :disabled="!fileCount(moduleSel[g.key])"
                               @click="importNow(g.key)">{{ fileCount(moduleSel[g.key]) > 1 ? '批量导入 ' + fileCount(moduleSel[g.key]) + ' 个文件' : '立即导入' }}</el-button>
                  </div>
                </div>
              </div>
@@ -167,8 +167,8 @@
      overview: {},
      groups: [
        {
          key: 'freight', name: '货运', types: [
            { value: 'h2032', label: 'H203-2月报' },
          key: 'freight', name: '道路货运', types: [
            { value: 'h2032', label: '道路货物运输月度生产情况' },
            { value: 'transportAuth', label: '运政车辆' },
            { value: 'trackMileage', label: '轨迹里程' },
            { value: 'scaleSplit', label: '规上规下拆分' },
@@ -177,20 +177,20 @@
        },
        {
          key: 'passenger', name: '公路旅客', types: [
            { value: 'passengerMonthly', label: 'H203-1旅客月报' },
            { value: 'passengerMonthly', label: '公路旅客运输月度生产情况' },
            { value: 'passengerAuth', label: '运政车辆数' },
            { value: 'passengerIndividual', label: '个体客运量/周转量' }
          ]
        },
        {
          key: 'city', name: '城市客运', types: [
            { value: 'cityBus', label: '城市公交月报' },
            { value: 'cityTaxi', label: '巡游出租月报' },
            { value: 'cityBus', label: '城市公共交通月度运营情况' },
            { value: 'cityTaxi', label: '巡游出租汽车运营服务情况月报' },
            { value: 'cityTaxiAuth', label: '出租车运政车辆' }
          ]
        },
        {
          key: 'invest', name: '投资', types: [
          key: 'invest', name: '站场投资', types: [
            { value: 'investment', label: '投资月报(客运站场)' },
            { value: 'investmentLogistics', label: '投资月报(物流园区)' },
            { value: 'investmentSystem', label: '投资系统导出' },
@@ -199,9 +199,24 @@
          ]
        },
        {
          key: 'energy', name: '能耗', types: [
            { value: 'energyMonthly', label: 'H204能耗季报' },
          key: 'energy', name: '货运能耗季报', types: [
            { value: 'energyMonthly', label: '道路货运车辆能源消耗情况' },
            { value: 'energyAuth', label: '能耗车辆运政信息' }
          ]
        },
        {
          key: 'annual', name: '年报', types: [
            { value: 'annualCompanyBasic', label: '单位基本情况' },
            { value: 'annualEnterpriseFinance', label: '法人企业财务状况' },
            { value: 'annualPassengerEnergy', label: '公路旅客运输能源消耗情况' },
            { value: 'annualBusOperation', label: '公共汽电车运营情况' },
            { value: 'annualMetroVehicles', label: '城市轨道交通车辆及线路情况' },
            { value: 'annualMetroStaff', label: '城市轨道交通人员及运营情况' },
            { value: 'annualFerry', label: '城市客运轮渡运营情况' },
            { value: 'annualPublicEnergy', label: '公共交通能源消耗情况' },
            { value: 'annualCityInfrastructureOperators', label: '城市客运基础设施情况、城市客运经营业户情况' },
            { value: 'annualTaxiOwnership', label: '巡游出租汽车营运车辆拥有情况' },
            { value: 'annualTaxiProduction', label: '巡游出租汽车运输生产情况' }
          ]
        }
      ]
@@ -301,22 +316,31 @@
      return map[t] || ''
    },
    onFilePicked(key, file) {
      this.$set(this.files, this.moduleSel[key], file.raw)
      this.$set(this.fileNames, this.moduleSel[key], file.name)
      const type = this.moduleSel[key]
      if (!type) return
      const list = (this.files[type] || []).filter(f => f.name !== file.name)
      list.push(file.raw)
      this.$set(this.files, type, list)
      this.$set(this.fileNames, type, list.map(f => f.name))
      const up = this.$refs['up' + key]
      if (up && up.clearFiles) up.clearFiles()
    },
    importNow(key) {
      const type = this.moduleSel[key]
      if (!type) return
      if (this.isCityDirType(type)) {
      if (this.isDirOnlyType(type)) {
        this.importCityDir(type)
        this.moduleSel[key] = ''
        this.importType = ''
        return
      }
      const file = this.files[type]
      if (!file) { this.$message.warning('请先为该类型选择文件'); return }
      const fileList = this.files[type] || []
      if (!fileList.length) { this.$message.warning('请先为该类型选择文件'); return }
      if (fileList.length > 1 || type === 'investment' || type === 'investmentLogistics') {
        this.batchImport(type, key, fileList)
        return
      }
      const file = fileList[0]
      if (!this.period) { this.$message.warning('请先选择报表期'); return }
      const g = this.groups.find(x => x.key === key)
      const t = g.types.find(x => x.value === type)
@@ -342,10 +366,57 @@
        this.$delete(this.fileNames, type)
      })
    },
    fileCount(type) { return (this.files[type] || []).length },
    fileNamesText(type) {
      const list = this.fileNames[type] || []
      return list.length ? '已选 ' + list.length + ' 个文件:' + list.join('、') : ''
    },
    batchImport(type, key, fileList) {
      if (!this.period) { this.$message.warning('请先选择报表期'); return }
      const fd = new FormData()
      fd.append('type', type)
      fd.append('period', this.period)
      fileList.forEach(f => fd.append('files', f))
      const g = this.groups.find(x => x.key === key)
      const label = g ? g.name : type
      this.$message({ message: '正在批量导入 ' + fileList.length + ' 个文件…', duration: 3000 })
      api.post('/import/batch', fd, { timeout: 600000 })
        .then(res => {
          const d = (res && res.data) || {}
          if (!d.files) {
            this.$message.error((res && res.message) || '批量导入失败')
            return
          }
          const files = d.files || []
          const errCount = files.filter(f => f.error || f.success === 0).length
          const html = files.map(f =>
            '<div style="margin:6px 0;line-height:1.6"><b>' + (f.fileName || '') + '</b>' +
            (f.city ? '(' + f.city + ')' : '') + ':解析 ' + (f.success || 0) + ' 条' +
            (f.error ? ' <span style="color:#F56C6C">' + f.error + '</span>' : '') + '</div>'
          ).join('')
          this.$alert(html, (label || type) + ' 批量导入完成:共 ' + d.total + ' 个文件' +
            (d.fail > 0 ? ',失败 ' + d.fail + ' 条' : '') + '(成功 ' + d.success + ' 条)', {
            dangerouslyUseHTMLString: true,
            type: errCount > 0 ? 'warning' : 'success',
            confirmButtonText: '知道了'
          })
          if (type === 'investment' || type === 'investmentLogistics') {
            this.checkInvest(this.period)
          }
          this.afterImport()
          this.files[type] = null
          this.$delete(this.fileNames, type)
        })
        .catch(e => {
          if (e && e._toast) return
          this.$message.error((e.response && e.response.data && e.response.data.message) || '批量导入失败')
        })
    },
    isCityDirType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
    isDirOnlyType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' },
    importCityDir(type) {
      if (!this.period) { this.$message.warning('请先选择报表期'); return }
      const category = type === 'investCityPassenger' ? '客运站场' : '物流园区'
      const category = (type === 'investCityPassenger' || type === 'investment') ? '客运站场' : '物流园区'
      const fd = new FormData()
      fd.append('period', this.period)
      fd.append('category', category)