2 changed files with 411 additions and 0 deletions
@ -0,0 +1,29 @@ |
|||||
|
import http from '@/utils/httpRequest' |
||||
|
|
||||
|
export const downloadSupplierPriceTemplate = () => http({ |
||||
|
url: http.adornUrl('/sys/initialImport/supplierPrice/template'), |
||||
|
method: 'get', |
||||
|
responseType: 'blob' |
||||
|
}) |
||||
|
|
||||
|
export const previewSupplierPrice = (file, site) => { |
||||
|
const formData = new FormData() |
||||
|
formData.append('file', file) |
||||
|
formData.append('site', site) |
||||
|
return http({ |
||||
|
url: http.adornUrl('/sys/initialImport/supplierPrice/preview'), |
||||
|
method: 'post', |
||||
|
data: formData, |
||||
|
headers: { |
||||
|
'Content-Type': 'multipart/form-data' |
||||
|
}, |
||||
|
timeout: 120000 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export const saveSupplierPrice = data => http({ |
||||
|
url: http.adornUrl('/sys/initialImport/supplierPrice/save'), |
||||
|
method: 'post', |
||||
|
data: http.adornData(data), |
||||
|
timeout: 600000 |
||||
|
}) |
||||
@ -0,0 +1,382 @@ |
|||||
|
<template> |
||||
|
<div class="mod-initial-import"> |
||||
|
<div class="page-toolbar"> |
||||
|
<div> |
||||
|
<div class="page-title">期初导入</div> |
||||
|
<div class="page-subtitle">按业务类型导入系统启用前的基础数据</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="rq"> |
||||
|
<el-table :data="importItems" border style="width: 100%"> |
||||
|
<el-table-column prop="name" label="期初导入功能" min-width="220" /> |
||||
|
<el-table-column prop="description" label="说明" min-width="520" show-overflow-tooltip /> |
||||
|
<el-table-column label="操作" width="220" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
icon="el-icon-upload2" |
||||
|
@click="openImportDialog(scope.row.type)"> |
||||
|
{{ scope.row.name }} |
||||
|
</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
</div> |
||||
|
<el-dialog |
||||
|
title="供应商价格期初导入" |
||||
|
v-drag |
||||
|
:visible.sync="dialogVisible" |
||||
|
width="94%" |
||||
|
top="5vh" |
||||
|
:show-close="!saving" |
||||
|
:close-on-click-modal="false" |
||||
|
:close-on-press-escape="!saving" |
||||
|
:before-close="handleDialogClose" |
||||
|
@closed="resetDialog"> |
||||
|
<div class="dialog-toolbar"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
icon="el-icon-download" |
||||
|
:loading="templateDownloading" |
||||
|
:disabled="previewLoading || saving" |
||||
|
@click="handleDownloadTemplate"> |
||||
|
下载导入模板 |
||||
|
</el-button> |
||||
|
<el-upload |
||||
|
ref="supplierPriceUpload" |
||||
|
action="#" |
||||
|
accept=".xls,.xlsx" |
||||
|
:auto-upload="false" |
||||
|
:show-file-list="false" |
||||
|
:limit="1" |
||||
|
:disabled="previewLoading || saving" |
||||
|
:on-change="handleFileChange" |
||||
|
:on-exceed="handleFileExceed"> |
||||
|
<el-button |
||||
|
icon="el-icon-upload2" |
||||
|
:loading="previewLoading" |
||||
|
:disabled="previewLoading || saving"> |
||||
|
选择导入文件 |
||||
|
</el-button> |
||||
|
</el-upload> |
||||
|
<span class="file-name" :title="fileName">{{ fileName || '未选择文件' }}</span> |
||||
|
<span class="preview-count">已读取 {{ previewRows.length }} 行</span> |
||||
|
</div> |
||||
|
|
||||
|
<el-table |
||||
|
v-loading="previewLoading" |
||||
|
:data="previewRows" |
||||
|
border |
||||
|
height="520" |
||||
|
size="mini" |
||||
|
empty-text="请选择Excel文件进行预览" |
||||
|
:row-class-name="previewRowClassName" |
||||
|
class="supplier-price-preview"> |
||||
|
<el-table-column |
||||
|
v-for="column in columns" |
||||
|
:key="column.prop" |
||||
|
:prop="column.prop" |
||||
|
:label="column.label" |
||||
|
:min-width="column.width" |
||||
|
:align="column.align" |
||||
|
show-overflow-tooltip /> |
||||
|
</el-table> |
||||
|
|
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
icon="el-icon-check" |
||||
|
:loading="saving" |
||||
|
:disabled="previewLoading || saving || !previewRows.length" |
||||
|
@click="handleSave"> |
||||
|
保存 |
||||
|
</el-button> |
||||
|
<el-button :disabled="previewLoading || saving" @click="dialogVisible = false">关闭</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
downloadSupplierPriceTemplate, |
||||
|
previewSupplierPrice, |
||||
|
saveSupplierPrice |
||||
|
} from '@/api/initialImport' |
||||
|
|
||||
|
export default { |
||||
|
name: 'SysInitialImport', |
||||
|
data () { |
||||
|
return { |
||||
|
importItems: [ |
||||
|
{ |
||||
|
type: 'supplierPrice', |
||||
|
name: '供应商价格期初导入', |
||||
|
description: '导入供应商物料的MOQ、阶梯数量和阶梯价格。' |
||||
|
} |
||||
|
], |
||||
|
dialogVisible: false, |
||||
|
previewLoading: false, |
||||
|
saving: false, |
||||
|
templateDownloading: false, |
||||
|
fileName: '', |
||||
|
previewRows: [], |
||||
|
missingItems: [], |
||||
|
missingSuppliers: [], |
||||
|
columns: [ |
||||
|
{ prop: 'rawCode', label: 'RAW CODE', width: 120, align: 'center' }, |
||||
|
{ prop: 'supplier', label: 'Short_NPC', width: 100, align: 'center' }, |
||||
|
{ prop: 'moqColor', label: 'MOQ/COLOR', width: 80, align: 'center' }, |
||||
|
{ prop: 'startDate', label: 'START DATE', width: 100, align: 'center' }, |
||||
|
{ prop: 'tierQty1', label: 'Tier_QTY_1', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty2', label: 'Tier_QTY_2', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty3', label: 'Tier_QTY_3', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty4', label: 'Tier_QTY_4', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty5', label: 'Tier_QTY_5', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty6', label: 'Tier_QTY_6', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty7', label: 'Tier_QTY_7', width: 80, align: 'center' }, |
||||
|
{ prop: 'tierQty8', label: 'Tier_QTY_8', width: 80, align: 'center' }, |
||||
|
{ prop: 'priceTier1', label: 'Price_Tier_1', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier2', label: 'Price_Tier_2', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier3', label: 'Price_Tier_3', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier4', label: 'Price_Tier_4', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier5', label: 'Price_Tier_5', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier6', label: 'Price_Tier_6', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier7', label: 'Price_Tier_7', width: 90, align: 'center' }, |
||||
|
{ prop: 'priceTier8', label: 'Price_Tier_8', width: 90, align: 'center' } |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
openImportDialog (type) { |
||||
|
if (type !== 'supplierPrice') { |
||||
|
return |
||||
|
} |
||||
|
this.dialogVisible = true |
||||
|
}, |
||||
|
handleFileChange (file) { |
||||
|
const rawFile = file && file.raw |
||||
|
if (!rawFile) { |
||||
|
return |
||||
|
} |
||||
|
const site = this.$store.state.user.site |
||||
|
if (!site) { |
||||
|
this.$message.error('当前登录用户没有Site') |
||||
|
this.clearSelectedFile() |
||||
|
return |
||||
|
} |
||||
|
this.fileName = file.name || '' |
||||
|
this.previewRows = [] |
||||
|
this.missingItems = [] |
||||
|
this.missingSuppliers = [] |
||||
|
this.previewLoading = true |
||||
|
previewSupplierPrice(rawFile, site).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
this.previewRows = data.data || [] |
||||
|
this.missingItems = data.missingItems || [] |
||||
|
this.missingSuppliers = data.missingSuppliers || [] |
||||
|
this.$message.success(`Excel读取成功,共${this.previewRows.length}行`) |
||||
|
this.showMissingDataAlert() |
||||
|
} else { |
||||
|
this.$message.error((data && data.msg) || 'Excel读取失败') |
||||
|
this.clearSelectedFile() |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
const message = error && error.response && error.response.data && error.response.data.msg |
||||
|
? error.response.data.msg |
||||
|
: 'Excel读取失败,请检查文件格式' |
||||
|
this.$message.error(message) |
||||
|
this.clearSelectedFile() |
||||
|
}).finally(() => { |
||||
|
this.previewLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
handleFileExceed (files) { |
||||
|
if (this.previewLoading || !files || !files.length) { |
||||
|
return |
||||
|
} |
||||
|
this.clearSelectedFile() |
||||
|
this.handleFileChange({ raw: files[0], name: files[0].name }) |
||||
|
}, |
||||
|
handleDownloadTemplate () { |
||||
|
this.templateDownloading = true |
||||
|
downloadSupplierPriceTemplate().then(response => { |
||||
|
const blob = new Blob([response.data], { |
||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' |
||||
|
}) |
||||
|
const url = window.URL.createObjectURL(blob) |
||||
|
const link = document.createElement('a') |
||||
|
link.href = url |
||||
|
link.download = '供应商价格期初导入模板.xlsx' |
||||
|
document.body.appendChild(link) |
||||
|
link.click() |
||||
|
document.body.removeChild(link) |
||||
|
window.URL.revokeObjectURL(url) |
||||
|
}).catch(() => { |
||||
|
this.$message.error('模板下载失败,请联系管理员') |
||||
|
}).finally(() => { |
||||
|
this.templateDownloading = false |
||||
|
}) |
||||
|
}, |
||||
|
handleSave () { |
||||
|
if (!this.previewRows.length) { |
||||
|
this.$message.warning('没有可保存的导入数据') |
||||
|
return |
||||
|
} |
||||
|
const site = this.$store.state.user.site |
||||
|
if (!site) { |
||||
|
this.$message.error('当前登录用户没有Site') |
||||
|
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) |
||||
|
}, |
||||
|
performSave (site) { |
||||
|
this.saving = true |
||||
|
saveSupplierPrice({ |
||||
|
site, |
||||
|
rows: this.previewRows |
||||
|
}).then(({ data }) => { |
||||
|
if (data && data.code === 0) { |
||||
|
const savedCount = data.savedCount == null ? this.previewRows.length : data.savedCount |
||||
|
const skippedCount = data.skippedCount || 0 |
||||
|
const skippedText = skippedCount > 0 ? `,跳过${skippedCount}行` : '' |
||||
|
this.$message.success(`保存完成,成功${savedCount}行${skippedText}`) |
||||
|
this.dialogVisible = false |
||||
|
} else { |
||||
|
this.$message.error((data && data.msg) || '保存失败') |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
const message = error && error.response && error.response.data && error.response.data.msg |
||||
|
? error.response.data.msg |
||||
|
: '保存失败,请稍后重试' |
||||
|
this.$message.error(message) |
||||
|
}).finally(() => { |
||||
|
this.saving = false |
||||
|
}) |
||||
|
}, |
||||
|
showMissingDataAlert () { |
||||
|
if (!this.missingItems.length && !this.missingSuppliers.length) { |
||||
|
return |
||||
|
} |
||||
|
const messages = [] |
||||
|
if (this.missingItems.length) { |
||||
|
messages.push(`以下Item不存在:${this.missingItems.join('、')}`) |
||||
|
} |
||||
|
if (this.missingSuppliers.length) { |
||||
|
messages.push(`以下供应商简称不存在:${this.missingSuppliers.join('、')}`) |
||||
|
} |
||||
|
const content = this.$createElement( |
||||
|
'div', |
||||
|
{ style: { maxHeight: '360px', overflowY: 'auto', wordBreak: 'break-all', lineHeight: '1.6' } }, |
||||
|
messages.map(message => this.$createElement('p', { style: { margin: '0 0 8px' } }, message)) |
||||
|
) |
||||
|
this.$alert(content, '数据校验提示', { |
||||
|
confirmButtonText: '确定', |
||||
|
type: 'warning' |
||||
|
}).catch(() => {}) |
||||
|
}, |
||||
|
previewRowClassName ({ row }) { |
||||
|
return row && row.valid === false ? 'initial-import-warning-row' : '' |
||||
|
}, |
||||
|
handleDialogClose (done) { |
||||
|
if (!this.saving) { |
||||
|
done() |
||||
|
} |
||||
|
}, |
||||
|
clearSelectedFile () { |
||||
|
this.fileName = '' |
||||
|
if (this.$refs.supplierPriceUpload) { |
||||
|
this.$refs.supplierPriceUpload.clearFiles() |
||||
|
} |
||||
|
}, |
||||
|
resetDialog () { |
||||
|
this.previewLoading = false |
||||
|
this.saving = false |
||||
|
this.templateDownloading = false |
||||
|
this.previewRows = [] |
||||
|
this.missingItems = [] |
||||
|
this.missingSuppliers = [] |
||||
|
this.clearSelectedFile() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.mod-initial-import { |
||||
|
padding: 16px; |
||||
|
} |
||||
|
|
||||
|
.page-toolbar { |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
justify-content: space-between; |
||||
|
margin-bottom: 14px; |
||||
|
} |
||||
|
|
||||
|
.page-title { |
||||
|
color: #303133; |
||||
|
font-size: 18px; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.page-subtitle { |
||||
|
margin-top: 4px; |
||||
|
color: #909399; |
||||
|
font-size: 13px; |
||||
|
} |
||||
|
|
||||
|
.dialog-toolbar { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
min-height: 40px; |
||||
|
margin-bottom: 12px; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.file-name { |
||||
|
display: inline-block; |
||||
|
max-width: 360px; |
||||
|
overflow: hidden; |
||||
|
color: #606266; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.preview-count { |
||||
|
margin-left: auto; |
||||
|
color: #606266; |
||||
|
font-variant-numeric: tabular-nums; |
||||
|
} |
||||
|
|
||||
|
.supplier-price-preview { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.supplier-price-preview /deep/ .initial-import-warning-row > td { |
||||
|
background-color: #fff7d6 !important; |
||||
|
} |
||||
|
|
||||
|
.supplier-price-preview /deep/ .initial-import-warning-row:hover > td { |
||||
|
background-color: #fff0b3 !important; |
||||
|
} |
||||
|
|
||||
|
.dialog-footer { |
||||
|
text-align: right; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue