Browse Source

价格表

master
常熟吴彦祖 3 weeks ago
parent
commit
7bf7ebc60b
  1. 33
      src/api/initialImport.js
  2. 1
      src/views/modules/order/poOrder.vue
  3. 254
      src/views/modules/sys/initialImport.vue

33
src/api/initialImport.js

@ -55,3 +55,36 @@ export const savePartSku = data => http({
data: http.adornData(data),
timeout: 600000
})
export const downloadPoOrderTemplate = (site, customer) => http({
url: http.adornUrl('/pODetail/downloadTemplate'),
method: 'get',
params: {
site,
customer
},
responseType: 'blob'
})
export const previewPoOrder = (file, site, customer) => {
const formData = new FormData()
formData.append('file', file)
formData.append('site', site)
formData.append('customer', customer)
return http({
url: http.adornUrl('/pODetail/importCreatePoOrder/preview'),
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 180000
})
}
export const savePoOrderBatch = data => http({
url: http.adornUrl('/pODetail/importCreatePoOrder/save'),
method: 'post',
data: http.adornData(data),
timeout: 600000
})

1
src/views/modules/order/poOrder.vue

@ -50,7 +50,6 @@
</el-button>
<el-button v-if="editMode" style="margin-left: 2px" @click="cancelEditMode">取消</el-button>
<el-button type="primary" style="margin-left: 2px" @click="openAddDialog" v-if="!editMode">新增</el-button>
<!-- <el-button type="primary" style="margin-left: 2px" @click="openImportDialog" v-if="!editMode">导入</el-button>-->
</el-form-item>
</el-form>
<el-table

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

@ -34,6 +34,13 @@
:before-close="handleDialogClose"
@closed="resetDialog">
<div class="dialog-toolbar">
<com-po-order-customer-picker
v-if="isPoOrderImport"
v-model="poCustomer"
:site="currentSite"
:disabled="previewLoading || saving"
@input="handlePoCustomerChange"
class="po-import-customer-picker" />
<el-button
type="primary"
icon="el-icon-download"
@ -60,18 +67,27 @@
</el-button>
</el-upload>
<span class="file-name" :title="fileName">{{ fileName || '未选择文件' }}</span>
<span class="preview-count">已读取 {{ previewRows.length }} 行</span>
<span class="preview-count">
已读取 {{ previewRows.length }} 行<span v-if="previewRows.length > previewDisplayLimit">,表格显示前{{ previewDisplayLimit }}行</span>
</span>
</div>
<el-table
v-loading="previewLoading"
:data="previewRows"
:data="previewDisplayRows"
border
height="520"
size="mini"
empty-text="请选择Excel文件进行预览"
:row-class-name="previewRowClassName"
class="initial-import-preview">
<el-table-column
v-if="isPoOrderImport"
prop="excelRowNo"
label="Excel行号"
width="90"
fixed="left"
align="center" />
<el-table-column
v-for="column in activeColumns"
:key="column.prop"
@ -81,15 +97,16 @@
:align="column.align"
show-overflow-tooltip />
</el-table>
<div v-if="isPoOrderImport && saveProgressText" class="save-progress">{{ saveProgressText }}</div>
<div slot="footer" class="dialog-footer">
<el-button
type="primary"
icon="el-icon-check"
:loading="saving"
:disabled="previewLoading || saving || !previewRows.length"
:disabled="saveDisabled"
@click="handleSave">
保存
{{ saveButtonText }}
</el-button>
<el-button :disabled="previewLoading || saving" @click="dialogVisible = false">关闭</el-button>
</div>
@ -99,16 +116,23 @@
<script>
import {
downloadPoOrderTemplate,
downloadPartSkuTemplate,
downloadSupplierPriceTemplate,
previewPartSku,
previewPoOrder,
previewSupplierPrice,
savePartSku,
savePoOrderBatch,
saveSupplierPrice
} from '@/api/initialImport'
import ComPoOrderCustomerPicker from '@/views/modules/order/com_poOrder_CustomerPicker.vue'
export default {
name: 'SysInitialImport',
components: {
ComPoOrderCustomerPicker
},
data () {
return {
importItems: [
@ -121,6 +145,11 @@ export default {
type: 'partSku',
name: '物料SKU导入',
description: '按Item更新物料SKU,不存在的SKU会自动新增。'
},
{
type: 'poOrder',
name: 'PO订单导入',
description: '按Customer下载动态模板并批量创建PO订单。'
}
],
activeImportType: '',
@ -129,7 +158,12 @@ export default {
saving: false,
templateDownloading: false,
fileName: '',
importFile: null,
poCustomer: '',
previewRows: [],
previewDisplayLimit: 500,
poOrderColumns: [],
saveProgressText: '',
missingItems: [],
missingSuppliers: [],
supplierPriceColumns: [
@ -161,19 +195,46 @@ export default {
}
},
computed: {
currentSite () {
return this.$store.state.user.site || ''
},
isPoOrderImport () {
return this.activeImportType === 'poOrder'
},
activeColumns () {
if (this.isPoOrderImport) {
return this.poOrderColumns
}
return this.activeImportType === 'partSku' ? this.partSkuColumns : this.supplierPriceColumns
},
previewDisplayRows () {
return this.previewRows.slice(0, this.previewDisplayLimit)
},
dialogTitle () {
if (this.activeImportType === 'poOrder') {
return 'PO订单导入'
}
return this.activeImportType === 'partSku' ? '物料SKU导入' : '供应商价格期初导入'
},
dialogWidth () {
if (this.activeImportType === 'poOrder') {
return '94%'
}
return this.activeImportType === 'partSku' ? '680px' : '94%'
},
saveButtonText () {
return '保存'
},
saveDisabled () {
if (this.isPoOrderImport) {
return this.previewLoading || this.saving || !this.previewRows.length || !this.poCustomer
}
return this.previewLoading || this.saving || !this.previewRows.length
}
},
methods: {
openImportDialog (type) {
if (type !== 'supplierPrice' && type !== 'partSku') {
if (type !== 'supplierPrice' && type !== 'partSku' && type !== 'poOrder') {
return
}
this.resetDialog()
@ -192,16 +253,27 @@ export default {
return
}
this.fileName = file.name || ''
this.importFile = rawFile
this.saveProgressText = ''
if (this.isPoOrderImport && !this.poCustomer) {
this.$message.warning('请先选择 Customer')
this.clearSelectedFile()
return
}
this.previewRows = []
this.poOrderColumns = []
this.missingItems = []
this.missingSuppliers = []
this.previewLoading = true
const previewRequest = this.activeImportType === 'partSku' ? previewPartSku : previewSupplierPrice
previewRequest(rawFile, site).then(({ data }) => {
const previewRequest = this.resolvePreviewRequest(rawFile, site)
previewRequest.then(({ data }) => {
if (data && data.code === 0) {
this.previewRows = data.data || []
this.missingItems = data.missingItems || []
this.missingSuppliers = data.missingSuppliers || []
if (this.isPoOrderImport) {
this.poOrderColumns = this.buildPoOrderColumns(data.columnMapping || {})
}
this.$message.success(`Excel读取成功,共${this.previewRows.length}行`)
this.showMissingDataAlert()
} else {
@ -218,6 +290,39 @@ export default {
this.previewLoading = false
})
},
resolvePreviewRequest (rawFile, site) {
if (this.isPoOrderImport) {
return previewPoOrder(rawFile, site, this.poCustomer)
}
return this.activeImportType === 'partSku'
? previewPartSku(rawFile, site)
: previewSupplierPrice(rawFile, site)
},
buildPoOrderColumns (columnMapping) {
const columns = Object.keys(columnMapping).map(prop => ({
prop,
label: columnMapping[prop],
width: 130,
align: 'left'
}))
columns.push(
{ prop: 'part_no', label: '物料编码', width: 140, align: 'left' },
{ prop: 'supplier_no', label: '供应商编码', width: 140, align: 'left' },
{ prop: 'validationMessage', label: '校验结果', width: 220, align: 'left' }
)
return columns
},
handlePoCustomerChange () {
if (!this.importFile && !this.previewRows.length) {
return
}
this.previewRows = []
this.poOrderColumns = []
this.missingItems = []
this.missingSuppliers = []
this.saveProgressText = ''
this.clearSelectedFile()
},
handleFileExceed (files) {
if (this.previewLoading || !files || !files.length) {
return
@ -226,13 +331,18 @@ export default {
this.handleFileChange({ raw: files[0], name: files[0].name })
},
handleDownloadTemplate () {
const site = this.currentSite
if (!site) {
this.$message.error('当前登录用户没有Site')
return
}
if (this.isPoOrderImport && !this.poCustomer) {
this.$message.warning('请先选择 Customer')
return
}
this.templateDownloading = true
const downloadRequest = this.activeImportType === 'partSku'
? downloadPartSkuTemplate
: downloadSupplierPriceTemplate
const downloadFileName = this.activeImportType === 'partSku'
? '物料SKU期初导入模板.xlsx'
: '供应商价格期初导入模板.xlsx'
const downloadRequest = this.resolveDownloadTemplateRequest()
const downloadFileName = this.resolveDownloadTemplateFileName()
downloadRequest().then(response => {
const blob = new Blob([response.data], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@ -251,7 +361,32 @@ export default {
this.templateDownloading = false
})
},
resolveDownloadTemplateRequest () {
if (this.isPoOrderImport) {
return () => downloadPoOrderTemplate(this.currentSite, this.poCustomer)
}
return this.activeImportType === 'partSku'
? downloadPartSkuTemplate
: downloadSupplierPriceTemplate
},
resolveDownloadTemplateFileName () {
if (this.isPoOrderImport) {
return `PO订单导入模板_${this.poCustomer}.xlsx`
}
return this.activeImportType === 'partSku'
? '物料SKU期初导入模板.xlsx'
: '供应商价格期初导入模板.xlsx'
},
handleSave () {
if (this.isPoOrderImport) {
const invalidRows = this.previewRows.filter(row => !row || row.valid === false)
if (invalidRows.length) {
this.$message.warning(`有${invalidRows.length}行校验未通过,请修正Excel后重新预览`)
return
}
this.performPoOrderImport()
return
}
if (!this.previewRows.length) {
this.$message.warning('没有可保存的导入数据')
return
@ -303,6 +438,51 @@ export default {
this.saving = false
})
},
async performPoOrderImport () {
if (!this.previewRows.length) {
this.$message.warning('请先选择导入文件并完成预览')
return
}
if (!this.currentSite) {
this.$message.error('当前登录用户没有Site')
return
}
if (!this.poCustomer) {
this.$message.warning('请先选择 Customer')
return
}
this.saving = true
this.saveProgressText = ''
const batchSize = 500
const totalCount = this.previewRows.length
let savedCount = 0
try {
while (this.previewRows.length) {
const rows = this.previewRows.slice(0, batchSize)
const { data } = await savePoOrderBatch({
site: this.currentSite,
customer: this.poCustomer,
rows
})
if (!(data && data.code === 0)) {
throw new Error((data && data.msg) || `第${Math.floor(savedCount / batchSize) + 1}批保存失败`)
}
savedCount += Number(data.savedCount == null ? rows.length : data.savedCount)
// 已成功提交的行立即从待保存列表移除。若后续批次失败,用户再次点击保存时
// 只会续传剩余行,避免前面已提交的数据被重复创建。
this.previewRows.splice(0, rows.length)
this.saveProgressText = `正在分批保存:${savedCount}/${totalCount}`
}
this.$message.success(`保存完成,共成功${savedCount}行`)
this.dialogVisible = false
} catch (error) {
const responseMessage = error && error.response && error.response.data && error.response.data.msg
const message = responseMessage || error.message || '保存失败,请稍后重试'
this.$message.error(`已保存${savedCount}行,后续批次停止:${message}`)
} finally {
this.saving = false
}
},
showMissingDataAlert () {
if (!this.missingItems.length && !this.missingSuppliers.length) {
return
@ -311,8 +491,8 @@ export default {
if (this.missingItems.length) {
messages.push(`以下Item不存在:${this.missingItems.join('、')}`)
}
if (this.activeImportType === 'supplierPrice' && this.missingSuppliers.length) {
messages.push(`以下供应商简称不存在:${this.missingSuppliers.join('、')}`)
if ((this.activeImportType === 'supplierPrice' || this.isPoOrderImport) && this.missingSuppliers.length) {
messages.push(`以下ShortNPC不存在:${this.missingSuppliers.join('、')}`)
}
const content = this.$createElement(
'div',
@ -334,6 +514,7 @@ export default {
},
clearSelectedFile () {
this.fileName = ''
this.importFile = null
if (this.$refs.initialImportUpload) {
this.$refs.initialImportUpload.clearFiles()
}
@ -346,6 +527,9 @@ export default {
this.previewRows = []
this.missingItems = []
this.missingSuppliers = []
this.poOrderColumns = []
this.saveProgressText = ''
this.poCustomer = ''
this.clearSelectedFile()
}
}
@ -379,11 +563,20 @@ export default {
.dialog-toolbar {
display: flex;
align-items: center;
flex-wrap: wrap;
min-height: 40px;
margin-bottom: 12px;
gap: 10px;
}
.po-import-customer-picker {
width: 100%;
}
.po-import-customer-picker /deep/ .el-form-item {
margin-bottom: 0;
}
.file-name {
display: inline-block;
max-width: 360px;
@ -411,6 +604,37 @@ export default {
background-color: #fff0b3 !important;
}
.save-progress {
margin-top: 10px;
color: #606266;
font-size: 13px;
text-align: right;
}
.po-import-result {
min-height: 180px;
padding: 12px;
color: #606266;
font-size: 13px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.po-import-errors {
max-height: 160px;
margin-top: 8px;
overflow: auto;
}
.po-import-error-row {
line-height: 1.6;
}
.po-import-error-more {
color: #909399;
}
.dialog-footer {
text-align: right;
}

Loading…
Cancel
Save