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), data: http.adornData(data),
timeout: 600000 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>
<el-button v-if="editMode" style="margin-left: 2px" @click="cancelEditMode">取消</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="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-item>
</el-form> </el-form>
<el-table <el-table

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

@ -34,6 +34,13 @@
:before-close="handleDialogClose" :before-close="handleDialogClose"
@closed="resetDialog"> @closed="resetDialog">
<div class="dialog-toolbar"> <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 <el-button
type="primary" type="primary"
icon="el-icon-download" icon="el-icon-download"
@ -60,18 +67,27 @@
</el-button> </el-button>
</el-upload> </el-upload>
<span class="file-name" :title="fileName">{{ fileName || '未选择文件' }}</span> <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> </div>
<el-table <el-table
v-loading="previewLoading" v-loading="previewLoading"
:data="previewRows"
:data="previewDisplayRows"
border border
height="520" height="520"
size="mini" size="mini"
empty-text="请选择Excel文件进行预览" empty-text="请选择Excel文件进行预览"
:row-class-name="previewRowClassName" :row-class-name="previewRowClassName"
class="initial-import-preview"> class="initial-import-preview">
<el-table-column
v-if="isPoOrderImport"
prop="excelRowNo"
label="Excel行号"
width="90"
fixed="left"
align="center" />
<el-table-column <el-table-column
v-for="column in activeColumns" v-for="column in activeColumns"
:key="column.prop" :key="column.prop"
@ -81,15 +97,16 @@
:align="column.align" :align="column.align"
show-overflow-tooltip /> show-overflow-tooltip />
</el-table> </el-table>
<div v-if="isPoOrderImport && saveProgressText" class="save-progress">{{ saveProgressText }}</div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button <el-button
type="primary" type="primary"
icon="el-icon-check" icon="el-icon-check"
:loading="saving" :loading="saving"
:disabled="previewLoading || saving || !previewRows.length"
:disabled="saveDisabled"
@click="handleSave"> @click="handleSave">
保存
{{ saveButtonText }}
</el-button> </el-button>
<el-button :disabled="previewLoading || saving" @click="dialogVisible = false">关闭</el-button> <el-button :disabled="previewLoading || saving" @click="dialogVisible = false">关闭</el-button>
</div> </div>
@ -99,16 +116,23 @@
<script> <script>
import { import {
downloadPoOrderTemplate,
downloadPartSkuTemplate, downloadPartSkuTemplate,
downloadSupplierPriceTemplate, downloadSupplierPriceTemplate,
previewPartSku, previewPartSku,
previewPoOrder,
previewSupplierPrice, previewSupplierPrice,
savePartSku, savePartSku,
savePoOrderBatch,
saveSupplierPrice saveSupplierPrice
} from '@/api/initialImport' } from '@/api/initialImport'
import ComPoOrderCustomerPicker from '@/views/modules/order/com_poOrder_CustomerPicker.vue'
export default { export default {
name: 'SysInitialImport', name: 'SysInitialImport',
components: {
ComPoOrderCustomerPicker
},
data () { data () {
return { return {
importItems: [ importItems: [
@ -121,6 +145,11 @@ export default {
type: 'partSku', type: 'partSku',
name: '物料SKU导入', name: '物料SKU导入',
description: '按Item更新物料SKU,不存在的SKU会自动新增。' description: '按Item更新物料SKU,不存在的SKU会自动新增。'
},
{
type: 'poOrder',
name: 'PO订单导入',
description: '按Customer下载动态模板并批量创建PO订单。'
} }
], ],
activeImportType: '', activeImportType: '',
@ -129,7 +158,12 @@ export default {
saving: false, saving: false,
templateDownloading: false, templateDownloading: false,
fileName: '', fileName: '',
importFile: null,
poCustomer: '',
previewRows: [], previewRows: [],
previewDisplayLimit: 500,
poOrderColumns: [],
saveProgressText: '',
missingItems: [], missingItems: [],
missingSuppliers: [], missingSuppliers: [],
supplierPriceColumns: [ supplierPriceColumns: [
@ -161,19 +195,46 @@ export default {
} }
}, },
computed: { computed: {
currentSite () {
return this.$store.state.user.site || ''
},
isPoOrderImport () {
return this.activeImportType === 'poOrder'
},
activeColumns () { activeColumns () {
if (this.isPoOrderImport) {
return this.poOrderColumns
}
return this.activeImportType === 'partSku' ? this.partSkuColumns : this.supplierPriceColumns return this.activeImportType === 'partSku' ? this.partSkuColumns : this.supplierPriceColumns
}, },
previewDisplayRows () {
return this.previewRows.slice(0, this.previewDisplayLimit)
},
dialogTitle () { dialogTitle () {
if (this.activeImportType === 'poOrder') {
return 'PO订单导入'
}
return this.activeImportType === 'partSku' ? '物料SKU导入' : '供应商价格期初导入' return this.activeImportType === 'partSku' ? '物料SKU导入' : '供应商价格期初导入'
}, },
dialogWidth () { dialogWidth () {
if (this.activeImportType === 'poOrder') {
return '94%'
}
return this.activeImportType === 'partSku' ? '680px' : '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: { methods: {
openImportDialog (type) { openImportDialog (type) {
if (type !== 'supplierPrice' && type !== 'partSku') {
if (type !== 'supplierPrice' && type !== 'partSku' && type !== 'poOrder') {
return return
} }
this.resetDialog() this.resetDialog()
@ -192,16 +253,27 @@ export default {
return return
} }
this.fileName = file.name || '' this.fileName = file.name || ''
this.importFile = rawFile
this.saveProgressText = ''
if (this.isPoOrderImport && !this.poCustomer) {
this.$message.warning('请先选择 Customer')
this.clearSelectedFile()
return
}
this.previewRows = [] this.previewRows = []
this.poOrderColumns = []
this.missingItems = [] this.missingItems = []
this.missingSuppliers = [] this.missingSuppliers = []
this.previewLoading = true 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) { if (data && data.code === 0) {
this.previewRows = data.data || [] this.previewRows = data.data || []
this.missingItems = data.missingItems || [] this.missingItems = data.missingItems || []
this.missingSuppliers = data.missingSuppliers || [] this.missingSuppliers = data.missingSuppliers || []
if (this.isPoOrderImport) {
this.poOrderColumns = this.buildPoOrderColumns(data.columnMapping || {})
}
this.$message.success(`Excel读取成功,共${this.previewRows.length}行`) this.$message.success(`Excel读取成功,共${this.previewRows.length}行`)
this.showMissingDataAlert() this.showMissingDataAlert()
} else { } else {
@ -218,6 +290,39 @@ export default {
this.previewLoading = false 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) { handleFileExceed (files) {
if (this.previewLoading || !files || !files.length) { if (this.previewLoading || !files || !files.length) {
return return
@ -226,13 +331,18 @@ export default {
this.handleFileChange({ raw: files[0], name: files[0].name }) this.handleFileChange({ raw: files[0], name: files[0].name })
}, },
handleDownloadTemplate () { 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 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 => { downloadRequest().then(response => {
const blob = new Blob([response.data], { const blob = new Blob([response.data], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@ -251,7 +361,32 @@ export default {
this.templateDownloading = false 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 () { 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) { if (!this.previewRows.length) {
this.$message.warning('没有可保存的导入数据') this.$message.warning('没有可保存的导入数据')
return return
@ -303,6 +438,51 @@ export default {
this.saving = false 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 () { showMissingDataAlert () {
if (!this.missingItems.length && !this.missingSuppliers.length) { if (!this.missingItems.length && !this.missingSuppliers.length) {
return return
@ -311,8 +491,8 @@ export default {
if (this.missingItems.length) { if (this.missingItems.length) {
messages.push(`以下Item不存在:${this.missingItems.join('、')}`) 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( const content = this.$createElement(
'div', 'div',
@ -334,6 +514,7 @@ export default {
}, },
clearSelectedFile () { clearSelectedFile () {
this.fileName = '' this.fileName = ''
this.importFile = null
if (this.$refs.initialImportUpload) { if (this.$refs.initialImportUpload) {
this.$refs.initialImportUpload.clearFiles() this.$refs.initialImportUpload.clearFiles()
} }
@ -346,6 +527,9 @@ export default {
this.previewRows = [] this.previewRows = []
this.missingItems = [] this.missingItems = []
this.missingSuppliers = [] this.missingSuppliers = []
this.poOrderColumns = []
this.saveProgressText = ''
this.poCustomer = ''
this.clearSelectedFile() this.clearSelectedFile()
} }
} }
@ -379,11 +563,20 @@ export default {
.dialog-toolbar { .dialog-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap;
min-height: 40px; min-height: 40px;
margin-bottom: 12px; margin-bottom: 12px;
gap: 10px; gap: 10px;
} }
.po-import-customer-picker {
width: 100%;
}
.po-import-customer-picker /deep/ .el-form-item {
margin-bottom: 0;
}
.file-name { .file-name {
display: inline-block; display: inline-block;
max-width: 360px; max-width: 360px;
@ -411,6 +604,37 @@ export default {
background-color: #fff0b3 !important; 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 { .dialog-footer {
text-align: right; text-align: right;
} }

Loading…
Cancel
Save