Browse Source

价格表

master
常熟吴彦祖 3 weeks ago
parent
commit
c406aa1336
  1. 1
      src/views/modules/order/poOrderStatusOptions.js
  2. 210
      src/views/modules/sys/initialImport.vue

1
src/views/modules/order/poOrderStatusOptions.js

@ -9,6 +9,7 @@ export const PO_ORDER_STATUS_OPTIONS = [
{ label: '验货完成', value: '验货完成' }, { label: '验货完成', value: '验货完成' },
{ label: '部分发货', value: '部分发货' }, { label: '部分发货', value: '部分发货' },
{ label: '已发货', value: '已发货' }, { label: '已发货', value: '已发货' },
{ label: '完全发货', value: '完全发货' },
{ label: '已取消', value: '已取消' }, { label: '已取消', value: '已取消' },
{ label: '已关闭', value: '已关闭' } { label: '已关闭', value: '已关闭' }
] ]

210
src/views/modules/sys/initialImport.vue

@ -66,9 +66,18 @@
选择导入文件 选择导入文件
</el-button> </el-button>
</el-upload> </el-upload>
<el-button
icon="el-icon-download"
:loading="exporting"
:disabled="previewLoading || saving || exporting || !previewRows.length"
@click="handleExportPreview">
导出校验结果
</el-button>
<span class="file-name" :title="fileName">{{ fileName || '未选择文件' }}</span> <span class="file-name" :title="fileName">{{ fileName || '未选择文件' }}</span>
<span v-if="hasInvalidPreviewRows" class="invalid-hint">存在校验失败行,请先导出并修正后再保存</span>
<span class="preview-count"> <span class="preview-count">
已读取 {{ previewRows.length }} 行<span v-if="previewRows.length > previewDisplayLimit">,表格显示前{{ previewDisplayLimit }}行</span> 已读取 {{ previewRows.length }} 行<span v-if="previewRows.length > previewDisplayLimit">,表格显示前{{ previewDisplayLimit }}行</span>
<span v-if="invalidPreviewCount">,其中 {{ invalidPreviewCount }} 行校验失败</span>
</span> </span>
</div> </div>
@ -163,6 +172,7 @@ export default {
previewLoading: false, previewLoading: false,
saving: false, saving: false,
templateDownloading: false, templateDownloading: false,
exporting: false,
fileName: '', fileName: '',
importFile: null, importFile: null,
poCustomer: '', poCustomer: '',
@ -208,10 +218,28 @@ export default {
return this.activeImportType === 'poOrder' return this.activeImportType === 'poOrder'
}, },
activeColumns () { activeColumns () {
let columns
if (this.isPoOrderImport) { if (this.isPoOrderImport) {
return this.poOrderColumns
}
return this.activeImportType === 'partSku' ? this.partSkuColumns : this.supplierPriceColumns
columns = this.poOrderColumns
} else if (this.activeImportType === 'partSku') {
columns = this.partSkuColumns
} else {
columns = this.supplierPriceColumns
}
// 三个导入预览都把错误信息放在最后一列,和导出文件保持一致。
const withoutError = (columns || []).filter(column => column.prop !== 'validationMessage')
return withoutError.concat([{
prop: 'validationMessage',
label: '错误信息',
width: 280,
align: 'left'
}])
},
invalidPreviewCount () {
return this.previewRows.filter(row => this.isPreviewRowInvalid(row)).length
},
hasInvalidPreviewRows () {
return this.invalidPreviewCount > 0
}, },
previewDisplayRows () { previewDisplayRows () {
return this.previewRows.slice(0, this.previewDisplayLimit) return this.previewRows.slice(0, this.previewDisplayLimit)
@ -226,16 +254,20 @@ export default {
if (this.activeImportType === 'poOrder') { if (this.activeImportType === 'poOrder') {
return '94%' return '94%'
} }
return this.activeImportType === 'partSku' ? '680px' : '94%'
return this.activeImportType === 'partSku' ? '860px' : '94%'
}, },
saveButtonText () { saveButtonText () {
return '保存' return '保存'
}, },
saveDisabled () { saveDisabled () {
// 任意导入类型只要存在黄标行,就不允许继续保存。
if (this.previewLoading || this.saving || !this.previewRows.length || this.hasInvalidPreviewRows) {
return true
}
if (this.isPoOrderImport) { if (this.isPoOrderImport) {
return this.previewLoading || this.saving || !this.previewRows.length || !this.poCustomer
return !this.poCustomer
} }
return this.previewLoading || this.saving || !this.previewRows.length
return false
} }
}, },
methods: { methods: {
@ -333,6 +365,7 @@ export default {
: previewSupplierPrice(rawFile, site) : previewSupplierPrice(rawFile, site)
}, },
buildPoOrderColumns (columnMapping) { buildPoOrderColumns (columnMapping) {
// 预览表按模板字段展示;物料编码、供应商编码分别由 Item、Vendor Name 解析得到。
const columns = Object.keys(columnMapping).map(prop => ({ const columns = Object.keys(columnMapping).map(prop => ({
prop, prop,
label: columnMapping[prop], label: columnMapping[prop],
@ -341,8 +374,7 @@ export default {
})) }))
columns.push( columns.push(
{ prop: 'part_no', label: '物料编码', width: 140, align: 'left' }, { prop: 'part_no', label: '物料编码', width: 140, align: 'left' },
{ prop: 'supplier_no', label: '供应商编码', width: 140, align: 'left' },
{ prop: 'validationMessage', label: '校验结果', width: 220, align: 'left' }
{ prop: 'supplier_no', label: '供应商编码', width: 140, align: 'left' }
) )
return columns return columns
}, },
@ -418,17 +450,17 @@ export default {
if (!this.ensureInitialImportAvailable()) { if (!this.ensureInitialImportAvailable()) {
return return
} }
if (this.isPoOrderImport) {
const invalidRows = this.previewRows.filter(row => !row || row.valid === false)
if (invalidRows.length) {
this.$message.warning(`有${invalidRows.length}行校验未通过,请修正Excel后重新预览`)
if (!this.previewRows.length) {
this.$message.warning('请先选择导入文件并完成预览')
return return
} }
this.performPoOrderImport()
// 三个导入统一:有黄标行一律禁止保存,避免把失败数据写入系统。
if (this.hasInvalidPreviewRows) {
this.$message.warning(`有${this.invalidPreviewCount}行校验未通过,请修正Excel后重新预览。可先导出校验结果`)
return return
} }
if (!this.previewRows.length) {
this.$message.warning('没有可保存的导入数据')
if (this.isPoOrderImport) {
this.performPoOrderImport()
return return
} }
const site = this.$store.state.user.site const site = this.$store.state.user.site
@ -436,21 +468,6 @@ export default {
this.$message.error('当前登录用户没有Site') this.$message.error('当前登录用户没有Site')
return return
} }
const invalidCount = this.previewRows.filter(row => row && row.valid === false).length
if (invalidCount > 0) {
this.$confirm(
`存在${invalidCount}行淡黄色数据。如果继续保存,无效数据行将跳过,是否继续?`,
'数据校验提示',
{
confirmButtonText: '继续保存',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
this.performSave(site)
}).catch(() => {})
return
}
this.performSave(site) this.performSave(site)
}, },
performSave (site) { performSave (site) {
@ -531,8 +548,10 @@ export default {
if (this.missingItems.length) { if (this.missingItems.length) {
messages.push(`以下Item不存在:${this.missingItems.join('、')}`) messages.push(`以下Item不存在:${this.missingItems.join('、')}`)
} }
// PO 导入按 Vendor Name 查供应商;价格期初仍按 ShortNPC 提示。
if ((this.activeImportType === 'supplierPrice' || this.isPoOrderImport) && this.missingSuppliers.length) { if ((this.activeImportType === 'supplierPrice' || this.isPoOrderImport) && this.missingSuppliers.length) {
messages.push(`以下ShortNPC不存在:${this.missingSuppliers.join('、')}`)
const supplierLabel = this.isPoOrderImport ? 'Vendor Name' : 'ShortNPC'
messages.push(`以下${supplierLabel}不存在:${this.missingSuppliers.join('、')}`)
} }
const content = this.$createElement( const content = this.$createElement(
'div', 'div',
@ -545,7 +564,128 @@ export default {
}).catch(() => {}) }).catch(() => {})
}, },
previewRowClassName ({ row }) { previewRowClassName ({ row }) {
return row && row.valid === false ? 'initial-import-warning-row' : ''
return this.isPreviewRowInvalid(row) ? 'initial-import-warning-row' : ''
},
/**
* 预览行是否校验失败。只认 valid === false,与页面黄标规则一致。
*/
isPreviewRowInvalid (row) {
return !!(row && row.valid === false)
},
/**
* 导出当前预览数据及校验结果。
* 触发条件:已完成预览且存在数据行。
* 结果:下载 Excel,失败行整行标黄,最后一列为错误信息。
*/
handleExportPreview () {
if (!this.previewRows.length) {
this.$message.warning('请先选择导入文件并完成预览')
return
}
this.exporting = true
try {
const columns = this.buildPreviewExportColumns()
const xml = this.buildPreviewExportXml(columns, this.previewRows)
const blob = new Blob(['\ufeff' + xml], { type: 'application/vnd.ms-excel;charset=utf-8' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = this.resolvePreviewExportFileName()
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success('校验结果已导出')
} catch (error) {
this.$message.error((error && error.message) || '导出失败,请稍后重试')
} finally {
this.exporting = false
}
},
/**
* 导出列与预览表一致:PO 额外带 Excel 行号,最后一列固定为错误信息。
*/
buildPreviewExportColumns () {
const columns = []
if (this.isPoOrderImport) {
columns.push({ prop: 'excelRowNo', label: 'Excel行号' })
}
return columns.concat(this.activeColumns || [])
},
resolvePreviewExportFileName () {
const now = new Date()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hour = String(now.getHours()).padStart(2, '0')
const minute = String(now.getMinutes()).padStart(2, '0')
const second = String(now.getSeconds()).padStart(2, '0')
return `${this.dialogTitle}校验结果_${now.getFullYear()}${month}${day}${hour}${minute}${second}.xls`
},
/**
* 生成带行底色的 SpreadsheetML,Excel 打开后失败行仍为黄色。
* 入参 columns 为导出列;rows 为当前全部预览行(不只表格显示的前 500 行)。
*/
buildPreviewExportXml (columns, rows) {
const headerCells = columns.map(column => {
const width = column.prop === 'validationMessage' ? 280 : 80
return `<Column ss:AutoFitWidth="0" ss:Width="${width}"/>`
}).join('')
const headerRow = '<Row ss:StyleID="Header">' + columns.map(column => {
return `<Cell><Data ss:Type="String">${this.escapeExcelXml(column.label)}</Data></Cell>`
}).join('') + '</Row>'
const dataRows = (rows || []).map(row => {
const styleAttr = this.isPreviewRowInvalid(row) ? ' ss:StyleID="Warn"' : ''
const cells = columns.map(column => {
const value = this.formatExportCellValue(row, column.prop)
return `<Cell><Data ss:Type="String">${this.escapeExcelXml(value)}</Data></Cell>`
}).join('')
return `<Row${styleAttr}>${cells}</Row>`
}).join('')
return `<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Center" ss:WrapText="1"/>
</Style>
<Style ss:ID="Header">
<Font ss:Bold="1"/>
<Interior ss:Color="#D9D9D9" ss:Pattern="Solid"/>
</Style>
<Style ss:ID="Warn">
<Interior ss:Color="#FFF7D6" ss:Pattern="Solid"/>
</Style>
</Styles>
<Worksheet ss:Name="校验结果">
<Table>${headerCells}${headerRow}${dataRows}</Table>
</Worksheet>
</Workbook>`
},
/**
* 导出单元格文本。空值输出空串,避免 Excel 出现 undefined。
*/
formatExportCellValue (row, prop) {
if (!row || !prop) {
return ''
}
const value = row[prop]
if (value == null || value === '') {
return ''
}
return String(value)
},
/**
* SpreadsheetML 特殊字符转义,防止错误信息里的符号破坏 XML。
*/
escapeExcelXml (value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
}, },
handleDialogClose (done) { handleDialogClose (done) {
if (!this.saving) { if (!this.saving) {
@ -564,6 +704,7 @@ export default {
this.previewLoading = false this.previewLoading = false
this.saving = false this.saving = false
this.templateDownloading = false this.templateDownloading = false
this.exporting = false
this.previewRows = [] this.previewRows = []
this.missingItems = [] this.missingItems = []
this.missingSuppliers = [] this.missingSuppliers = []
@ -626,6 +767,11 @@ export default {
white-space: nowrap; white-space: nowrap;
} }
.invalid-hint {
color: #e6a23c;
font-size: 13px;
}
.preview-count { .preview-count {
margin-left: auto; margin-left: auto;
color: #606266; color: #606266;

Loading…
Cancel
Save