| | |
| | | <app-header title="交通统计报表审核系统 - 报表生成"></app-header> |
| | | <el-container> |
| | | <el-aside width="200px"> |
| | | <el-menu router :default-active="activeMenu"> |
| | | <el-menu ref="sideMenu" router :default-active="$route.path" @select="onMenuSelect"> |
| | | <el-menu-item index="/dashboard">工作台</el-menu-item> |
| | | <el-menu-item index="/import">数据导入</el-menu-item> |
| | | <el-menu-item index="/data">数据查看</el-menu-item> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 第二行:报表类型 Tab(货运 / 旅客 / 投资 / 能耗 / 城市客运) --> |
| | | <el-tabs v-model="activeKey" class="module-tabs"> |
| | | <el-tab-pane v-for="m in modules" :key="m.key" :name="m.key"> |
| | | <span slot="label">{{ m.title }}<el-tag v-if="m.tag" size="mini" :type="m.tagType || 'info'" style="margin-left:6px">{{ m.tag }}</el-tag></span> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | <!-- 第二行:报表类型切换(货运 / 旅客 / 投资 / 能耗 / 城市客运);el-radio-group 替代 el-tabs,规避 el-tabs 布局无限循环(与 /import 修复一致) --> |
| | | <el-radio-group v-model="activeKey" class="module-tabs"> |
| | | <el-radio-button v-for="m in visibleModules" :key="m.key" :label="m.key"> |
| | | {{ m.title }}<el-tag v-if="m.tag" size="mini" :type="m.tagType || 'info'" style="margin-left:6px">{{ m.tag }}</el-tag> |
| | | </el-radio-button> |
| | | </el-radio-group> |
| | | |
| | | <!-- 第三行:查询条件区(周期 + 月份 + 查询) --> |
| | | <section class="query-panel"> |
| | |
| | | <span v-if="!it.ready" class="cov-bad" style="margin-left:10px">{{ it.note }}</span> |
| | | <span v-if="it.ready && it.audited === false" class="cov-bad" style="margin-left:10px">未审核</span> |
| | | </div> |
| | | <table class="preview-summary" v-if="it.summary && it.summary.length"> |
| | | <table class="preview-summary wb-table" v-if="it.key === 'summaryWorkbook' && it.summary && it.summary.length"> |
| | | <thead> |
| | | <tr><th style="width:44%">指标</th><th style="width:30%">数据</th><th>同比</th></tr> |
| | | </thead> |
| | | <tbody> |
| | | <template v-for="(sec, si) in wbRows(it.summary)"> |
| | | <tr v-for="(row, ri) in sec.rows" :key="si + '-' + ri"> |
| | | <td v-if="ri === 0" :rowspan="sec.rows.length" class="wb-sec">{{ sec.name }}</td> |
| | | <td class="wb-label">{{ row.label }}</td> |
| | | <td class="wb-value">{{ row.value === null ? '—' : fmtVal(row.value) }}</td> |
| | | <td class="wb-yoy" :class="{ 'yoy-empty': row.yoy === null }">{{ row.yoy === null ? '—' : row.yoy }}</td> |
| | | </tr> |
| | | </template> |
| | | </tbody> |
| | | </table> |
| | | <div v-if="it.key === 'summaryWorkbook' && wbMissing(it.summary)" class="wb-note"> |
| | | 部分指标缺少上年同期({{ wbPrevYear() }})基期数据,同比暂未计算 |
| | | </div> |
| | | <table class="preview-summary" v-else-if="it.summary && it.summary.length"> |
| | | <tbody> |
| | | <tr v-for="(s, i) in it.summary" :key="i"> |
| | | <td>{{ s.name }}</td> |
| | | <td>{{ s.label }}</td> |
| | | <td><span v-if="s.value === null || s.value === ''" class="cell-empty">(空)</span>{{ fmtVal(s.value) }}</td> |
| | | <td style="white-space:nowrap;color:#909399">{{ s.unit || '' }}</td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | |
| | | data() { |
| | | return { |
| | | loading: '', |
| | | activeMenu: '/report', |
| | | activeKey: '', |
| | | updatedAt: '', |
| | | // 查询条件:cycle 周期(month 月报 / quarter 季报 / year 年报),period 报表期(月份 yyyy-MM 或年份 yyyy) |
| | |
| | | noteDraft: '', |
| | | modules: [ |
| | | { |
| | | key: 'freight', title: '货运报表', items: [ |
| | | key: 'summary', title: '汇总大表', items: [ |
| | | { key: 'summaryWorkbook', label: '道路运输量汇总表(整本)', fileName: '生成_道路运输量汇总表.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, |
| | | { |
| | | key: 'freight', title: '货运报表', hidden: true, items: [ |
| | | { key: 'cityDetail', label: '货运量分市州明细', fileName: '生成_货运量分市州明细.xlsx', status: null, statusNote: '' }, |
| | | { key: 'freightRank', label: '货运量排名', fileName: '生成_货运量排名.xlsx', status: null, statusNote: '' }, |
| | | { key: 'turnoverRank', label: '周转量排名', fileName: '生成_周转量排名.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, |
| | | { |
| | | key: 'passenger', title: '旅客报表', items: [ |
| | | key: 'passenger', title: '旅客报表', hidden: true, items: [ |
| | | { key: 'passengerCityDetail', label: '公路旅客分市州', fileName: '生成_公路旅客分市州.xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidDetail', label: '中口径明细', fileName: '生成_公路旅客中口径明细.xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidRank', label: '中口径排名', fileName: '生成_公路旅客中口径排名.xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidAnalysis', label: '中口径分析', fileName: '生成_公路旅客中口径分析.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, { |
| | | key: 'cityPassenger', title: '城市客运报表', tag: '公交/出租/轨道轮渡/汇总', tagType: 'success', items: [ |
| | | key: 'cityPassenger', title: '城市客运报表', hidden: true, items: [ |
| | | { key: 'cityBusDetail', label: '城市公交客运量分市州明细', fileName: '生成_城市公交客运量分市州明细.xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityTaxiDetail', label: '巡游出租客运量分市州明细', fileName: '生成_巡游出租客运量分市州明细.xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityRailFerryDetail', label: '轨道轮渡客运量分市州明细', fileName: '生成_轨道轮渡客运量分市州明细.xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityPassengerCitySum', label: '城市客运客运量分市州累计', fileName: '生成_城市客运客运量分市州累计.xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityPassengerDetailSum', label: '城市客运各市州明细表', fileName: '生成_城市客运各市州明细表.xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityPassengerSummary', label: '城市客运汇总', fileName: '生成_城市客运汇总.xlsx', status: null, statusNote: '' } |
| | | { key: 'cityPassengerSummary', label: '城市客运汇总', fileName: '生成_城市客运汇总.xlsx', status: null, statusNote: '' }, |
| | | { key: 'wycSplit', label: '网约车客运量拆分表', fileName: '生成_网约车客运量拆分表.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, |
| | | { |
| | |
| | | if (!s) return [] |
| | | return s.split(',').filter(Boolean) |
| | | }, |
| | | visibleModules() { |
| | | return this.modules.filter(m => !m.hidden) |
| | | }, |
| | | curModule() { |
| | | return this.modules.find(m => m.key === this.activeKey) || this.modules[0] |
| | | }, |
| | |
| | | // 报表期下拉分组:年报按年份;月报/季报按年分组列 1-12 月(能耗只列季度末月) |
| | | periodGroups() { |
| | | const year = new Date().getFullYear() |
| | | const years = [year - 1, year, year + 1] |
| | | const years = [year, year - 1, year + 1] |
| | | if (this.cycle === 'year') { |
| | | return [{ label: '选择年份', options: years.map(y => ({ label: y + '年', value: String(y) })) }] |
| | | } |
| | |
| | | } |
| | | }, |
| | | created() { |
| | | const mine = this.myModules.filter(k => this.modules.some(m => m.key === k)) |
| | | this.activeKey = (mine.length ? mine : this.modules.map(m => m.key))[0] |
| | | const mine = this.myModules.filter(k => this.visibleModules.some(m => m.key === k)) |
| | | this.activeKey = (mine.length ? mine : this.visibleModules.map(m => m.key))[0] |
| | | this.ensureCycle() |
| | | this.refreshAll() |
| | | }, methods: { |
| | |
| | | } |
| | | return String(v) |
| | | }, |
| | | wbRows(summary) { |
| | | const get = (key) => { |
| | | const hit = (summary || []).find(x => x.label === key) |
| | | return hit && hit.value !== null && hit.value !== undefined && hit.value !== '' ? hit.value : null |
| | | } |
| | | const pct = (key) => { |
| | | const v = get(key) |
| | | return v === null ? null : this.fmtVal(v) + '%' |
| | | } |
| | | const mk = (defs) => defs.map(d => ({ |
| | | label: d[0], |
| | | value: get(d[1]), |
| | | yoy: d[2] ? pct(d[2]) : null |
| | | })) |
| | | return [ |
| | | { |
| | | name: '道路货运', |
| | | rows: mk([ |
| | | ['当月货运量(万吨)', '全省货运量·当月'], |
| | | ['当月货物周转量(万吨)', '全省货物周转量·当月', '全省货物周转量·当月同比'], |
| | | ['累计货运量(万吨)', '全省货运量·累计'], |
| | | ['累计货物周转量(万吨公里)', '全省货物周转量·累计', '全省货物周转量·累计同比'] |
| | | ]) |
| | | }, |
| | | { |
| | | name: '城市客运', |
| | | rows: mk([ |
| | | ['当月城市客运量:', '城市客运量·当月'], |
| | | ['当月城市客运周转量', '城市客运周转量·当月'], |
| | | ['累计城市客运量:', '城市客运量·累计'], |
| | | ['累计城市客运周转量', '城市客运周转量·累计'] |
| | | ]) |
| | | }, |
| | | { |
| | | name: '中口径', |
| | | rows: mk([ |
| | | ['当月中口径客运量', '中口径客运量·当月'], |
| | | ['当月中口径周转量', '中口径周转量·当月'], |
| | | ['累计中口径客运量', '中口径客运量·累计'], |
| | | ['累计中口径周转量', '中口径周转量·累计'] |
| | | ]) |
| | | } |
| | | ] |
| | | }, |
| | | wbMissing(summary) { |
| | | let total = 0 |
| | | let have = 0 |
| | | ;(summary || []).forEach(x => { |
| | | if (String(x.label).indexOf('同比') >= 0) return |
| | | total++ |
| | | if (x.value !== null && x.value !== undefined && x.value !== '') have++ |
| | | }) |
| | | return total > 0 && have < total |
| | | }, |
| | | wbPrevYear() { |
| | | const m = /^(\d{4})-(\d{2})$/.exec(this.previewPeriod || '') |
| | | if (!m) return '上年同期' |
| | | return (Number(m[1]) - 1) + '-' + m[2] |
| | | }, |
| | | coverageText(it) { |
| | | const cov = it.monthCoverage || [] |
| | | if (!cov.length) return '无数据' |
| | |
| | | @media (max-width: 900px) { |
| | | .panel-head { flex-direction: column; align-items: flex-start; } |
| | | } |
| | | .wb-table th { background:#f5f7fa; } |
| | | .wb-sec { background:#F7F9FC; font-weight:600; text-align:center; color:#303133; } |
| | | .wb-label { color:#303133; } |
| | | .wb-value { text-align:right; font-variant-numeric: tabular-nums; } |
| | | .wb-yoy { text-align:right; color:#606266; } |
| | | .wb-yoy.yoy-empty { color:#C0C4CC; } |
| | | .wb-note { color:#E6A23C; font-size:12px; margin:2px 0 8px; } |
| | | </style> |