|
|
@ -50,7 +50,7 @@ |
|
|
</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="exportModal">导入</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 |
|
|
@ -237,64 +237,75 @@ |
|
|
</el-pagination> |
|
|
</el-pagination> |
|
|
|
|
|
|
|
|
<!-- |
|
|
<!-- |
|
|
模态框 #1:PO订单导入弹窗 |
|
|
|
|
|
用途: |
|
|
|
|
|
1) 选择 Excel 文件; |
|
|
|
|
|
2) 预览后端解析结果(含字段映射、跳过列提示); |
|
|
|
|
|
3) 确认后批量保存。 |
|
|
|
|
|
|
|
|
模态框 #1:PO订单导入弹窗(按 customer 权限动态模板) |
|
|
|
|
|
交互规则: |
|
|
|
|
|
1) 模板第一行按当前 customer 可见字段顺序生成; |
|
|
|
|
|
2) 上传后后端先校验第一行栏位,顺序允许调整,但栏位需匹配模板; |
|
|
|
|
|
3) 导入执行中所有按钮与文件选择统一置灰,避免重复提交。 |
|
|
关联状态: |
|
|
关联状态: |
|
|
uploadDialogVisible / uploadFile / uploadPreviewList / uploadColumnMapping / uploadSkippedColumns |
|
|
|
|
|
|
|
|
importDialogVisible / importFile / importSubmitting / importLastResult |
|
|
--> |
|
|
--> |
|
|
<el-dialog title="PO订单数据导入" v-drag :visible.sync="uploadDialogVisible" :height="height" width="800px"> |
|
|
|
|
|
<!-- 选择文件 --> |
|
|
|
|
|
<div style="margin-bottom:15px;display:flex;align-items:center"> |
|
|
|
|
|
<span style="margin-right:10px">请选择文件</span> |
|
|
|
|
|
<el-input v-model="uploadFileName" style="width:400px;margin-right:10px" readonly></el-input> |
|
|
|
|
|
<el-upload ref="upload" action="/api/pODetail/upload" |
|
|
|
|
|
:show-file-list="false" :on-change="handleFileChange" |
|
|
|
|
|
:auto-upload="false"> |
|
|
|
|
|
<el-button type="primary">选择文件</el-button> |
|
|
|
|
|
</el-upload> |
|
|
|
|
|
<el-button type="success" style="margin-left:10px" @click="previewUpload">上传</el-button> |
|
|
|
|
|
<el-button type="primary" @click="downloadTemplate" style="margin-left:10px"> |
|
|
|
|
|
<i class="el-icon-download"></i> 下载模板 |
|
|
|
|
|
</el-button> |
|
|
|
|
|
</div> |
|
|
|
|
|
<!-- 跳过列提示 --> |
|
|
|
|
|
|
|
|
<el-dialog |
|
|
|
|
|
title="PO订单导入" |
|
|
|
|
|
v-drag |
|
|
|
|
|
:visible.sync="importDialogVisible" |
|
|
|
|
|
width="560px" |
|
|
|
|
|
append-to-body |
|
|
|
|
|
:close-on-click-modal="false" |
|
|
|
|
|
:show-close="!importSubmitting" |
|
|
|
|
|
:close-on-press-escape="!importSubmitting" |
|
|
|
|
|
@closed="resetImportDialog"> |
|
|
<el-alert |
|
|
<el-alert |
|
|
v-if="uploadSkippedColumns.length" |
|
|
|
|
|
type="warning" |
|
|
|
|
|
|
|
|
type="info" |
|
|
:closable="false" |
|
|
:closable="false" |
|
|
show-icon |
|
|
show-icon |
|
|
style="margin-bottom:10px" |
|
|
|
|
|
:title="'已跳过列:' + uploadSkippedColumns.join('、')" |
|
|
|
|
|
/> |
|
|
|
|
|
<div v-if="uploadColumnMappingEntries.length" style="margin-bottom:10px;font-size:12px;color:#606266"> |
|
|
|
|
|
列映射: |
|
|
|
|
|
<span v-for="item in uploadColumnMappingEntries" :key="item.header" style="margin-right:12px"> |
|
|
|
|
|
{{ item.header }} → {{ item.fieldCode }} |
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
|
title="请先下载模板并填写数据;导入时会先校验第一行栏位,顺序可以调整。" |
|
|
|
|
|
style="margin-bottom: 12px;" /> |
|
|
|
|
|
<div style="display:flex;align-items:center;"> |
|
|
|
|
|
<el-input |
|
|
|
|
|
v-model="importFileName" |
|
|
|
|
|
style="width:320px;margin-right:10px;" |
|
|
|
|
|
:disabled="importSubmitting" |
|
|
|
|
|
readonly |
|
|
|
|
|
placeholder="请选择导入文件" /> |
|
|
|
|
|
<el-upload |
|
|
|
|
|
ref="importUpload" |
|
|
|
|
|
action="#" |
|
|
|
|
|
:show-file-list="false" |
|
|
|
|
|
:auto-upload="false" |
|
|
|
|
|
:disabled="importSubmitting" |
|
|
|
|
|
:on-change="handleImportFileChange"> |
|
|
|
|
|
<el-button type="primary" :disabled="importSubmitting">选择文件</el-button> |
|
|
|
|
|
</el-upload> |
|
|
</div> |
|
|
</div> |
|
|
<!-- 预览表格 --> |
|
|
|
|
|
<el-table |
|
|
|
|
|
:data="uploadPreviewList" |
|
|
|
|
|
border |
|
|
|
|
|
height="250" |
|
|
|
|
|
style="width:100%;margin-top:10px" |
|
|
|
|
|
> |
|
|
|
|
|
<el-table-column |
|
|
|
|
|
v-for="col in uploadPreviewColumns" |
|
|
|
|
|
:key="col.prop" |
|
|
|
|
|
:prop="col.prop" |
|
|
|
|
|
:label="col.label" |
|
|
|
|
|
align="center" |
|
|
|
|
|
min-width="100" |
|
|
|
|
|
/> |
|
|
|
|
|
</el-table> |
|
|
|
|
|
<!-- 按钮 --> |
|
|
|
|
|
<div style="margin-top:20px;text-align:center"> |
|
|
|
|
|
<el-button type="primary" @click="confirmUpload">保存</el-button> |
|
|
|
|
|
<el-button @click="uploadDialogVisible=false" style="margin-right:30px">关闭</el-button> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="importLastResult" style="margin-top: 14px; font-size: 13px; color: #606266;"> |
|
|
|
|
|
<div> |
|
|
|
|
|
导入结果:成功 {{ importLastResult.successCount || 0 }} 条,失败 {{ importLastResult.failCount || 0 }} 条 |
|
|
|
|
|
</div> |
|
|
|
|
|
<div |
|
|
|
|
|
v-if="importLastResult.rowErrors && importLastResult.rowErrors.length" |
|
|
|
|
|
style="margin-top: 8px; max-height: 160px; overflow: auto;"> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="item in importLastResult.rowErrors.slice(0, 20)" |
|
|
|
|
|
:key="`import-error-${item.rowNo}-${item.message}`" |
|
|
|
|
|
style="line-height: 1.6;"> |
|
|
|
|
|
第{{ item.rowNo }}行:{{ item.message }} |
|
|
|
|
|
</div> |
|
|
|
|
|
<div v-if="importLastResult.rowErrors.length > 20" style="color:#909399;"> |
|
|
|
|
|
仅展示前20条失败原因,请按失败提示分批修复后重试。 |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div slot="footer" class="dialog-footer"> |
|
|
|
|
|
<el-button @click="downloadImportTemplate" :disabled="importSubmitting">下载模板</el-button> |
|
|
|
|
|
<el-button |
|
|
|
|
|
type="primary" |
|
|
|
|
|
@click="submitImportCreatePoOrder" |
|
|
|
|
|
:loading="importSubmitting" |
|
|
|
|
|
:disabled="importSubmitting || !importFile">开始导入</el-button> |
|
|
|
|
|
<el-button @click="importDialogVisible = false" :disabled="importSubmitting">关闭</el-button> |
|
|
</div> |
|
|
</div> |
|
|
</el-dialog> |
|
|
</el-dialog> |
|
|
|
|
|
|
|
|
@ -618,7 +629,6 @@ import { |
|
|
applyFieldSchemaToColumns, |
|
|
applyFieldSchemaToColumns, |
|
|
getAdvancedQueryFieldsFromSchema, |
|
|
getAdvancedQueryFieldsFromSchema, |
|
|
getEditableExtFieldsFromSchema, |
|
|
getEditableExtFieldsFromSchema, |
|
|
buildImportPreviewColumns, |
|
|
|
|
|
normalizeSchemaRowValues, |
|
|
normalizeSchemaRowValues, |
|
|
buildExtValuesFromRow |
|
|
buildExtValuesFromRow |
|
|
} from './poOrderFieldSchemaHelper.js' |
|
|
} from './poOrderFieldSchemaHelper.js' |
|
|
@ -683,8 +693,8 @@ const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = { |
|
|
* - fieldSchemaFields/baseColumnList/columnList: 字段schema与表格列配置(动态列渲染核心) |
|
|
* - fieldSchemaFields/baseColumnList/columnList: 字段schema与表格列配置(动态列渲染核心) |
|
|
* - dictSelectOptionsMap/addDictSelectOptionsMap: 字典下拉选项缓存(列表/新增分开缓存) |
|
|
* - dictSelectOptionsMap/addDictSelectOptionsMap: 字典下拉选项缓存(列表/新增分开缓存) |
|
|
* - columnSelectOptions: 常用下拉配置集合(客户、买手、状态、是/否、运输方式等) |
|
|
* - columnSelectOptions: 常用下拉配置集合(客户、买手、状态、是/否、运输方式等) |
|
|
* - uploadDialogVisible/uploadFile/uploadPreviewList/uploadColumnMapping/uploadPreviewColumns/uploadSkippedColumns: |
|
|
|
|
|
* 导入流程全量状态 |
|
|
|
|
|
|
|
|
* - importDialogVisible/importFile/importSubmitting/importLastResult: |
|
|
|
|
|
* 导入流程状态(模板下载、导入执行、失败反馈) |
|
|
* - addDialogVisible/addForm/addFormSchemaFields/addFormSaving: |
|
|
* - addDialogVisible/addForm/addFormSchemaFields/addFormSaving: |
|
|
* 新增主弹窗状态(主记录) |
|
|
* 新增主弹窗状态(主记录) |
|
|
* - addDetailDialogVisible/addDetailDialogMode/addDetailForm/addDetailRows/addDetailCurrentDraftId/addDetailEditingDraftId: |
|
|
* - addDetailDialogVisible/addDetailDialogMode/addDetailForm/addDetailRows/addDetailCurrentDraftId/addDetailEditingDraftId: |
|
|
@ -697,7 +707,7 @@ const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = { |
|
|
* 行编辑阶段的辅助暂存(用于复杂选择组件联动) |
|
|
* 行编辑阶段的辅助暂存(用于复杂选择组件联动) |
|
|
* |
|
|
* |
|
|
* 二、模态框与子弹层说明 |
|
|
* 二、模态框与子弹层说明 |
|
|
* - uploadDialogVisible: PO订单导入(文件上传、预览、批量保存) |
|
|
|
|
|
|
|
|
* - importDialogVisible: PO订单导入(动态模板 + 逐行导入) |
|
|
* - addDialogVisible: 新增PO订单(主记录 + 明细草稿) |
|
|
* - addDialogVisible: 新增PO订单(主记录 + 明细草稿) |
|
|
* - addDetailDialogVisible: 新增明细(新增/编辑/复制) |
|
|
* - addDetailDialogVisible: 新增明细(新增/编辑/复制) |
|
|
* - supplierPartDialogVisible: 供应商可供物料选择 |
|
|
* - supplierPartDialogVisible: 供应商可供物料选择 |
|
|
@ -744,8 +754,8 @@ const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = { |
|
|
* - getTransportOptions, handleSupplierChange |
|
|
* - getTransportOptions, handleSupplierChange |
|
|
* |
|
|
* |
|
|
* I. 导入导出 |
|
|
* I. 导入导出 |
|
|
* - exportModal, handleFileChange, previewUpload, refreshUploadPreviewColumns |
|
|
|
|
|
* - confirmUpload, downloadTemplate, exportExcel |
|
|
|
|
|
|
|
|
* - openImportDialog, handleImportFileChange, submitImportCreatePoOrder |
|
|
|
|
|
* - downloadImportTemplate, resetImportDialog, exportExcel |
|
|
* |
|
|
* |
|
|
* J. 新增流程(主记录 + 明细) |
|
|
* J. 新增流程(主记录 + 明细) |
|
|
* - resolveAddOrderTypeByCustomer, openAddDialog, loadAddFieldSchema |
|
|
* - resolveAddOrderTypeByCustomer, openAddDialog, loadAddFieldSchema |
|
|
@ -809,14 +819,12 @@ export default { |
|
|
displayOnlyColumnProps: ['shortOffice', 'shortNpc', 'item', 'categoryName', 'sku', 'planner'], |
|
|
displayOnlyColumnProps: ['shortOffice', 'shortNpc', 'item', 'categoryName', 'sku', 'planner'], |
|
|
customEditColumnProps: [], |
|
|
customEditColumnProps: [], |
|
|
|
|
|
|
|
|
// ===== 导入弹窗状态 ===== |
|
|
|
|
|
uploadDialogVisible: false, |
|
|
|
|
|
uploadFileName: '', |
|
|
|
|
|
uploadFile: null, |
|
|
|
|
|
uploadPreviewList: [], |
|
|
|
|
|
uploadSkippedColumns: [], |
|
|
|
|
|
uploadColumnMapping: {}, |
|
|
|
|
|
uploadPreviewColumns: [], |
|
|
|
|
|
|
|
|
// ===== 导入弹窗状态(新导入:模板下载 + 直接执行)===== |
|
|
|
|
|
importDialogVisible: false, |
|
|
|
|
|
importFileName: '', |
|
|
|
|
|
importFile: null, |
|
|
|
|
|
importSubmitting: false, |
|
|
|
|
|
importLastResult: null, |
|
|
|
|
|
|
|
|
// ===== 新增主弹窗 + 明细弹窗状态 ===== |
|
|
// ===== 新增主弹窗 + 明细弹窗状态 ===== |
|
|
addDialogVisible: false, |
|
|
addDialogVisible: false, |
|
|
@ -1986,15 +1994,6 @@ export default { |
|
|
return '复制明细' |
|
|
return '复制明细' |
|
|
} |
|
|
} |
|
|
return '新增明细' |
|
|
return '新增明细' |
|
|
}, |
|
|
|
|
|
uploadColumnMappingEntries () { |
|
|
|
|
|
if (!this.uploadColumnMapping || typeof this.uploadColumnMapping !== 'object') { |
|
|
|
|
|
return [] |
|
|
|
|
|
} |
|
|
|
|
|
return Object.keys(this.uploadColumnMapping).map(header => ({ |
|
|
|
|
|
header, |
|
|
|
|
|
fieldCode: this.uploadColumnMapping[header] |
|
|
|
|
|
})) |
|
|
|
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
created () { |
|
|
created () { |
|
|
@ -3391,69 +3390,45 @@ export default { |
|
|
}) |
|
|
}) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// ========== 上传功能 ========== |
|
|
|
|
|
// 打开上传弹窗 |
|
|
|
|
|
exportModal() { |
|
|
|
|
|
|
|
|
// ========== 导入功能(新实现) ========== |
|
|
|
|
|
/** |
|
|
|
|
|
* 打开导入弹窗并重置状态。 |
|
|
|
|
|
* 业务意图: |
|
|
|
|
|
* 1) 每次打开都清空上一次文件与结果,避免误用历史数据; |
|
|
|
|
|
* 2) 强制用户先选 customer,确保模板字段与导入权限与 customer 一致。 |
|
|
|
|
|
*/ |
|
|
|
|
|
openImportDialog () { |
|
|
if (!this.searchData.customer) { |
|
|
if (!this.searchData.customer) { |
|
|
this.$message.warning('请先选择 Customer') |
|
|
this.$message.warning('请先选择 Customer') |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
this.uploadDialogVisible = true |
|
|
|
|
|
this.uploadFileName = '' |
|
|
|
|
|
this.uploadFile = null |
|
|
|
|
|
this.uploadPreviewList = [] |
|
|
|
|
|
this.uploadSkippedColumns = [] |
|
|
|
|
|
this.uploadColumnMapping = {} |
|
|
|
|
|
this.uploadPreviewColumns = [] |
|
|
|
|
|
}, |
|
|
|
|
|
// 文件选择变化 |
|
|
|
|
|
handleFileChange(file) { |
|
|
|
|
|
this.uploadFile = file.raw |
|
|
|
|
|
this.uploadFileName = file.name |
|
|
|
|
|
}, |
|
|
|
|
|
// 预览上传数据 |
|
|
|
|
|
previewUpload() { |
|
|
|
|
|
if (!this.uploadFile) { |
|
|
|
|
|
this.$message.warning('请先选择文件') |
|
|
|
|
|
|
|
|
this.resetImportDialog() |
|
|
|
|
|
this.importDialogVisible = true |
|
|
|
|
|
}, |
|
|
|
|
|
/** |
|
|
|
|
|
* 导入弹窗关闭后的统一清理。 |
|
|
|
|
|
* 边界处理:导入进行中禁止被重置,避免中途清空文件状态导致重复提交。 |
|
|
|
|
|
*/ |
|
|
|
|
|
resetImportDialog () { |
|
|
|
|
|
if (this.importSubmitting) { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
if (!this.searchData.customer) { |
|
|
|
|
|
this.$message.warning('请先选择 Customer') |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
if (this.$refs.importUpload && this.$refs.importUpload.clearFiles) { |
|
|
|
|
|
this.$refs.importUpload.clearFiles() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const formData = new FormData() |
|
|
|
|
|
formData.append('file', this.uploadFile) |
|
|
|
|
|
formData.append('site', this.searchData.site || this.$store.state.user.site) |
|
|
|
|
|
formData.append('customer', this.searchData.customer) |
|
|
|
|
|
|
|
|
|
|
|
this.$http({ |
|
|
|
|
|
url: this.$http.adornUrl('/pODetail/previewUpload'), |
|
|
|
|
|
method: 'post', |
|
|
|
|
|
data: formData |
|
|
|
|
|
}).then(({ data }) => { |
|
|
|
|
|
if (data && data.code === 0) { |
|
|
|
|
|
const preview = data.data || {} |
|
|
|
|
|
this.uploadPreviewList = preview.rows || [] |
|
|
|
|
|
this.uploadSkippedColumns = preview.skippedColumns || [] |
|
|
|
|
|
this.uploadColumnMapping = preview.columnMapping || {} |
|
|
|
|
|
this.refreshUploadPreviewColumns() |
|
|
|
|
|
this.$message.success('文件解析成功,请确认数据后点击保存') |
|
|
|
|
|
} else { |
|
|
|
|
|
this.$message.error(data.msg || '文件解析失败') |
|
|
|
|
|
} |
|
|
|
|
|
}).catch((error) => { |
|
|
|
|
|
console.error('文件解析失败:', error) |
|
|
|
|
|
const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误' |
|
|
|
|
|
this.$message.error('文件解析失败: ' + errorMsg) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
this.importFileName = '' |
|
|
|
|
|
this.importFile = null |
|
|
|
|
|
this.importLastResult = null |
|
|
}, |
|
|
}, |
|
|
refreshUploadPreviewColumns () { |
|
|
|
|
|
this.uploadPreviewColumns = buildImportPreviewColumns( |
|
|
|
|
|
this.uploadPreviewList, |
|
|
|
|
|
this.fieldSchemaFields, |
|
|
|
|
|
this.uploadColumnMapping |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 记录用户选择的导入文件。 |
|
|
|
|
|
* 输入:el-upload change 事件文件对象。 |
|
|
|
|
|
* 输出:仅保留当前一次选择,后续由“开始导入”统一提交。 |
|
|
|
|
|
*/ |
|
|
|
|
|
handleImportFileChange (file) { |
|
|
|
|
|
this.importFile = file && file.raw ? file.raw : null |
|
|
|
|
|
this.importFileName = file && file.name ? file.name : '' |
|
|
|
|
|
this.importLastResult = null |
|
|
}, |
|
|
}, |
|
|
exportExcel () { |
|
|
exportExcel () { |
|
|
if (this.editMode) { |
|
|
if (this.editMode) { |
|
|
@ -3499,55 +3474,15 @@ export default { |
|
|
this.$message.error('导出失败: ' + error.message) |
|
|
this.$message.error('导出失败: ' + error.message) |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
// 确认上传 |
|
|
|
|
|
confirmUpload() { |
|
|
|
|
|
if (this.uploadPreviewList.length === 0) { |
|
|
|
|
|
this.$message.warning('没有可保存的数据') |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 下载导入模板。 |
|
|
|
|
|
* 业务规则:模板由后端按 site + customer 当前可见字段动态生成,第一行即校验基准栏位。 |
|
|
|
|
|
*/ |
|
|
|
|
|
downloadImportTemplate () { |
|
|
|
|
|
if (!this.searchData.customer) { |
|
|
|
|
|
this.$message.warning('请先选择 Customer') |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.$confirm(`确认保存 ${this.uploadPreviewList.length} 条数据吗?`, '提示', { |
|
|
|
|
|
confirmButtonText: '确定', |
|
|
|
|
|
cancelButtonText: '取消', |
|
|
|
|
|
type: 'warning' |
|
|
|
|
|
}).then(() => { |
|
|
|
|
|
const formData = new FormData() |
|
|
|
|
|
formData.append('file', this.uploadFile) |
|
|
|
|
|
formData.append('site', this.searchData.site || this.$store.state.user.site) |
|
|
|
|
|
formData.append('customer', this.searchData.customer) |
|
|
|
|
|
|
|
|
|
|
|
this.$http({ |
|
|
|
|
|
url: this.$http.adornUrl('/pODetail/batchSave'), |
|
|
|
|
|
method: 'post', |
|
|
|
|
|
data: formData |
|
|
|
|
|
}).then(({ data }) => { |
|
|
|
|
|
if (data && data.code === 0) { |
|
|
|
|
|
const result = data.data || {} |
|
|
|
|
|
const successCount = result.successCount || 0 |
|
|
|
|
|
const failCount = result.failCount || 0 |
|
|
|
|
|
let msg = `导入完成:成功 ${successCount} 条,失败 ${failCount} 条` |
|
|
|
|
|
if (result.rowErrors && result.rowErrors.length) { |
|
|
|
|
|
msg += ';' + result.rowErrors.slice(0, 3).map(e => `第${e.rowNo}行 ${e.message}`).join(';') |
|
|
|
|
|
} |
|
|
|
|
|
if (failCount > 0) { |
|
|
|
|
|
this.$message.warning(msg) |
|
|
|
|
|
} else { |
|
|
|
|
|
this.$message.success(msg) |
|
|
|
|
|
} |
|
|
|
|
|
this.uploadDialogVisible = false |
|
|
|
|
|
this.getDataList() |
|
|
|
|
|
} else { |
|
|
|
|
|
this.$message.error(data.msg || '数据保存失败') |
|
|
|
|
|
} |
|
|
|
|
|
}).catch((error) => { |
|
|
|
|
|
console.error('数据保存失败:', error) |
|
|
|
|
|
const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误' |
|
|
|
|
|
this.$message.error('数据保存失败: ' + errorMsg) |
|
|
|
|
|
}) |
|
|
|
|
|
}).catch(() => {}) |
|
|
|
|
|
}, |
|
|
|
|
|
// 下载模板 |
|
|
|
|
|
downloadTemplate() { |
|
|
|
|
|
const loading = this.$loading({ |
|
|
const loading = this.$loading({ |
|
|
lock: true, |
|
|
lock: true, |
|
|
text: '正在下载模板...', |
|
|
text: '正在下载模板...', |
|
|
@ -3558,6 +3493,10 @@ export default { |
|
|
this.$http({ |
|
|
this.$http({ |
|
|
url: this.$http.adornUrl('/pODetail/downloadTemplate'), |
|
|
url: this.$http.adornUrl('/pODetail/downloadTemplate'), |
|
|
method: 'get', |
|
|
method: 'get', |
|
|
|
|
|
params: { |
|
|
|
|
|
site: this.searchData.site || this.$store.state.user.site, |
|
|
|
|
|
customer: this.searchData.customer |
|
|
|
|
|
}, |
|
|
responseType: 'blob' |
|
|
responseType: 'blob' |
|
|
}).then(response => { |
|
|
}).then(response => { |
|
|
loading.close() |
|
|
loading.close() |
|
|
@ -3570,7 +3509,7 @@ export default { |
|
|
const url = window.URL.createObjectURL(blob) |
|
|
const url = window.URL.createObjectURL(blob) |
|
|
const link = document.createElement('a') |
|
|
const link = document.createElement('a') |
|
|
link.href = url |
|
|
link.href = url |
|
|
link.download = 'PO订单导入模板.xlsx' |
|
|
|
|
|
|
|
|
link.download = `PO订单导入模板_${this.searchData.customer}.xlsx` |
|
|
|
|
|
|
|
|
document.body.appendChild(link) |
|
|
document.body.appendChild(link) |
|
|
link.click() |
|
|
link.click() |
|
|
@ -3585,6 +3524,65 @@ export default { |
|
|
this.$message.error('模板下载失败,请联系管理员') |
|
|
this.$message.error('模板下载失败,请联系管理员') |
|
|
}) |
|
|
}) |
|
|
}, |
|
|
}, |
|
|
|
|
|
/** |
|
|
|
|
|
* 执行导入(后端逐行创建 header + detail)。 |
|
|
|
|
|
* 关键约束: |
|
|
|
|
|
* 1) 导入期间统一禁用弹窗按钮,避免重复发起; |
|
|
|
|
|
* 2) 请求超时时间单独提升为 10 分钟,兼容大批量(数千行)导入; |
|
|
|
|
|
* 3) 返回成功/失败条数与行级错误,便于用户按行修复。 |
|
|
|
|
|
*/ |
|
|
|
|
|
submitImportCreatePoOrder () { |
|
|
|
|
|
if (!this.importFile) { |
|
|
|
|
|
this.$message.warning('请先选择导入文件') |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
if (!this.searchData.customer) { |
|
|
|
|
|
this.$message.warning('请先选择 Customer') |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
if (this.importSubmitting) { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
const formData = new FormData() |
|
|
|
|
|
formData.append('file', this.importFile) |
|
|
|
|
|
formData.append('site', this.searchData.site || this.$store.state.user.site) |
|
|
|
|
|
formData.append('customer', this.searchData.customer) |
|
|
|
|
|
|
|
|
|
|
|
this.importSubmitting = true |
|
|
|
|
|
this.importLastResult = null |
|
|
|
|
|
this.$http({ |
|
|
|
|
|
url: this.$http.adornUrl('/pODetail/importCreatePoOrder'), |
|
|
|
|
|
method: 'post', |
|
|
|
|
|
data: formData, |
|
|
|
|
|
timeout: 10 * 60 * 1000 |
|
|
|
|
|
}).then(({ data }) => { |
|
|
|
|
|
if (!(data && data.code === 0)) { |
|
|
|
|
|
this.$message.error((data && data.msg) || '导入失败') |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
const result = data.data || {} |
|
|
|
|
|
this.importLastResult = result |
|
|
|
|
|
const successCount = Number(result.successCount || 0) |
|
|
|
|
|
const failCount = Number(result.failCount || 0) |
|
|
|
|
|
let msg = `导入完成:成功 ${successCount} 条,失败 ${failCount} 条` |
|
|
|
|
|
if (result.rowErrors && result.rowErrors.length) { |
|
|
|
|
|
msg += ';' + result.rowErrors.slice(0, 3).map(item => `第${item.rowNo}行 ${item.message}`).join(';') |
|
|
|
|
|
} |
|
|
|
|
|
if (failCount > 0) { |
|
|
|
|
|
this.$message.warning(msg) |
|
|
|
|
|
} else { |
|
|
|
|
|
this.$message.success(msg) |
|
|
|
|
|
} |
|
|
|
|
|
if (successCount > 0) { |
|
|
|
|
|
this.getDataList() |
|
|
|
|
|
} |
|
|
|
|
|
}).catch((error) => { |
|
|
|
|
|
const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误' |
|
|
|
|
|
this.$message.error('导入失败: ' + errorMsg) |
|
|
|
|
|
}).finally(() => { |
|
|
|
|
|
this.importSubmitting = false |
|
|
|
|
|
}) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
// 打开供应商选择弹窗 |
|
|
// 打开供应商选择弹窗 |
|
|
openSupplierSelect(row) { |
|
|
openSupplierSelect(row) { |
|
|
|