feat(wms/report): 新增自定义导出列顺序功能并优化导出弹窗布局
1. 在ExcelUtil工具类中新增exportExcelOrdered方法,支持按指定顺序的列动态生成表头和数据行进行导出 2. 重构接收报表页面的自定义导出弹窗:将布局拆分为左侧可选列面板和右侧导出顺序面板,支持拖拽排序 3. 新增后端/exportCustomOrdered接口,接收有序字段列表并调用新的导出方法 4. 优化弹窗样式:调整宽度、间距、滚动区域,新增顺序序号和移除按钮 5. 移除原有的/exportCustom接口,统一使用新的有序导出逻辑
This commit is contained in:
@@ -87,9 +87,9 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 自定义导出列选择弹窗 -->
|
||||
<el-dialog title="自定义导出 - 选择导出列" :visible.sync="customExportVisible" width="750px">
|
||||
<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: 220px" />
|
||||
<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>
|
||||
@@ -97,25 +97,47 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-export-body">
|
||||
<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 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>
|
||||
</el-checkbox-group>
|
||||
</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>{{ selectedColumns.length }}</b> / {{ flatColumns.length }} 列</span>
|
||||
<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="selectedColumns.length === 0">
|
||||
导出选中列
|
||||
<el-button type="primary" @click="doCustomExport" :disabled="orderedColumns.length === 0">
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -140,6 +162,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';
|
||||
|
||||
|
||||
export default {
|
||||
@@ -155,6 +178,7 @@ export default {
|
||||
TimeRangePicker,
|
||||
HierarchicalPivot,
|
||||
CrossTable,
|
||||
draggable,
|
||||
},
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status'],
|
||||
data() {
|
||||
@@ -184,6 +208,7 @@ export default {
|
||||
customExportVisible: false,
|
||||
exportColumns: {},
|
||||
selectedColumns: [],
|
||||
orderedColumns: [],
|
||||
columnSearch: '',
|
||||
columnGroups: {
|
||||
'基本信息': ['itemTypeDesc', 'warehouseName', 'actualWarehouseName', 'dataTypeText'],
|
||||
@@ -287,6 +312,22 @@ export default {
|
||||
},
|
||||
|
||||
},
|
||||
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: {
|
||||
// 加载列设置
|
||||
loadColumns() {
|
||||
@@ -354,14 +395,17 @@ export default {
|
||||
this.customExportVisible = true
|
||||
})
|
||||
},
|
||||
// 执行自定义导出
|
||||
// 执行自定义导出(按 orderedColumns 顺序)
|
||||
doCustomExport() {
|
||||
this.customExportVisible = false
|
||||
this.download('wms/materialCoil/exportCustom', {
|
||||
this.download('wms/materialCoil/exportCustomOrdered', {
|
||||
coilIds: this.coilIds,
|
||||
columns: this.selectedColumns.join(','),
|
||||
columnsOrdered: this.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)
|
||||
@@ -406,8 +450,8 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.custom-export-actions {
|
||||
@@ -415,29 +459,134 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
.custom-export-body {
|
||||
max-height: 420px;
|
||||
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: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.column-group-title {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #606266;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 2px;
|
||||
border-left: 3px solid #409eff;
|
||||
color: #909399;
|
||||
margin-bottom: 6px;
|
||||
padding-left: 8px;
|
||||
border-left: 2px solid #dcdfe6;
|
||||
}
|
||||
.column-group-items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 4px 12px;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user