Files
klp-oa/klp-ui/src/views/aps/quickSheetPreview.vue

355 lines
10 KiB
Vue
Raw Normal View History

2026-03-14 15:06:18 +08:00
<template>
<div class="aps-quick-sheet-preview excel-theme">
<div class="sheet-toolbar">
<div class="sheet-actions">
<el-date-picker
v-model="filter.range"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
size="small"
style="width: 240px"
@change="onFilterChange"
/>
<el-select v-model="filter.lineId" clearable filterable size="small" placeholder="产线" style="width: 160px" @change="onFilterChange">
<el-option v-for="line in lineOptions" :key="line.lineId" :label="line.lineName || line.lineCode || ('产线' + line.lineId)" :value="line.lineId" />
</el-select>
<el-input v-model="filter.customer" size="small" clearable placeholder="客户" style="width: 160px" @input="onFilterChange" />
<el-button size="small" icon="el-icon-refresh" @click="loadRows">刷新</el-button>
<el-button size="small" icon="el-icon-setting" @click="columnSettingVisible = true">列设置</el-button>
2026-03-14 15:06:18 +08:00
<el-button size="small" icon="el-icon-download" @click="exportExcel">导出</el-button>
</div>
</div>
<div class="sheet-body">
<el-table
ref="quickSheetPreviewTable"
2026-03-14 15:06:18 +08:00
v-loading="loading"
:data="displayRows"
border
size="mini"
class="excel-table"
>
<el-table-column label="序号" width="60" align="center" fixed="left">
2026-03-14 15:06:18 +08:00
<template slot-scope="scope">
{{ ((pager.pageNum - 1) * pager.pageSize) + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column
v-for="col in displayColumns"
2026-03-14 15:06:18 +08:00
:key="col.prop || col.label"
:label="col.label"
:prop="col.prop"
:width="col.width"
:min-width="col.minWidth"
:align="col.align || 'center'"
:fixed="fixedProps.includes(col.prop) ? 'left' : false"
2026-03-14 15:06:18 +08:00
:show-overflow-tooltip="col.showOverflowTooltip !== false"
/>
</el-table>
<div class="pager-wrap">
<el-pagination
small
background
layout="prev, pager, next, total"
:current-page.sync="pager.pageNum"
:page-size="pager.pageSize"
:total="totalRows"
/>
</div>
</div>
<el-drawer
title="列设置"
:visible.sync="columnSettingVisible"
direction="rtl"
size="55%"
append-to-body>
<div class="col-setting-body">
<div class="col-setting-toolbar">
<el-button size="mini" @click="restoreDefaultColumns">恢复默认</el-button>
</div>
<draggable v-model="columnSettingList" handle=".drag-handle" animation="200" @change="persistColumnSettings" class="col-setting-grid">
<div v-for="item in columnSettingList" :key="item.prop" class="col-setting-item">
<i class="el-icon-rank drag-handle" />
<el-checkbox v-model="item.visible" @change="persistColumnSettings">{{ item.label }}</el-checkbox>
</div>
</draggable>
</div>
</el-drawer>
2026-03-14 15:06:18 +08:00
</div>
</template>
<script>
import { fetchQuickSheetList, exportQuickSheet } from '@/api/aps/quickSheet'
import { listProductionLine } from '@/api/wms/productionLine'
import { getTemplateByKey } from './sheets/templates'
import { saveAs } from 'file-saver'
import draggable from 'vuedraggable'
2026-03-14 15:06:18 +08:00
export default {
name: 'ApsQuickSheetPreview',
components: { draggable },
2026-03-14 15:06:18 +08:00
data() {
return {
loading: false,
rows: [],
lineOptions: [],
filter: {
range: [],
lineId: null,
customer: ''
},
pager: {
pageNum: 1,
pageSize: 25
},
columnSettingVisible: false,
columnSettingList: []
2026-03-14 15:06:18 +08:00
}
},
computed: {
currentTemplate() {
return getTemplateByKey('unified')
},
flatColumns() {
const res = []
const loop = (cols) => {
;(cols || []).forEach(c => {
if (c.children && c.children.length) loop(c.children)
else res.push(c)
})
}
loop(this.currentTemplate.columns || [])
return res.filter(c => c && c.prop)
},
displayColumns() {
const setting = this.columnSettingList || []
const hasSetting = setting.length > 0
const base = this.flatColumns
const selected = hasSetting
? setting.filter(i => i.visible).map(i => base.find(c => c.prop === i.prop)).filter(Boolean)
: base
const selectedSet = new Set(selected.map(c => c.prop))
const rest = base.filter(c => !selectedSet.has(c.prop))
return [...selected, ...rest]
2026-03-14 15:06:18 +08:00
},
filteredRows() {
const [startAfter, endBefore] = this.filter.range || []
const lineName = (this.filter.lineName || '').trim()
const customer = (this.filter.customer || '').trim().toLowerCase()
return this.rows.filter(r => {
if (startAfter || endBefore) {
const d = r.startTime ? String(r.startTime).slice(0, 10) : ''
if (startAfter && d && d < startAfter) return false
if (endBefore && d && d > endBefore) return false
}
if (lineName && r.lineName !== lineName) return false
if (customer) {
const txt = String(r.customerName || '').toLowerCase()
if (!txt.includes(customer)) return false
}
return true
})
},
displayRows() {
const start = (this.pager.pageNum - 1) * this.pager.pageSize
return this.filteredRows.slice(start, start + this.pager.pageSize)
},
totalRows() {
return this.filteredRows.length
}
},
created() {
const today = new Date()
const start = `${today.getFullYear()}-${`${today.getMonth() + 1}`.padStart(2, '0')}-${`${today.getDate()}`.padStart(2, '0')}`
this.filter.range = [start, '']
this.initColumnSettings()
2026-03-14 15:06:18 +08:00
this.loadRows()
this.loadLines()
},
beforeDestroy() {
this.persistColumnSettings()
},
2026-03-14 15:06:18 +08:00
methods: {
initColumnSettings() {
const key = this.getColumnSettingKey()
const stored = localStorage.getItem(key)
const base = this.flatColumns.map(col => ({ prop: col.prop, label: col.label, visible: true }))
if (!stored) {
this.columnSettingList = base
return
}
try {
const parsed = JSON.parse(stored)
if (!Array.isArray(parsed)) {
this.columnSettingList = base
return
}
const map = new Map(parsed.map(item => [item.prop, item]))
const merged = base.map(item => {
const saved = map.get(item.prop)
return saved ? { ...item, visible: saved.visible !== false } : item
})
const savedProps = parsed.map(i => i.prop)
const extras = base.filter(i => !savedProps.includes(i.prop))
this.columnSettingList = [...parsed.filter(i => base.some(b => b.prop === i.prop)).map(i => {
const match = base.find(b => b.prop === i.prop)
return { ...match, visible: i.visible !== false }
}), ...extras]
} catch (e) {
this.columnSettingList = base
}
},
persistColumnSettings() {
const key = this.getColumnSettingKey()
const payload = (this.columnSettingList || []).map(item => ({
prop: item.prop,
label: item.label,
visible: item.visible !== false
}))
localStorage.setItem(key, JSON.stringify(payload))
},
restoreDefaultColumns() {
const base = this.flatColumns.map(col => ({ prop: col.prop, label: col.label, visible: true }))
this.columnSettingList = base
this.persistColumnSettings()
},
getColumnSettingKey() {
return 'apsQuickSheetPreviewColumns_unified'
},
2026-03-14 15:06:18 +08:00
async loadLines() {
const res = await listProductionLine({ pageNum: 1, pageSize: 1000 })
this.lineOptions = res.rows || []
},
async loadRows() {
this.loading = true
try {
const params = this.buildQueryParams()
const res = await fetchQuickSheetList(params)
this.rows = (res.data || []).map(r => ({ ...r }))
} finally {
this.loading = false
}
},
onFilterChange() {
this.pager.pageNum = 1
this.loadRows()
},
exportExcel() {
const params = this.buildQueryParams()
exportQuickSheet(params)
.then(blob => {
const filename = `quick_sheet_preview_${new Date().getTime()}.xlsx`
saveAs(new Blob([blob], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), filename)
})
},
buildQueryParams() {
const [startDate, endDate] = this.filter.range || []
return {
startDate: startDate || undefined,
endDate: endDate || undefined,
lineId: this.filter.lineId || undefined,
customerName: this.filter.customer || undefined
}
}
}
}
</script>
<style scoped lang="scss">
.aps-quick-sheet-preview {
padding: 8px;
background: #f7f9fc;
}
.sheet-toolbar {
margin-bottom: 8px;
padding: 8px;
border: 1px solid #eef2f7;
border-radius: 8px;
background: #fff;
}
.sheet-actions {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.sheet-body {
border: 1px solid #eef2f7;
border-radius: 8px;
background: #fff;
padding: 6px;
}
::v-deep .excel-table {
border: 1px solid #edf1f7;
}
::v-deep .excel-table th.el-table__cell {
background: #fafbfe;
color: #5d6b82;
font-weight: 600;
border-right: 1px solid #edf1f7;
border-bottom: 1px solid #edf1f7;
padding: 4px 0;
}
::v-deep .excel-table td.el-table__cell {
border-right: 1px solid #f0f2f6;
border-bottom: 1px solid #f0f2f6;
padding: 1px 2px;
}
.pager-wrap {
margin-top: 8px;
display: flex;
justify-content: flex-end;
}
.col-setting-body {
padding: 12px;
height: calc(100% - 12px);
overflow: hidden;
}
.col-setting-toolbar {
margin-bottom: 10px;
display: flex;
justify-content: flex-end;
}
.col-setting-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.col-setting-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
border: 1px solid #ebeef5;
border-radius: 6px;
background: #fff;
margin-bottom: 0;
min-height: 38px;
}
.col-setting-item:hover {
border-color: #d9ecff;
background: #f5faff;
}
.drag-handle {
cursor: move;
color: #909399;
}
2026-03-14 15:06:18 +08:00
</style>