Browse Source

物料高级复制

master
常熟吴彦祖 1 week ago
parent
commit
2edff8a461
  1. 2
      src/api/part/partInfo.js
  2. 279
      src/views/modules/srmPart/PartAdvancedCopyDialog.vue
  3. 21
      src/views/modules/srmPart/partInfoList.vue

2
src/api/part/partInfo.js

@ -5,6 +5,8 @@ export const searchPartList = data => createAPI('/part/searchPartList', 'POST',
export const createPart = data => createAPI('/part/save', 'POST', data) export const createPart = data => createAPI('/part/save', 'POST', data)
export const updatePart = data => createAPI('/part/save', 'POST', data) export const updatePart = data => createAPI('/part/save', 'POST', data)
export const copyPartByBase = data => createAPI('/part/copyByBasePartNo', 'POST', data) export const copyPartByBase = data => createAPI('/part/copyByBasePartNo', 'POST', data)
// 高级复制:固定复制主档/属性,可选供应商价格与检验模板,支持目标覆盖确认 - rqrq
export const advancedCopyPart = data => createAPI('/part/advancedCopy', 'POST', data)
export const startPartFormalApprove = data => createAPI('/part/startFormalApprove', 'POST', data) export const startPartFormalApprove = data => createAPI('/part/startFormalApprove', 'POST', data)
export const changePartDefaultSupplier = data => createAPI('/part/changeDefaultSupplier', 'POST', data) export const changePartDefaultSupplier = data => createAPI('/part/changeDefaultSupplier', 'POST', data)
// 查询基准料号下已维护过的SKU(去重),用于新增时SKU联想 // 查询基准料号下已维护过的SKU(去重),用于新增时SKU联想

279
src/views/modules/srmPart/PartAdvancedCopyDialog.vue

@ -0,0 +1,279 @@
<template>
<!-- 上下源/目标分区,操作按钮统一放底部 footer,沿用页面现有 Element 风格 - rqrq -->
<el-dialog
title="高级复制"
:visible.sync="visible"
width="840px"
top="8vh"
:close-on-click-modal="false"
:close-on-press-escape="!saveLoading"
:show-close="!saveLoading"
class="advanced-copy-dialog"
v-drag>
<div class="advanced-copy-layout">
<div class="advanced-copy-fields">
<fieldset class="copy-section">
<legend>源物料信息</legend>
<el-form :model="source" label-position="top" disabled>
<el-row :gutter="16">
<el-col :span="6">
<el-form-item label="工厂编号"><el-input v-model="source.site" /></el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="基准料号"><el-input v-model="source.basePartNo" /></el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="物料后缀"><el-input v-model="source.partSuffix" /></el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="14">
<el-form-item label="物料名称"><el-input v-model="source.partDesc" /></el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="规格型号"><el-input v-model="source.partSpec" /></el-form-item>
</el-col>
</el-row>
</el-form>
</fieldset>
<fieldset class="copy-section">
<legend>目标物料信息</legend>
<el-form ref="targetForm" :model="form" :rules="rules" :disabled="saveLoading" label-position="top">
<el-row :gutter="16">
<el-col :span="6">
<el-form-item label="工厂编号"><el-input v-model="form.site" disabled /></el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="基准料号" prop="basePartNo">
<el-input v-model="form.basePartNo" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="物料后缀" prop="partSuffix">
<el-input v-model="form.partSuffix" maxlength="29" placeholder="可不填" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="14">
<el-form-item label="物料名称" prop="partDesc">
<el-input v-model="form.partDesc" maxlength="255" />
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="规格型号" prop="partSpec">
<el-input v-model="form.partSpec" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="target-part-no">目标物料编码:{{ targetPartNo || '—' }}</div>
</fieldset>
<fieldset class="copy-section copy-options">
<legend>复制项目</legend>
<!-- 固定项不提交开关,后端始终复制主档和属性;两个可选项默认不勾选 - rqrq -->
<el-checkbox :value="true" disabled>物料信息+属性</el-checkbox>
<el-checkbox v-model="form.copySupplier" :disabled="saveLoading">供应商可供物料+价格表</el-checkbox>
<el-checkbox v-model="form.copyInspectionTemplate" :disabled="saveLoading">检验模板</el-checkbox>
</fieldset>
</div>
</div>
<!-- 与其他弹框一致,底部横向排列保存和关闭;保留提交期间的禁用状态 - rqrq -->
<div slot="footer" class="dialog-footer">
<el-button type="primary" :loading="saveLoading" :disabled="saveLoading" @click="save">保存</el-button>
<el-button :disabled="saveLoading" @click="visible = false">关闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { advancedCopyPart } from '@/api/part/partInfo'
export default {
name: 'PartAdvancedCopyDialog',
data () {
return {
visible: false,
saveLoading: false,
source: {},
form: {},
rules: {
basePartNo: [{ required: true, whitespace: true, message: '请输入基准料号', trigger: 'blur' }],
partDesc: [{ required: true, whitespace: true, message: '请输入物料名称', trigger: 'blur' }],
partSpec: [{ required: true, whitespace: true, message: '请输入规格型号', trigger: 'blur' }]
}
}
},
computed: {
// 预览与后端一致:基准料号必填,后缀空白时不追加连字符 - rqrq
targetPartNo () {
const base = (this.form.basePartNo || '').trim().toUpperCase()
const suffix = (this.form.partSuffix || '').trim().toUpperCase()
return base ? base + (suffix ? `-${suffix}` : '') : ''
}
},
methods: {
/** 以当前选中行作为唯一复制源;打开时重新初始化,避免沿用上次勾选或目标输入 - rqrq */
open (row) {
if (!row || !row.partNo) {
this.$message.warning('请先选择源物料')
return
}
const site = (this.$store.state.user.site || '').trim().toUpperCase()
if (!site || (row.site || '').trim().toUpperCase() !== site) {
this.$message.warning('请选择当前工厂的物料')
return
}
const partNo = row.partNo.trim().toUpperCase()
// 主档未维护基准料号时,上下区域均带出完整物料编码,不按 '-' 拆分 - rqrq
const storedBase = (row.basePartNo || '').trim().toUpperCase()
const base = storedBase || partNo
// 仅主档已有基准料号且编码匹配时提取后缀;空基准料号的物料后缀始终为空 - rqrq
const suffix = storedBase && partNo.indexOf(`${storedBase}-`) === 0 ? partNo.substring(storedBase.length + 1) : ''
this.source = {
site,
partNo,
basePartNo: base,
partSuffix: suffix,
partDesc: row.partDesc || '',
partSpec: row.partSpec || ''
}
this.form = {
site,
sourcePartNo: partNo,
basePartNo: base,
partSuffix: suffix,
partDesc: row.partDesc || '',
partSpec: row.partSpec || '',
copySupplier: false,
copyInspectionTemplate: false
}
this.visible = true
this.$nextTick(() => {
if (this.$refs.targetForm) this.$refs.targetForm.clearValidate()
})
},
/** 保存前校验编码;后台先检查源配置,目标存在时才返回覆盖确认,取消不会写入 - rqrq */
save () {
if (this.saveLoading) return
// 工厂切换后必须重新选择源物料,避免弹框显示的工厂与实际提交工厂不一致 - rqrq
if ((this.$store.state.user.site || '').trim().toUpperCase() !== this.source.site) {
this.$message.warning('当前工厂已变化,请关闭弹框后重新选择源物料')
return
}
this.$refs.targetForm.validate(valid => {
if (!valid) return
const suffix = (this.form.partSuffix || '').trim().toUpperCase()
if (suffix.includes('-')) {
this.$message.warning('物料后缀不能包含 "-"')
return
}
if (this.targetPartNo.length > 30) {
this.$message.warning('目标物料编码不能超过30个字符')
return
}
if (this.targetPartNo === this.source.partNo) {
this.$message.warning('目标物料编码不能与源物料相同')
return
}
// 固定本次提交快照,确认覆盖期间锁住表单,避免确认的目标与实际提交不一致。
const request = {
...this.form,
site: this.$store.state.user.site,
sourcePartNo: this.source.partNo,
basePartNo: this.form.basePartNo.trim().toUpperCase(),
partSuffix: suffix,
partDesc: this.form.partDesc.trim(),
partSpec: this.form.partSpec.trim(),
overwriteExisting: false
}
this.submitCopy(request)
})
},
/** 执行复制及一次覆盖确认;失败保留弹框输入,成功关闭并通知物料清单刷新 - rqrq */
async submitCopy (request) {
this.saveLoading = true
try {
let { data } = await advancedCopyPart(request)
if (!data || data.code !== 0) {
this.$alert((data && data.msg) || '复制失败', '错误', { confirmButtonText: '确定' })
return
}
if (data.row && data.row.overwriteRequired) {
try {
await this.$confirm(`目标物料【${data.row.partNo}】已经存在,是否覆盖?`, '提示', {
confirmButtonText: '覆盖',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
} catch (cancel) {
// 取消覆盖时不再次请求,后台第一次请求也未写入任何数据。
return
}
const response = await advancedCopyPart({ ...request, overwriteExisting: true })
data = response.data
if (!data || data.code !== 0) {
this.$alert((data && data.msg) || '复制失败', '错误', { confirmButtonText: '确定' })
return
}
}
this.$message.success(`复制成功,物料编码:${data.row.partNo}`)
this.visible = false
this.$emit('copied')
} catch (error) {
this.$message.error('复制失败:' + (error.message || '请稍后重试'))
} finally {
this.saveLoading = false
}
}
}
}
</script>
<style scoped>
/* 保留原有控件外观,内容分区占满宽度,按钮使用标准 footer 布局 - rqrq */
.advanced-copy-fields {
min-width: 0;
}
.copy-section {
margin: 0 0 16px;
padding: 8px 16px 16px;
border: 1px solid #DCDFE6;
border-radius: 4px;
}
.copy-section legend {
padding: 0 6px;
color: #303133;
}
.copy-section /deep/ .el-form-item {
margin-bottom: 16px;
}
.copy-section /deep/ .el-input {
width: 100%;
}
.target-part-no {
color: #606266;
overflow-wrap: break-word;
}
.copy-options {
display: flex;
flex-wrap: wrap;
gap: 12px 20px;
margin-bottom: 0;
}
.copy-options .el-checkbox {
margin-left: 0;
margin-right: 0;
}
.advanced-copy-dialog /deep/ .el-dialog {
max-width: calc(100vw - 32px);
}
.advanced-copy-dialog /deep/ .el-dialog__body {
/* 为弹框标题和底部按钮预留高度,长内容只在 body 内滚动 - rqrq */
max-height: calc(84vh - 130px);
overflow-y: auto;
}
</style>

21
src/views/modules/srmPart/partInfoList.vue

@ -52,6 +52,8 @@
<el-button @click="openCopyDialog" type="primary" style="margin-left: 2px">复制</el-button> <el-button @click="openCopyDialog" type="primary" style="margin-left: 2px">复制</el-button>
<!-- 主表导出:风格与供应商列表一致,复用当前查询条件全量导出 - rqrq --> <!-- 主表导出:风格与供应商列表一致,复用当前查询条件全量导出 - rqrq -->
<el-button @click="exportExcel()" type="primary" style="margin-left: 2px">{{'导出'}}</el-button> <el-button @click="exportExcel()" type="primary" style="margin-left: 2px">{{'导出'}}</el-button>
<!-- 高级复制固定使用当前选中物料,保存后刷新主表及当前明细 - rqrq -->
<el-button @click="$refs.advancedCopyDialog.open(currentRow)" type="primary" style="margin-left: 2px">高级复制</el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -131,6 +133,7 @@
</el-tabs> </el-tabs>
<Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist> <Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
<PartAdvancedCopyDialog ref="advancedCopyDialog" @copied="getMainData" />
<!-- 产品编辑对话框 --> <!-- 产品编辑对话框 -->
<!-- 默认弹窗可拖动,特殊需求除外 - rqrq --> <!-- 默认弹窗可拖动,特殊需求除外 - rqrq -->
@ -150,7 +153,8 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="物料后缀" prop="partSuffix" :required="isAdd">
<!-- 后缀可不填,空值时产品编码直接使用基准料号 - rqrq -->
<el-form-item label="物料后缀" prop="partSuffix">
<el-input v-model="currentpart.partSuffix" :disabled="!isAdd" @input="syncGeneratedPartNo"></el-input> <el-input v-model="currentpart.partSuffix" :disabled="!isAdd" @input="syncGeneratedPartNo"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -405,6 +409,7 @@ import specParams from './com_part_SpecParams'
import suppliers from './com_part_Suppliers' import suppliers from './com_part_Suppliers'
import approval from './com_part_Approval' import approval from './com_part_Approval'
import attachments from './com_part_Attachments' import attachments from './com_part_Attachments'
import PartAdvancedCopyDialog from './PartAdvancedCopyDialog'
import { import {
approveNodeInstance, approveNodeInstance,
@ -953,15 +958,13 @@ export default {
copyRules: { copyRules: {
basePartNo: [ basePartNo: [
{ required: true, message: '请输入基准料号', trigger: 'blur' } { required: true, message: '请输入基准料号', trigger: 'blur' }
],
partSuffix: [
{ required: true, message: '请输入物料后缀', trigger: 'blur' }
] ]
} }
} }
}, },
/* 组件 */ /* 组件 */
components: { components: {
PartAdvancedCopyDialog,
Chooselist, Chooselist,
basicInfo, basicInfo,
specParams, specParams,
@ -1085,13 +1088,14 @@ export default {
} }
return row[columnProp] return row[columnProp]
}, },
// 新增和原有复制共用规则:后缀为空时直接使用基准料号,不生成尾部 '-' - rqrq
generatePartNo (basePartNo, partSuffix) { generatePartNo (basePartNo, partSuffix) {
const base = (basePartNo || '').trim().toUpperCase() const base = (basePartNo || '').trim().toUpperCase()
const suffix = (partSuffix || '').trim().toUpperCase() const suffix = (partSuffix || '').trim().toUpperCase()
if (!base || !suffix) {
if (!base) {
return '' return ''
} }
return `${base}-${suffix}`
return suffix ? `${base}-${suffix}` : base
}, },
syncGeneratedPartNo () { syncGeneratedPartNo () {
if (!this.isAdd) return if (!this.isAdd) return
@ -1645,8 +1649,9 @@ export default {
if (this.isAdd) { if (this.isAdd) {
const basePartNo = (this.currentpart.basePartNo || '').trim().toUpperCase() const basePartNo = (this.currentpart.basePartNo || '').trim().toUpperCase()
const partSuffix = (this.currentpart.partSuffix || '').trim().toUpperCase() const partSuffix = (this.currentpart.partSuffix || '').trim().toUpperCase()
if (!basePartNo || !partSuffix) {
this.$message.warning('请填写基准料号和物料后缀')
// 基准料号必填,后缀允许留空;与高级复制的编码规则一致 - rqrq
if (!basePartNo) {
this.$message.warning('请填写基准料号')
return return
} }
if (partSuffix.includes('-')) { if (partSuffix.includes('-')) {

Loading…
Cancel
Save