traffic-audit-web/src/views/DataImport.vue
@@ -1,6 +1,6 @@
<template>
  <el-container>
    <el-header>交通统计报表审核系统 - 数据导入</el-header>
  <el-container direction="vertical">
    <app-header title="交通统计报表审核系统 - 数据导入"></app-header>
    <el-container>
      <el-aside width="200px">
        <el-menu router default-active="/import">
@@ -12,7 +12,6 @@
          <el-menu-item index="/report">报表生成</el-menu-item>
          <el-menu-item index="/rules">规则管理</el-menu-item>
          <el-menu-item index="/users">用户管理</el-menu-item>
          <el-menu-item index="/login" @click="logout">退出登录</el-menu-item>
</el-menu>
      </el-aside>
      <el-main>
@@ -27,7 +26,7 @@
          <el-card shadow="hover" class="import-card" style="margin-bottom:15px">
            <div class="card-head">本月导入进度({{ period || '未选择报表期' }})
              <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadOverview">刷新</el-button>
              <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadData">刷新</el-button>
            </div>
            <div v-if="!period" class="tip">请先在上方选择报表期</div>
            <div v-else class="overview-grid">
@@ -159,6 +158,8 @@
              <div class="tip">单击文件夹进入子文件夹;<b>双击目标文件夹立即导入</b>其中全部 Excel;也可进入后点下方按钮。导入时会按文件名自动识别月份。</div>
              <div v-if="dirExcelCount === 0" class="tip">当前文件夹内没有 Excel 文件(.xlsx/.xls)</div>
              <div v-else class="tip">当前文件夹内 Excel:{{ dirExcelCount }} 个 —— {{ dirFiles.slice(0, 6).join('、') }}{{ dirFiles.length > 6 ? ' 等' : '' }}(仅导入直接位于该文件夹的文件)</div>
              <div v-if="dirDirsTruncated" class="tip">子文件夹过多,仅显示前 300 个</div>
              <div v-if="dirFilesTruncated" class="tip">Excel 文件过多(共 {{ dirExcelCount }} 个),仅显示前 300 个</div>
            </div>
            <span slot="footer">
              <el-button size="small" @click="dirDialog = false">取消</el-button>
@@ -169,7 +170,7 @@
          <el-card shadow="never" style="margin-top:15px" class="batch-card">
            <div slot="header" class="card-head">
              导入记录({{ period || '全部报表期' }})
              <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadBatches">刷新</el-button>
              <el-button type="text" icon="el-icon-refresh" style="float:right" @click="loadData">刷新</el-button>
            </div>
            <el-table :data="batches" border size="small" v-loading="batchLoading" style="width:100%">
              <el-table-column prop="importType" label="导入类型" width="150">
@@ -206,6 +207,7 @@
  </el-container>
</template>
<script>
import AppHeader from '@/components/AppHeader.vue'
import api from '@/api'
const TYPE_LABELS = {
  h2032: 'H203-2月报', transportAuth: '运政车辆', trackMileage: '轨迹里程', scaleSplit: '规上规下拆分',
@@ -229,6 +231,7 @@
  INVEST_SYSTEM: 'investmentSystem'
}
export default {
  components: { AppHeader },
  name: 'DataImport',
  data() {
    return {
@@ -242,7 +245,9 @@
      dirCurrent: '',
      dirParent: '',
      dirDirs: [],
      dirDirsTruncated: false,
      dirExcelCount: 0,
      dirFilesTruncated: false,
      dirFiles: [],
      dirLoading: false,
      dirImporting: false,
@@ -389,7 +394,7 @@
      this.importType = this.moduleSel[key] || ''
      this.dirPath = (this.importType && localStorage.getItem('dirLast_' + this.importType)) || ''
    },
    onPeriodChange() { this.loadOverview(); this.loadBatches() },
    onPeriodChange() { this.loadData() },
    endpoint(t) {
      const map = {
        h2032: '/import/h2032',
@@ -427,10 +432,12 @@
          this.dirCurrent = d.current || ''
          this.dirParent = d.parent || ''
          this.dirDirs = d.dirs || []
          this.dirDirsTruncated = !!d.dirsTruncated
          this.dirExcelCount = d.excelCount || 0
          this.dirFiles = d.files || []
          this.dirFilesTruncated = !!d.filesTruncated
        })
        .catch(e => { this.$message.error((e.response && e.response.data && e.response.data.message) || '目录加载失败') })
        .catch(e => { if (e && e._toast) return; this.$message.error((e.response && e.response.data && e.response.data.message) || '目录加载失败') })
        .finally(() => { this.dirLoading = false })
    },
    goDirParent() { if (this.dirParent) this.loadDirs(this.dirParent) },
@@ -457,6 +464,11 @@
          const d = (res && res.data) || {}
          const files = (d.files || []).filter(n => /\.(xlsx|xls)$/i.test(n))
          if (!files.length) { this.$message.warning('该目录没有 Excel 文件(.xlsx/.xls):' + dir); this.dirImporting = false; return }
          if (files.length > 60) {
            this.dirImporting = false
            this.$message.warning('目录内 Excel 文件过多(' + files.length + ' 个),为避免浏览器卡顿请分批导入(每批不超过 60 个)')
            return
          }
          localStorage.setItem('dirLast_' + type, dir)
          this.dirPath = dir
          this.dirImportFiles = files.map(n => ({ name: n, status: 'pending', period: '', msg: '' }))
@@ -493,6 +505,7 @@
        })
        .catch(e => {
          this.dirImporting = false
          if (e && e._toast) return
          this.$message.error((e.response && e.response.data && e.response.data.message) || '目录不存在或无法访问:' + dir)
        })
    },
@@ -573,6 +586,7 @@
          this.afterImport()
        })
        .catch(e => {
          if (e && e._toast) return
          this.$message.error((e.response && e.response.data && e.response.data.message) || '批量导入失败')
        })
        .finally(() => { this.cityDirLoading = false })
@@ -643,13 +657,17 @@
        run(0)
      })
    },
    afterImport() { this.loadOverview(); this.loadBatches() },
    loadOverview() {
      if (!this.period) return
      api.get('/import/batches', { params: { period: this.period, limit: 200 } })
    afterImport() { this.loadData() },
    loadData() {
      if (this.batchLoading) return
      this.batchLoading = true
      const params = { limit: 200 }
      if (this.period) params.period = this.period
      api.get('/import/batches', { params })
        .then(res => {
          const map = {}
          const records = (res.data && res.data.records) || []
          this.batches = records.slice(0, 50)
          const map = {}
          records.forEach(r => {
            const key = this.batchKeyToType(r.importType)
            if (!key) return
@@ -660,7 +678,8 @@
          })
          this.overview = map
        })
        .catch(() => {})
        .catch(e => { if (!e || !e._toast) this.$message.error('导入记录加载失败,请刷新重试') })
        .finally(() => { this.batchLoading = false })
    },
    batchKeyToType(k) {
      if (BATCH_TYPE_MAP[k]) return BATCH_TYPE_MAP[k]
@@ -712,8 +731,7 @@
    }
  },
  mounted() {
    this.loadBatches()
    this.loadOverview()
    this.loadData()
  }
}
</script>