| | |
| | | <app-header title="交通统计报表审核系统 - 数据导入"></app-header> |
| | | <el-container> |
| | | <el-aside width="200px"> |
| | | <el-menu router default-active="/import"> |
| | | <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> |
| | |
| | | |
| | | <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"> |
| | |
| | | <div class="card-head">选择类型并挂载文件 |
| | | <span class="tip" style="float:right;line-height:24px">各模块互不影响:先选类型、挂载文件,可「立即导入」或「加入队列」统一执行;也可在下方粘贴目录路径点「导入该目录」,文件夹里多个月的 Excel 会按文件名自动识别月份、分别导入对应报表期</span> |
| | | </div> |
| | | <el-tabs v-model="activeModule" class="module-tabs"> |
| | | <el-tab-pane v-for="g in groups" :key="g.key" :label="g.name" :name="g.key"> |
| | | <div class="module-tabs-head"> |
| | | <el-radio-group v-model="activeModule" size="small"> |
| | | <el-radio-button v-for="g in groups" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | <div v-for="g in groups" :key="g.key" v-show="activeModule === g.key" class="module-tab-pane"> |
| | | <div class="module-sel"> |
| | | <el-select v-model="moduleSel[g.key]" placeholder="选择导入类型" style="width:260px" |
| | | @change="onModuleChange(g.key)" clearable> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | <div v-if="importType" class="type-info"> |
| | | <div>模板:{{ templateName }} |
| | | <el-link v-if="templateUrl" type="primary" icon="el-icon-download" :underline="false" |
| | |
| | | <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> |
| | |
| | | <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"> |
| | |
| | | dirCurrent: '', |
| | | dirParent: '', |
| | | dirDirs: [], |
| | | dirDirsTruncated: false, |
| | | dirExcelCount: 0, |
| | | dirFilesTruncated: false, |
| | | dirFiles: [], |
| | | dirLoading: false, |
| | | dirImporting: false, |
| | |
| | | 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', |
| | |
| | | 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) }, |
| | |
| | | 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: '' })) |
| | |
| | | }) |
| | | .catch(e => { |
| | | this.dirImporting = false |
| | | if (e && e._toast) return |
| | | this.$message.error((e.response && e.response.data && e.response.data.message) || '目录不存在或无法访问:' + dir) |
| | | }) |
| | | }, |
| | |
| | | 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 }) |
| | |
| | | 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 |
| | |
| | | }) |
| | | 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] |
| | |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.loadBatches() |
| | | this.loadOverview() |
| | | this.loadData() |
| | | } |
| | | } |
| | | </script> |
| | |
| | | .el-header { background: #409EFF; color: white; line-height: 60px; font-size: 18px; } |
| | | .import-card { margin-bottom: 0; } |
| | | .card-head { font-weight: 600; margin-bottom: 6px; } |
| | | .module-tabs >>> .el-tabs__header { margin-bottom: 12px; } |
| | | .module-sel { max-width: 480px; } |
| | | .group-label { font-size: 13px; color: #606266; font-weight: 600; margin-bottom: 6px; padding-left: 6px; border-left: 3px solid #409EFF; } |
| | | .module-file { margin-top: 8px; } |
| | |
| | | .dir-empty { padding: 14px; font-size: 12px; color: #909399; text-align: center; } |
| | | .dir-files { margin-top: 8px; } |
| | | |
| | | .module-tabs-head { margin-bottom: 10px; } |
| | | .module-tab-pane { min-height: 120px; } |
| | | </style> |