| | |
| | | <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"> |
| | |
| | | <table class="preview-summary" v-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: [] |
| | | }, |
| | | { |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | username() { return localStorage.getItem('username') || '' }, |
| | | realName() { return localStorage.getItem('realName') || '' }, |
| | | username() { return sessionStorage.getItem('username') || '' }, |
| | | realName() { return sessionStorage.getItem('realName') || '' }, |
| | | myModules() { |
| | | const s = localStorage.getItem('modules') |
| | | const s = sessionStorage.getItem('modules') |
| | | 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 m ? m.title : key |
| | | }, |
| | | logout() { |
| | | localStorage.removeItem('token'); localStorage.removeItem('username'); localStorage.removeItem('realName'); localStorage.removeItem('modules') |
| | | sessionStorage.removeItem('token'); sessionStorage.removeItem('username'); sessionStorage.removeItem('realName'); sessionStorage.removeItem('modules') |
| | | this.$router.push('/login') |
| | | }, |
| | | // ==================== 查询条件与状态刷新 ==================== |
| | |
| | | const m = this.curModule |
| | | if (!m || !this.period) return |
| | | const params = Object.assign(this.buildParams(), { types: m.items.map(i => i.key).join(',') }) |
| | | api.get('/report/ready', { params }) |
| | | api.get('/report/ready', { params, timeout: 60000 }) |
| | | .then(res => { |
| | | const list = (res && res.data && res.data.items) || [] |
| | | list.forEach(r => { |
| | |
| | | if (!items.length) { this.$message.warning('请选择要预览的报表'); return } |
| | | this.loading = 'preview-' + m.key |
| | | const params = Object.assign(this.buildParams(), { types: items.map(i => i.key).join(',') }) |
| | | api.get('/report/preview', { params }) |
| | | api.get('/report/preview', { params, timeout: 60000 }) |
| | | .then(res => { |
| | | const list = (res && res.data && res.data.items) || [] |
| | | this.previewItems = list.map(it => { |
| | |
| | | const params = this.buildParams() |
| | | const doExport = () => { |
| | | if (items.length === 1) { |
| | | api.get('/report/export/' + items[0].key, { params, responseType: 'blob' }) |
| | | api.get('/report/export/' + items[0].key, { params, responseType: 'blob', timeout: 120000 }) |
| | | .then(res => { this.downloadBlob(res, items[0].fileName); this.$message.success('报表导出成功'); this.loading = '' }) |
| | | .catch(err => this.failWith(err)) |
| | | return |
| | | } |
| | | const zipParams = Object.assign({}, params, { types: items.map(i => i.key).join(',') }) |
| | | api.get('/report/export/selected', { params: zipParams, responseType: 'blob' }) |
| | | api.get('/report/export/selected', { params: zipParams, responseType: 'blob', timeout: 120000 }) |
| | | .then(res => { this.downloadBlob(res, '生成_所选报表_' + this.period + '.zip'); this.$message.success('已导出 ' + items.length + ' 张报表(zip)'); this.loading = '' }) |
| | | .catch(err => this.failWith(err)) |
| | | } |
| | | const readyParams = Object.assign({}, params, { types: items.map(i => i.key).join(',') }) |
| | | api.get('/report/ready', { params: readyParams }) |
| | | api.get('/report/ready', { params: readyParams, timeout: 60000 }) |
| | | .then(rd => { |
| | | const list = (rd && rd.data && rd.data.items) || [] |
| | | const missing = list.filter(i => !i.ready) |
| | |
| | | .catch(() => doExport()) |
| | | }, |
| | | failWith(err) { |
| | | if (err && err._toast) { this.loading = ''; return } |
| | | let msg = '' |
| | | const d = err && err.response && err.response.data |
| | | if (d && typeof d === 'object' && d.message) msg = d.message |