refactor(wms-report): 统一报表页面模板,提取公共逻辑为action-template
1. 将merge、repair下的多个报表页面重构为使用action-template组件 2. 提取自定义导出功能为公共组件CustomExport并复用至receive.vue和action-template 3. 统一报表页面的查询、导出、列配置等公共逻辑
This commit is contained in:
@@ -86,66 +86,18 @@
|
||||
<columns-setting :reportType="activeColumnConfig"></columns-setting>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 自定义导出列选择弹窗 -->
|
||||
<el-dialog title="自定义导出 - 选择导出列" :visible.sync="customExportVisible" width="850px" top="5vh">
|
||||
<div class="custom-export-toolbar">
|
||||
<el-input v-model="columnSearch" placeholder="搜索列名" prefix-icon="el-icon-search" clearable size="small" style="width: 200px" />
|
||||
<div class="custom-export-actions">
|
||||
<el-button size="small" @click="selectAllColumns">全选</el-button>
|
||||
<el-button size="small" @click="invertColumns">反选</el-button>
|
||||
<el-button size="small" @click="selectedColumns = []">清空</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-export-body">
|
||||
<div class="export-left">
|
||||
<div class="export-panel-title">可选列</div>
|
||||
<div class="export-left-scroll">
|
||||
<el-checkbox-group v-model="selectedColumns">
|
||||
<div v-for="(group, gName) in groupedColumns" :key="gName" class="column-group">
|
||||
<div class="column-group-title">{{ gName }}</div>
|
||||
<div class="column-group-items">
|
||||
<el-checkbox
|
||||
v-for="field in group"
|
||||
:key="field.key"
|
||||
:label="field.key"
|
||||
:style="{ display: columnSearch && !filterMatch(field) ? 'none' : '' }"
|
||||
>{{ field.label }}</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-right">
|
||||
<div class="export-panel-title">
|
||||
导出顺序
|
||||
<span class="order-count">{{ orderedColumns.length }}</span>
|
||||
</div>
|
||||
<div class="export-right-scroll">
|
||||
<draggable v-model="orderedColumns" class="ordered-list" ghost-class="ghost" handle=".drag-handle">
|
||||
<div v-for="field in orderedColumns" :key="field" class="ordered-item">
|
||||
<i class="el-icon-rank drag-handle"></i>
|
||||
<span class="order-index">{{ orderedColumns.indexOf(field) + 1 }}</span>
|
||||
<span class="order-label">{{ exportColumns[field] || field }}</span>
|
||||
<i class="el-icon-close order-remove" @click.stop="removeOrderedField(field)"></i>
|
||||
</div>
|
||||
</draggable>
|
||||
<div v-if="orderedColumns.length === 0" class="empty-tip">勾选左侧列后出现在此处,可拖拽排序</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="custom-export-footer">
|
||||
<span class="selected-tip">已选 <b>{{ orderedColumns.length }}</b> / {{ flatColumns.length }} 列</span>
|
||||
<el-button @click="customExportVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="doCustomExport" :disabled="orderedColumns.length === 0">
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<custom-export
|
||||
ref="customExport"
|
||||
:visible.sync="customExportVisible"
|
||||
storage-key="coil-report-receive"
|
||||
:column-groups="columnGroups"
|
||||
@export="handleCustomExport"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCoilWithIds, getExportColumns } from "@/api/wms/coil";
|
||||
import { listCoilWithIds } from "@/api/wms/coil";
|
||||
import {
|
||||
listPendingAction,
|
||||
} from '@/api/wms/pendingAction';
|
||||
@@ -162,7 +114,7 @@ import TimeRangePicker from "@/views/wms/report/components/timeRangePicker.vue";
|
||||
import HierarchicalPivot from "@/views/wms/report/components/hierarchicalPivot/index.vue";
|
||||
import CrossTable from "@/views/wms/report/components/crossTable/index.vue";
|
||||
import { saveReportFile } from "@/views/wms/report/js/reportFile";
|
||||
import draggable from 'vuedraggable';
|
||||
import CustomExport from "@/views/wms/report/components/CustomExport.vue";
|
||||
|
||||
|
||||
export default {
|
||||
@@ -178,7 +130,7 @@ export default {
|
||||
TimeRangePicker,
|
||||
HierarchicalPivot,
|
||||
CrossTable,
|
||||
draggable,
|
||||
CustomExport,
|
||||
},
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status'],
|
||||
data() {
|
||||
@@ -206,10 +158,6 @@ export default {
|
||||
activeColumnConfig: 'coil-report-receive',
|
||||
settingVisible: false,
|
||||
customExportVisible: false,
|
||||
exportColumns: {},
|
||||
selectedColumns: [],
|
||||
orderedColumns: [],
|
||||
columnSearch: '',
|
||||
columnGroups: {
|
||||
'基本信息': ['itemTypeDesc', 'warehouseName', 'actualWarehouseName', 'dataTypeText'],
|
||||
'钢卷号': ['enterCoilNo', 'supplierCoilNo', 'currentCoilNo'],
|
||||
@@ -295,38 +243,22 @@ export default {
|
||||
coilIds() {
|
||||
return this.list.map(item => item.coilId).join(',')
|
||||
},
|
||||
groupedColumns() {
|
||||
const result = {}
|
||||
Object.entries(this.columnGroups).forEach(([groupName, fieldKeys]) => {
|
||||
const items = fieldKeys
|
||||
.filter(key => this.exportColumns[key])
|
||||
.map(key => ({ key, label: this.exportColumns[key] }))
|
||||
if (items.length) {
|
||||
result[groupName] = items
|
||||
}
|
||||
})
|
||||
return result
|
||||
actionIds() {
|
||||
const ids = this.list.map(item => item.actionId).filter(id => id)
|
||||
return ids.length ? ids.join(',') : ''
|
||||
},
|
||||
flatColumns() {
|
||||
return Object.values(this.groupedColumns).flat()
|
||||
exportIdParams() {
|
||||
return this.actionIds ? { actionIds: this.actionIds } : { coilIds: this.coilIds }
|
||||
},
|
||||
hasExportData() {
|
||||
return !!(this.coilIds || this.actionIds)
|
||||
},
|
||||
exportQueryParams() {
|
||||
const { pageNum, pageSize, ...filters } = this.queryParams
|
||||
return filters
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
selectedColumns: {
|
||||
immediate: false,
|
||||
handler(nv, ov) {
|
||||
const newSet = new Set(nv)
|
||||
const oldSet = ov ? new Set(ov) : new Set()
|
||||
// 移除取消勾选的列
|
||||
this.orderedColumns = this.orderedColumns.filter(f => newSet.has(f))
|
||||
// 新勾选的追加到末尾
|
||||
const added = nv.filter(f => !oldSet.has(f))
|
||||
if (added.length) {
|
||||
this.orderedColumns.push(...added)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载列设置
|
||||
@@ -384,55 +316,25 @@ export default {
|
||||
// 导出
|
||||
exportData() {
|
||||
this.download('wms/materialCoil/export', {
|
||||
coilIds: this.coilIds,
|
||||
...this.exportQueryParams,
|
||||
...this.exportIdParams,
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
// 打开自定义导出弹窗
|
||||
openCustomExport() {
|
||||
getExportColumns().then(res => {
|
||||
this.exportColumns = res.data
|
||||
// 读缓存:上次保存的列顺序
|
||||
const cached = localStorage.getItem('custom-export-columns-coil-report-receive')
|
||||
if (cached) {
|
||||
try {
|
||||
const arr = JSON.parse(cached)
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
this.selectedColumns = [...arr]
|
||||
this.orderedColumns = [...arr]
|
||||
this.customExportVisible = true
|
||||
return
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
this.selectedColumns = []
|
||||
this.orderedColumns = []
|
||||
this.customExportVisible = true
|
||||
})
|
||||
if (!this.hasExportData) {
|
||||
this.$message({ message: '暂无数据可导出', type: 'warning' })
|
||||
return
|
||||
}
|
||||
this.$refs.customExport.open()
|
||||
},
|
||||
// 执行自定义导出(按 orderedColumns 顺序)
|
||||
doCustomExport() {
|
||||
// 缓存当前配置
|
||||
localStorage.setItem('custom-export-columns-coil-report-receive', JSON.stringify(this.orderedColumns))
|
||||
this.customExportVisible = false
|
||||
handleCustomExport(orderedColumns) {
|
||||
this.download('wms/materialCoil/exportCustomOrdered', {
|
||||
coilIds: this.coilIds,
|
||||
columnsOrdered: this.orderedColumns.join(','),
|
||||
...this.exportQueryParams,
|
||||
...this.exportIdParams,
|
||||
columnsOrdered: orderedColumns.join(','),
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
removeOrderedField(field) {
|
||||
this.selectedColumns = this.selectedColumns.filter(f => f !== field)
|
||||
},
|
||||
filterMatch(field) {
|
||||
const keyword = this.columnSearch.toLowerCase()
|
||||
return !keyword || field.label.toLowerCase().includes(keyword) || field.key.toLowerCase().includes(keyword)
|
||||
},
|
||||
selectAllColumns() {
|
||||
this.selectedColumns = this.flatColumns.map(f => f.key)
|
||||
},
|
||||
invertColumns() {
|
||||
const allKeys = this.flatColumns.map(f => f.key)
|
||||
this.selectedColumns = allKeys.filter(k => !this.selectedColumns.includes(k))
|
||||
},
|
||||
saveReport() {
|
||||
this.loading = true
|
||||
saveReportFile(this.coilIds, {
|
||||
@@ -462,157 +364,4 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-export-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.custom-export-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.custom-export-body {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
height: 420px;
|
||||
}
|
||||
.export-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
.export-right {
|
||||
width: 260px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid #ebeef5;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.export-panel-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.order-count {
|
||||
background: #409eff;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
padding: 1px 7px;
|
||||
border-radius: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.export-left-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.export-right-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.column-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.column-group-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #909399;
|
||||
margin-bottom: 6px;
|
||||
padding-left: 8px;
|
||||
border-left: 2px solid #dcdfe6;
|
||||
}
|
||||
.column-group-items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 2px 8px;
|
||||
}
|
||||
.column-group-items .el-checkbox {
|
||||
margin-right: 0;
|
||||
}
|
||||
.ordered-list {
|
||||
min-height: 60px;
|
||||
}
|
||||
.ordered-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 8px;
|
||||
margin-bottom: 4px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
cursor: default;
|
||||
transition: background .2s;
|
||||
}
|
||||
.ordered-item:hover {
|
||||
background: #ecf5ff;
|
||||
border-color: #c6e2ff;
|
||||
}
|
||||
.ordered-item.ghost {
|
||||
opacity: 0.4;
|
||||
background: #409eff;
|
||||
}
|
||||
.drag-handle {
|
||||
color: #c0c4cc;
|
||||
cursor: grab;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
.order-index {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 11px;
|
||||
color: #909399;
|
||||
background: #e4e7ed;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.order-label {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.order-remove {
|
||||
color: #c0c4cc;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.order-remove:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.empty-tip {
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
}
|
||||
.custom-export-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.selected-tip {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
.selected-tip b {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user