zhizhijie
9 天以前 654556a05394481b138b1070d83a22dbf311e94b
traffic-audit-web/src/views/AuditResult.vue
@@ -50,7 +50,9 @@
          <div class="sev-card sev-pending"><b>{{ severityStats.pending }}</b> 待处理</div>
          <div class="sev-card sev-done"><b>{{ severityStats.done }}</b> 已处理</div>
        </div>
        <el-form v-if="!focusMode" :inline="true" style="margin-top:8px;flex:none">
        <div v-if="!focusMode" class="audit-filter-bar">
        <el-form :inline="true">
          <el-form-item label="筛选"></el-form-item>
          <el-form-item label="企业名称">
            <el-input v-model="enterpriseFilter" placeholder="输入企业名称关键字" clearable style="width:200px" @keyup.enter.native="queryResults"></el-input>
          </el-form-item>
@@ -59,11 +61,12 @@
              <el-option v-for="rn in ruleOptions" :key="rn" :label="rn" :value="rn"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="问题类型">
          <el-form-item label="问题范围">
            <el-radio-group v-model="issueScope" size="small" @change="updateBatchSelection">
              <el-radio-button label="verify">核实型</el-radio-button>
              <el-radio-button label="all">全部</el-radio-button>
              <el-radio-button label="verify">仅需核实</el-radio-button>
              <el-radio-button label="all">含提醒型</el-radio-button>
            </el-radio-group>
            <span class="hint">提醒型仅提示变化、不需处理,导出默认不含</span>
          </el-form-item>
          <el-form-item label="状态">
            <el-select v-model="statusScope" style="width:110px" @change="updateBatchSelection">
@@ -87,9 +90,11 @@
            <el-button size="small" plain @click="statsCollapsed = !statsCollapsed">{{ statsCollapsed ? '展开' : '收起' }}规则统计</el-button>
          </el-form-item>
          <el-form-item>
            <span style="color:#909399;font-size:12px">共 {{ results.length }} 条问题,{{ mergedResults.length }} 家企业</span>
            <span style="color:#303133;font-size:13px;font-weight:600">当前筛选 {{ filteredResults.length }} 条 / {{ mergedResults.length }} 家企业</span>
            <span style="color:#909399;font-size:12px;margin-left:8px">(共 {{ results.length }} 条问题)</span>
          </el-form-item>
        </el-form>
        </div>
        <div v-if="ruleStats.length && !statsCollapsed && !focusMode" class="rule-stats" style="flex:none">
          <div class="stats-cards">
            <div v-for="rs in sortedRuleStats" :key="rs.ruleId" class="stat-card"
@@ -354,7 +359,7 @@
  data() {
    return {
      period: '', reportType: 'H2032', loading: false, marking: false, results: [], ruleStats: [], statsCollapsed: false,
      enterpriseFilter: '', ruleFilter: '', viewMode: 'detail', issueScope: 'verify', statusScope: 'all', batchSelection: [], focusMode: false,
      enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', issueScope: 'verify', statusScope: 'all', batchSelection: [], focusMode: false,
      explainDrafts: {}, savingExplainId: null, expandedExplain: null,
      monthVisible: false, monthLoading: false, monthType: '', monthPeriod: '', monthKeyword: '', monthRecords: [], monthColumns: []
    }
@@ -367,7 +372,7 @@
    },
    severityStats() {
      const s = { red: 0, yellow: 0, pending: 0, done: 0 }
      this.results.forEach(r => {
      this.filteredResults.forEach(r => {
        if (r.alertLevel === 'RED') s.red++
        else if (r.alertLevel === 'YELLOW') s.yellow++
        if (r.status === 'PENDING') s.pending++
@@ -383,9 +388,6 @@
    someSelected() {
      const list = this.filteredResults
      return list.some(r => r._selected) && !this.allSelected
    },
    isReminderRow(r) {
      return !!(r && r.ruleCode === 'RULE_004')
    },
    filteredResults() {
      const kw = (this.enterpriseFilter || '').trim()
@@ -424,6 +426,9 @@
    }
  },
  methods: {
    isReminderRow(r) {
      return !!(r && r.ruleCode === 'RULE_004')
    },
    dataType(t) { return DATA_TYPE_MAP[t] || '' },
    explainType(t) { return EXPLAIN_TYPES.indexOf(t) >= 0 },
    openMonthReport(row) {
@@ -604,8 +609,26 @@
    },
    exportExcel() {
      if (!this.period) { this.$message.warning('请选择报表期'); return }
      const q = 'period=' + this.period + '&reportType=' + this.reportType + '&onlyVerify=' + (this.issueScope !== 'all') + '&status=' + (this.statusScope || 'all')
      window.open('/api/audit/export?' + q)
      const params = {
        period: this.period,
        reportType: this.reportType,
        onlyVerify: this.issueScope !== 'all',
        status: this.statusScope || 'all'
      }
      api.get('/audit/export', { params, responseType: 'blob' })
        .then(res => this.downloadBlob(res, '审核结果_' + this.period + '.xlsx'))
        .catch(() => this.$message.error('导出失败:请确认该报表期已有审核结果'))
    },
    downloadBlob(res, fileName) {
      const blob = res instanceof Blob ? res : new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
      const url = window.URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = fileName
      document.body.appendChild(a)
      a.click()
      window.URL.revokeObjectURL(url)
      document.body.removeChild(a)
    },
    review(row, status) {
      if (status === 'IGNORED') {
@@ -769,4 +792,9 @@
.mr-sec-tab th { background: #F5F7FA; font-weight: 600; }
.mr-note { margin: 6px 12px 12px; background: #FDF6EC; border: 1px solid #FAECD8; border-radius: 4px; padding: 8px 10px; font-size: 12px; color: #8a6d3b; white-space: pre-wrap; word-break: break-all; }
.mr-note-line { margin: 2px 0; }
.audit-filter-bar { background:#FAFAFA; border:1px solid #EBEEF5; border-radius:8px; padding:2px 12px 0; margin-top:8px; flex:none; }
.audit-filter-bar .el-form-item { margin-bottom:6px; }
.audit-filter-bar .el-form-item__label { color:#606266; font-weight:600; }
.hint { color:#909399; font-size:12px; margin-left:6px; white-space:nowrap; }
</style>