traffic-audit-web/src/views/DataImport.vue
@@ -1,4 +1,4 @@
<template>
SKIP/CHECK (0): TYPE_LABELS upper <template>
  <el-container direction="vertical">
    <app-header title="交通统计报表审核系统 - 数据导入"></app-header>
    <el-container>
@@ -103,7 +103,11 @@
              <div v-show="sideOpen" class="side-content">
                <div class="side-head">
                  <span>本月导入进度({{ period || '未选择报表期' }})</span>
                  <el-button type="text" icon="el-icon-refresh" @click="loadData">刷新</el-button>
                  <span class="side-head-btns">
                    <el-button type="text" icon="el-icon-refresh" @click="loadData">刷新</el-button>
                    <el-button type="text" icon="el-icon-delete" style="color:#F56C6C" :loading="clearing"
                               :disabled="!period || clearing || importing" @click="clearPeriodData">清空</el-button>
                  </span>
                </div>
                <div v-if="!period" class="tip">请先在上方选择报表期</div>
                <div v-else class="overview-grid">
@@ -134,10 +138,11 @@
  investCityPassenger: '市州单表(客运站场)', investCityLogistics: '市州单表(物流园区)',
  'INVEST_客运站场_CITY': '市州单表(客运站场)', 'INVEST_物流园区_CITY': '市州单表(物流园区)',
  cityBus: '城市公交月报', cityTaxi: '巡游出租月报', cityTaxiAuth: '出租车运政车辆',
  wycTotal: '网约车总数', wycOrder: '网约车订单及全省总量',
  H2032: 'H203-2月报', H2031: 'H203-1旅客月报', H204: 'H204能耗季报', PASSENGER_AUTH: '旅客运政车辆数',
  PASSENGER_INDIVIDUAL: '个体客运量/周转量', ENERGY_AUTH: '能耗车辆运政信息', TRANSPORT_AUTH: '运政车辆',
  TRACK_MILEAGE: '轨迹里程', SCALE_SPLIT: '规上规下拆分', FREIGHT_TURNOVER: '货运量周转量',
  INVEST_SYSTEM: '投资系统导出', CITY_BUS: '城市公交月报', CITY_TAXI: '巡游出租月报', CITY_TAXI_AUTH: '出租车运政车辆'
  INVEST_SYSTEM: '投资系统导出', CITY_BUS: '城市公交月报', CITY_TAXI: '巡游出租月报', CITY_TAXI_AUTH: '出租车运政车辆', WYC_TOTAL: '网约车总数', WYC_ORDER: '网约车订单及全省总量'
}
const BATCH_TYPE_MAP = {
  H2032: 'h2032', TRANSPORT_AUTH: 'transportAuth', TRACK_MILEAGE: 'trackMileage',
@@ -145,6 +150,7 @@
  H2031: 'passengerMonthly', PASSENGER_AUTH: 'passengerAuth', PASSENGER_INDIVIDUAL: 'passengerIndividual',
  H204: 'energyMonthly', ENERGY_AUTH: 'energyAuth',
  CITY_BUS: 'cityBus', CITY_TAXI: 'cityTaxi', CITY_TAXI_AUTH: 'cityTaxiAuth',
  WYC_TOTAL: 'wycTotal', WYC_ORDER: 'wycOrder',
  INVEST_SYSTEM: 'investmentSystem'
}
export default {
@@ -156,6 +162,7 @@
      period: (() => { const d = new Date(); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') })(),
      cityDirLoading: false,
      importing: false,
      clearing: false,
      sideOpen: false,
      result: '',
      error: '',
@@ -187,7 +194,9 @@
          key: 'city', name: '城市客运', types: [
            { value: 'cityBus', label: '城市公共交通月度运营情况' },
            { value: 'cityTaxi', label: '巡游出租汽车运营服务情况月报' },
            { value: 'cityTaxiAuth', label: '出租车运政车辆' }
            { value: 'cityTaxiAuth', label: '出租车运政车辆' },
            { value: 'wycTotal', label: '网约车总数' },
            { value: 'wycOrder', label: '网约车订单及全省总量' }
          ]
        },
        {
@@ -247,7 +256,7 @@
        scaleSplit: '模板_规上规下拆分运输量.xlsx',
        freightTurnover: '模板_货运量周转量.xlsx',
        passengerMonthly: '公路旅客运输月度生产情况.xlsx(含运政车辆数sheet)',
        passengerAuth: '模板_公路旅客运政车辆数.xlsx(列:企业代码/企业名称/车辆数/载客位数)',
        passengerAuth: '模板_公路旅客运政车辆数.xls(支持 企业代码/企业名称/车辆数/载客位数 与 运政库导出 OWNERNAME/总车辆数/客位数)',
        passengerIndividual: '个体客运量汇总.xlsx(列:所属地区/企业代码/企业名称/个体客运量/个体旅客周转量)',
        energyMonthly: '道路货运车辆能源消耗情况(2026年).xlsx(能耗明细+运政车辆信息两sheet)',
        energyAuth: '模板_能耗车辆运政信息.xlsx(列:车牌号/车辆类型/燃料类型/标记吨位/准牵引质量)',
@@ -258,7 +267,9 @@
        investCityLogistics: '—(批量扫描目录,无需模板)',
        cityBus: '导入_城市公共交通月度运营情况(YYYY年M月).xls/.xlsx(企业级明细,取公共汽电车字段)',
        cityTaxi: '导入_巡游出租汽车运营服务情况月报(YYYY年M月).xls(市州级17行)',
        cityTaxiAuth: '导入模板_出租车信息(运政车辆信息).xlsx(车辆级明细,用于运营车数对比)'
        cityTaxiAuth: '导入模板_出租车信息(运政车辆信息).xlsx(车辆级明细,用于运营车数对比)',
        wycTotal: '模板_网约车总数.xlsx(A列=月份,B-E=全省客运量/城市内/周转量/城市内周转量)',
        wycOrder: '模板_网约车订单及全省总量.xlsx(A列=月份,B-E=全省pin,F=订单合计,G-W=17市州订单)'
      }
      return map[this.importType] || ''
    },
@@ -270,13 +281,15 @@
        scaleSplit: '货运/模板_规上规下拆分运输量.xlsx',
        freightTurnover: '货运/模板_货运量周转量.xlsx',
        passengerMonthly: '公路旅客/模板_交企统H203-1表_公路旅客运输月度生产情况.xlsx',
        passengerAuth: '公路旅客/模板_公路旅客运政车辆数.xlsx',
        passengerAuth: '公路旅客/模板_公路旅客运政车辆数.xls',
        passengerIndividual: '公路旅客/个体客运量汇总.xlsx',
        energyMonthly: '能耗/模板_道路货运车辆能源消耗情况(2026).xlsx',
        energyAuth: '能耗/模板_能耗车辆运政信息.xlsx',
        cityBus: '城市客运/生成_城市公交客运量分市州明细.xlsx',
        cityTaxi: '城市客运/生成_巡游出租客运量分市州明细.xlsx',
        cityTaxiAuth: '城市客运/导入模板_出租车信息(运政车辆信息).xlsx',
        wycTotal: '城市客运/模板_网约车总数.xlsx',
        wycOrder: '城市客运/模板_网约车订单及全省总量.xlsx',
        investment: '投资/输入/客运(客运站)/更新_7月客运站投资明细表(市州汇总大表,给领导).xlsx',
        investmentLogistics: '投资/输入/物流(物流园区)/更新_7月物流站场投资明细.xls',
        investmentSystem: '投资/模板_查询结果(投资系统-客运+物流 2026.7).xls'
@@ -314,7 +327,9 @@
        investmentSystem: '/import/investmentSystem',
        cityBus: '/import/cityBus',
        cityTaxi: '/import/cityTaxi',
        cityTaxiAuth: '/import/cityTaxiAuth'
        cityTaxiAuth: '/import/cityTaxiAuth',
        wycTotal: '/import/wycTotal',
        wycOrder: '/import/wycOrder'
      }
      return map[t] || ''
    },
@@ -362,6 +377,7 @@
        message: ''
      }
      this.importOne(row).then(() => {
        this.showImportResult(row)
        if ((row.type === 'investment' || row.type === 'investmentLogistics') && row.status === 'success') {
          this.checkInvest(this.period)
        }
@@ -456,6 +472,20 @@
        })
        .finally(() => { this.cityDirLoading = false })
    },
    showImportResult(row) {
      if (!row || !row.status) return
      const msg = (row.message || '').toString()
      if (row.status === 'success') {
        const text = (msg && msg.indexOf('导入') !== -1) ? msg : '数据导入成功'
        this.$message({ message: text, type: 'success', duration: 5000 })
      } else {
        this.$alert((msg || '数据导入失败,详见下方失败记录').replace(/\n/g, '<br>'), '数据导入失败', {
          dangerouslyUseHTMLString: true,
          type: 'error',
          confirmButtonText: '知道了'
        })
      }
    },
    importOne(row) {
      if (!this.period) { this.$message.warning('请先选择报表期'); return Promise.resolve() }
      row.status = 'uploading'
@@ -494,6 +524,28 @@
        })
        .catch(e => { if (!e || !e._toast) this.$message.error('导入记录加载失败,请刷新重试') })
        .finally(() => { this.batchLoading = false })
    },
    clearPeriodData() {
      if (!this.period) { this.$message.warning('请先选择报表期'); return }
      if (this.importing) { this.$message.warning('正在导入数据,请等待导入完成后再清空'); return }
      if (this.clearing) return
      this.$confirm(
        '将清空 ' + this.period + ' 报表期在数据库中的全部已导入数据,同时删除该报表期的审核结果与导入批次记录(便于整月重新导入)。\n此操作不可撤销,是否继续?',
        '清空 ' + this.period + ' 数据',
        { type: 'warning', confirmButtonText: '确认清空', cancelButtonText: '取消' }
      ).then(() => {
        this.clearing = true
        api.post('/import/clear', null, { params: { period: this.period }, timeout: 600000 })
          .then(() => {
            this.$message.success('已清空 ' + this.period + ' 报表期的数据,可以重新导入')
            this.loadData()
          })
          .catch(e => {
            if (e && e._toast) return
            this.$message.error((e.response && e.response.data && e.response.data.message) || '清空失败,请稍后重试')
          })
          .finally(() => { this.clearing = false })
      }).catch(() => {})
    },
    batchKeyToType(k) {
      if (BATCH_TYPE_MAP[k]) return BATCH_TYPE_MAP[k]
@@ -566,6 +618,8 @@
.import-side.side-open .side-toggle-text { writing-mode: horizontal-tb; letter-spacing: normal; margin-top: 0; }
.side-content { padding: 8px 12px 12px; max-height: calc(100vh - 220px); overflow-y: auto; overscroll-behavior: contain; }
.side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-weight: 600; margin-bottom: 6px; }
.side-head-btns { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.side-head-btns .el-button + .el-button { margin-left: 0; }
.import-side .overview-grid { display: block; }
.import-side .ov-row { display: block; margin-bottom: 6px; }
.import-side .ov-group { width: auto; }