Browse Source

IFS库存初始化导入

master
han\hanst 5 months ago
parent
commit
703a355c71
  1. 3
      src/api/warehouse/ifsInventoryInit.js
  2. 91
      src/views/modules/warehouse/ifsInventoryInit.vue
  3. 313
      src/views/modules/warehouse/ifsInventory_upload_excel.vue

3
src/api/warehouse/ifsInventoryInit.js

@ -14,3 +14,6 @@ export const printLabel = data => createAPI('/label/setting/printLabel','post',d
// 修改已打印数量 // 修改已打印数量
export const updatePrintQty = data => createAPI(`ifsInventoryInit/updatePrintQty`, 'POST', data) export const updatePrintQty = data => createAPI(`ifsInventoryInit/updatePrintQty`, 'POST', data)
// 导入IFS库存Excel
export const uploadIfsInventoryExcel = data => createAPI(`ifsInventoryInit/uploadIfsInventoryExcel`, 'POST', data)

91
src/views/modules/warehouse/ifsInventoryInit.vue

@ -1,5 +1,5 @@
<template> <template>
<div class="mod-config">
<div class="mod-config" style="margin-top: 10px">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item> <el-form-item>
<el-input v-model="dataForm.partNo" placeholder="物料编码" clearable></el-input> <el-input v-model="dataForm.partNo" placeholder="物料编码" clearable></el-input>
@ -20,6 +20,8 @@
<el-button @click="getDataList()">查询</el-button> <el-button @click="getDataList()">查询</el-button>
<el-button @click="resetForm()">重置</el-button> <el-button @click="resetForm()">重置</el-button>
<el-button type="primary" @click="batchCreateHu()">批量创建HU</el-button> <el-button type="primary" @click="batchCreateHu()">批量创建HU</el-button>
<el-button type="success" @click="openImportDialog()">导入Excel</el-button>
<el-button type="danger" @click="batchDeleteInventory()">批量删除</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -214,6 +216,9 @@
</div> </div>
</el-dialog> </el-dialog>
<!-- Excel导入对话框 -->
<ifs-inventory-upload-excel ref="ifsInventoryUploadExcel" @refreshTable="getDataList"></ifs-inventory-upload-excel>
<!-- 创建HU弹窗 --> <!-- 创建HU弹窗 -->
<el-dialog <el-dialog
title="创建HandlingUnit" title="创建HandlingUnit"
@ -338,8 +343,12 @@
<script> <script>
import { getInventoryStockList, createHandlingUnits, batchCreateHandlingUnits, printLabel, updatePrintQty } from '@/api/warehouse/ifsInventoryInit' import { getInventoryStockList, createHandlingUnits, batchCreateHandlingUnits, printLabel, updatePrintQty } from '@/api/warehouse/ifsInventoryInit'
import IfsInventoryUploadExcel from './ifsInventory_upload_excel.vue'
export default { export default {
components: {
IfsInventoryUploadExcel
},
data () { data () {
return { return {
dataForm: { dataForm: {
@ -817,6 +826,86 @@ export default {
this.getDataList() this.getDataList()
}, },
/**
* 打开导入对话框
*/
openImportDialog() {
this.$refs.ifsInventoryUploadExcel.init()
},
/**
* 批量删除IFS库存数据
* 真实删除不可恢复需要用户慎重确认
*/
batchDeleteInventory() {
//
if (!this.dataListSelections || this.dataListSelections.length === 0) {
this.$message.error('请至少选择一条数据进行删除!')
return
}
this.$confirm(
'⚠️ 警告:此操作将永久删除这些数据,无法恢复!\n\n请再次确认是否要删除?',
'确认',
{
confirmButtonText: '确认删除',
cancelButtonText: '我再想想',
type: 'error',
confirmButtonClass: 'el-button--danger',
distinguishCancelAndClose: true
}
).then(() => {
this.executeDeleteInventory()
}).catch(() => {
this.$message.info('已取消删除操作')
})
},
/**
* 执行批量删除操作
*/
executeDeleteInventory() {
const loading = this.$loading({
lock: true,
text: '正在删除数据,请稍候...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
//
const deleteItems = this.dataListSelections.map(row => ({
site: row.site,
warehouseId: row.warehouseId,
partNo: row.partNo,
batchNo: row.batchNo,
locationId: row.locationId,
wdr: row.wdr
}))
const requestData = {
items: deleteItems
}
this.$http({
url: this.$http.adornUrl('/ifsInventoryInit/batchDeleteInventory'),
method: 'post',
data: this.$http.adornData(requestData)
}).then(({ data }) => {
loading.close()
if (data && data.code === 0) {
this.$message.success(`批量删除成功!共删除 ${deleteItems.length} 条记录`)
//
this.getDataList()
} else {
this.$message.error(data.msg || '批量删除失败')
}
}).catch(error => {
loading.close()
this.$message.error('批量删除异常: ' + (error.message || '未知错误'))
})
},
// //
formatDate(date) { formatDate(date) {
if (!date) return '--' if (!date) return '--'

313
src/views/modules/warehouse/ifsInventory_upload_excel.vue

@ -0,0 +1,313 @@
<template>
<div class="customer-css">
<el-dialog
title="导入IFS库存数据"
:close-on-click-modal="false"
:visible.sync="visible"
width="450px"
class="customer-dialog"
@close="handleDialogClose"
:show-close="!uploading">
<el-form :inline="true" label-position="top" label-width="80px">
<el-row>
<el-form-item label=" ">
<el-button
type="primary"
icon="el-icon-download"
:disabled="uploading"
@click="downloadTemplate">
下载 Excel 模板
</el-button>
</el-form-item>
</el-row>
<el-row>
<el-col :span="24">
<el-upload
class="customer-upload"
drag
action="javascript:void(0);"
ref="uploadFile"
:limit="1"
accept=".xlsx,.xls"
:before-upload="beforeUploadHandle"
:on-change="onChange"
:auto-upload="false"
:disabled="uploading"
style="text-align: left;">
<i class="el-icon-upload"></i>
<div class="el-upload__text">
{{ uploading ? '正在上传中,请稍候...' : '将文件拖到此处,或点击上传' }}
</div>
</el-upload>
</el-col>
</el-row>
<el-row style="margin-top: 15px;">
<el-alert
title="导入说明"
type="info"
:closable="false"
show-icon>
<div style="font-size: 12px; line-height: 1.8;">
1. 请先下载模板按模板格式填写数据<br/>
2. 必填字段SitePart NoLot/Batch NoLocation NoQty On Hand<br/>
3. A-K列库存信息保存到inventory_stock_ifs表<br/>
4. L-U列料件属性信息保存到part_attribute表可选填<br/>
5. 料件属性如已存在会自动跳过不会报错<br/>
6. 日期格式YYYY/M/D YYYY-MM-DD
</div>
</el-alert>
</el-row>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="saveUploadFile" :loading="uploading" :disabled="uploading">
{{ uploading ? '上传中...' : '开始导入' }}
</el-button>
<el-button @click="closeDialog" :disabled="uploading">关闭</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { uploadIfsInventoryExcel } from '@/api/warehouse/ifsInventoryInit.js'
export default {
name: 'ifsInventoryUploadExcel',
data() {
return {
visible: false,
fileList: [],
uploading: false //
}
},
methods: {
/**
* 初始化组件
*/
init() {
this.fileList = []
this.uploading = false
this.visible = true
},
/**
* 上传之前的验证
*/
beforeUploadHandle(file) {
let extName = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
if (!(extName === '.xlsx' || extName === '.xls')) {
this.$message.error('数据导入失败,请选择正确的xlsx或xls格式的Excel文件')
return false
}
return true
},
/**
* 选择上传文件时
*/
onChange(file) {
//
this.fileList = []
this.fileList.push(file)
},
/**
* 关闭弹窗
*/
closeDialog() {
this.deleteFile()
this.visible = false
},
/**
* 处理弹窗关闭事件包括右上角X按钮
*/
handleDialogClose() {
if (this.uploading) {
this.$message.warning('正在上传中,请稍候...')
return
}
this.deleteFile()
},
/**
* 清除文件
*/
deleteFile() {
this.fileList = []
this.uploading = false
//
this.$refs.uploadFile.clearFiles()
//
this.$emit('refreshTable')
},
/**
* 格式化错误信息为HTML列表
*/
formatErrorMessages(errorMessages) {
if (!errorMessages || errorMessages.length === 0) {
return ''
}
// 20
const displayMessages = errorMessages.slice(0, 20)
const hasMore = errorMessages.length > 20
let html = '<div style="text-align: left; max-height: 400px; overflow-y: auto;">'
html += '<p style="color: #E6A23C; font-weight: bold; margin-bottom: 10px;">以下数据导入失败:</p>'
html += '<ol style="padding-left: 20px; line-height: 1.8;">'
displayMessages.forEach(msg => {
html += `<li style="color: #F56C6C; margin-bottom: 5px;">${msg}</li>`
})
html += '</ol>'
if (hasMore) {
html += `<p style="color: #909399; margin-top: 10px;">...还有 ${errorMessages.length - 20} 条错误</p>`
}
html += '</div>'
return html
},
/**
* 下载Excel模板
*/
downloadTemplate() {
const loading = this.$loading({
lock: true,
text: '正在下载模板...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.$http({
url: this.$http.adornUrl('/ifsInventoryInit/downloadTemplate'),
method: 'get',
responseType: 'blob'
}).then(response => {
loading.close()
//
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 = 'IFS库存导入模板.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
this.$message.success('模板下载成功')
}).catch(error => {
loading.close()
console.error('模板下载失败:', error)
this.$message.error('模板下载失败,请联系管理员')
})
},
/**
* 保存上传文件
*/
saveUploadFile() {
//
if (null == this.fileList || 0 === this.fileList.length) {
this.$message.error("请先选择要上传的Excel文件!")
return false
}
//
const file = this.fileList[0].raw
const fileName = file.name
const fileExtension = fileName.substring(fileName.lastIndexOf('.')).toLowerCase()
if (!('.xls' === fileExtension || '.xlsx' === fileExtension)) {
this.$message.error('文件格式不正确,只支持.xls和.xlsx格式的Excel文件')
return false
}
//
this.uploading = true
// FormData
const formData = new FormData()
formData.append("file", file)
formData.append("site", localStorage.getItem('site'))
formData.append("uploadBy", this.$store.state.user.name)
//
uploadIfsInventoryExcel(formData).then(({ data }) => {
if (data.code === 0) {
//
const errorMessages = data.errorMessages || []
if (errorMessages.length > 0) {
// 使
const errorHtml = this.formatErrorMessages(errorMessages)
this.$alert(errorHtml, '导入完成(部分数据失败)', {
confirmButtonText: '确定',
dangerouslyUseHTMLString: true,
customClass: 'import-error-dialog'
}).then(() => {
//
this.closeDialog()
})
} else {
//
this.$message({
message: data.msg || 'IFS库存数据导入成功',
type: 'success',
duration: 2000
})
//
this.closeDialog()
}
} else {
this.$alert(data.msg, '错误', {
confirmButtonText: '确定'
})
}
}).catch(error => {
console.error('Excel上传异常:', error)
this.$message.error('Excel文件上传异常,请检查文件格式和内容')
}).finally(() => {
//
this.uploading = false
})
}
}
}
</script>
<style scoped>
.customer-css .customer-upload {
width: 100%;
}
.customer-css .el-upload {
width: 100%;
}
.customer-css .el-upload-dragger {
width: 100%;
}
</style>
<style>
/* 导入错误弹窗样式 */
.import-error-dialog {
width: 600px !important;
}
.import-error-dialog .el-message-box__message {
max-height: 500px;
overflow-y: auto;
}
</style>
Loading…
Cancel
Save