1.0.35 采购修改页面关联物料维护

dev
yinq 1 month ago
parent ba66265f38
commit 9ab3276697

@ -1,6 +1,7 @@
import request from '@/utils/request'; import request from '@/utils/request';
import { AxiosPromise } from 'axios'; import { AxiosPromise } from 'axios';
import { ProjectPurchaseVO, ProjectPurchaseForm, ProjectPurchaseQuery } from '@/api/oa/erp/projectPurchase/types'; import { ProjectPurchaseVO, ProjectPurchaseForm, ProjectPurchaseQuery } from '@/api/oa/erp/projectPurchase/types';
import { ProjectPurchaseMaterialVO } from '@/api/oa/erp/projectPurchaseMaterial/types';
/** /**
* *
@ -86,3 +87,15 @@ export function getErpProjectPurchaseList (query) {
params: query params: query
}); });
}; };
/**
* ID
* @param projectId
* @param spareFlag
*/
export const getProjectMaterialsByProjectId = (projectId: string | number, spareFlag: string): AxiosPromise<ProjectPurchaseMaterialVO[]> => {
return request({
url: `/oa/erp/projectPurchase/getContractMaterialsByProjectId/${projectId}/${spareFlag}`,
method: 'get'
});
};

@ -49,6 +49,22 @@ export interface ProjectPurchaseMaterialVO {
*/ */
materialName: string; materialName: string;
/**
*
*/
relationMaterialCode?: string;
/**
*
*/
relationMaterialName?: string;
relationMaterialName?: string;
/**
*
*/
purchaseMaterialName?: string;
/** /**
* *
*/ */

@ -532,7 +532,9 @@ const initFormData: ContractInfoFormEx = {
const data = reactive<{ form: ContractInfoFormEx; rules: any }>({ const data = reactive<{ form: ContractInfoFormEx; rules: any }>({
form: { ...initFormData }, form: { ...initFormData },
rules: { rules: {
contractId: [{ required: true, message: '合同ID不能为空', trigger: 'blur' }] contractFlag: [{ required: true, message: '有无合同不能为空', trigger: 'blur' }],
contractName: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
paymentMethod: [{ required: true, message: '付款方式不能为空', trigger: 'blur' }],
} }
}); });

@ -100,36 +100,41 @@
</el-form> </el-form>
</el-card> </el-card>
<el-card shadow="never" class="mb-4"> <el-card shadow="never" >
<template #header> <template #header>
<div class="flex items-center"> <div class="table-header standard">
<el-button type="primary" icon="Plus" @click="handleAddMaterial(true)" :disabled="disableMaterialActions" v-if="!isReadOnly" class="mr-2"> <span class="title-dot standard-dot"></span>
添加标准物料 <span class="title-text">标准物料</span>
</el-button>
</div> </div>
</template> </template>
<el-table v-loading="materialLoading" :data="standardMaterialList" border :row-class-name="getRowClassName"> <el-table v-loading="materialLoading" :data="standardMaterialList" border :row-class-name="getRowClassName">
<el-table-column label="采购物料名称" prop="materialName" min-width="160" show-overflow-tooltip /> <el-table-column label="序号" width="70" align="center">
<el-table-column label="采购物料编码" prop="materialCode" min-width="140" show-overflow-tooltip /> <template #default="scope">
<el-table-column label="规格参数" prop="specificationDescription" min-width="160" show-overflow-tooltip /> {{ scope.$index + 1 }}
<el-table-column label="计划标识" prop="purchasePlanFlag" width="100"> </template>
</el-table-column>
<el-table-column label="关联物料编号" prop="relationMaterialCode" min-width="140" show-overflow-tooltip align="center" />
<el-table-column label="关联物料名称" prop="relationMaterialName" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="采购物料名称" prop="materialName" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="规格参数" prop="specificationDescription" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="计划标识" prop="purchasePlanFlag" width="100" align="center">
<template #default="scope"> <template #default="scope">
<dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" /> <dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" /> <el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" align="center" />
<el-table-column label="本次采购数量" prop="purchaseAmount" width="140" /> <el-table-column label="本次采购数量" prop="purchaseAmount" width="140" align="center" />
<el-table-column label="需求到货时间" prop="arrivalTime" width="180"> <el-table-column label="需求到货时间" prop="arrivalTime" width="180" align="center">
<template #default="scope"> <template #default="scope">
<span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d} {h}:{i}') || scope.row.arrivalTime }}</span> <span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d}') || scope.row.arrivalTime }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip /> <el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="操作" fixed="right" width="160" align="center" v-if="!isReadOnly"> <el-table-column label="操作" fixed="right" width="150" align="center" v-if="!isReadOnly">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEditMaterial(scope.row)"></el-button> <el-button link type="warning" @click="splitMaterial(scope.row)"></el-button>
<el-button link type="danger" icon="Delete" @click="handleDeleteMaterial(scope.row)"></el-button> <el-button link type="primary" @click="handleEditMaterial(scope.row)"></el-button>
<el-button link type="danger" @click="handleDeleteMaterial(scope.row)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -137,34 +142,42 @@
<el-card shadow="never" > <el-card shadow="never" >
<template #header> <template #header>
<div class="flex items-center"> <div class="table-header non-standard">
<el-button type="primary" icon="Plus" @click="handleAddMaterial(false)" :disabled="disableMaterialActions" v-if="!isReadOnly" class="mr-2"> <span class="title-dot non-standard-dot"></span>
添加非标准物料 <span class="title-text">非标准物料</span>
</el-button>
</div> </div>
</template> </template>
<el-table v-loading="materialLoading" :data="nonStandardMaterialList" border :row-class-name="getRowClassName"> <el-table v-loading="materialLoading" :data="nonStandardMaterialList" border :row-class-name="getRowClassName">
<el-table-column label="采购物料名称" prop="materialName" min-width="160" show-overflow-tooltip /> <el-table-column label="序号" width="70" align="center">
<el-table-column label="采购物料编码" prop="materialCode" min-width="140" show-overflow-tooltip /> <template #default="scope">
<el-table-column label="规格参数" prop="specificationDescription" min-width="160" show-overflow-tooltip /> {{ scope.$index + 1 }}
<el-table-column label="计划标识" prop="purchasePlanFlag" width="100"> </template>
</el-table-column>
<el-table-column label="关联物料名称" prop="relationMaterialName" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="采购物料名称" min-width="160" show-overflow-tooltip align="center">
<template #default="scope">
<span>{{ scope.row.purchaseMaterialName || scope.row.materialName }}</span>
</template>
</el-table-column>
<el-table-column label="规格参数" prop="specificationDescription" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="计划标识" prop="purchasePlanFlag" width="100" align="center">
<template #default="scope"> <template #default="scope">
<dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" /> <dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" /> <el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" align="center" />
<el-table-column label="本次采购数量" prop="purchaseAmount" width="140" /> <el-table-column label="本次采购数量" prop="purchaseAmount" width="140" align="center" />
<el-table-column label="需求到货时间" prop="arrivalTime" width="180"> <el-table-column label="需求到货时间" prop="arrivalTime" width="180" align="center">
<template #default="scope"> <template #default="scope">
<span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d} {h}:{i}') || scope.row.arrivalTime }}</span> <span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d}') || scope.row.arrivalTime }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip /> <el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="操作" fixed="right" width="160" align="center" v-if="!isReadOnly"> <el-table-column label="操作" fixed="right" width="150" align="center" v-if="!isReadOnly">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEditMaterial(scope.row)"></el-button> <el-button link type="warning" @click="splitMaterial(scope.row)"></el-button>
<el-button link type="danger" icon="Delete" @click="handleDeleteMaterial(scope.row)"></el-button> <el-button link type="primary" @click="handleEditMaterial(scope.row)"></el-button>
<el-button link type="danger" @click="handleDeleteMaterial(scope.row)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -180,21 +193,29 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">--> <el-col v-if="materialForm.materialFlag === '1'" :span="12">
<!-- <el-form-item label="标准物料" prop="materialFlag">--> <el-form-item label="关联物料编码">
<!-- <el-radio-group v-model="materialForm.materialFlag">--> <el-input v-model="materialForm.relationMaterialCode" disabled />
<!-- <el-radio v-for="dict in material_flag" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>--> </el-form-item>
<!-- </el-radio-group>--> </el-col>
<!-- </el-form-item>--> <el-col v-if="materialForm.materialFlag === '1'" :span="12">
<!-- </el-col>--> <el-form-item label="关联物料名称">
<el-col :span="12"> <el-input v-model="materialForm.relationMaterialName" disabled />
<el-form-item label="采购物料名称" prop="materialName"> </el-form-item>
<el-input v-model="materialForm.materialName" placeholder="请输入采购物料名称" /> </el-col>
<el-col v-if="materialForm.materialFlag === '2'" :span="12">
<el-form-item label="关联物料名称">
<el-input v-model="materialForm.relationMaterialName" disabled />
</el-form-item>
</el-col>
<el-col v-if="materialForm.materialFlag === '1'" :span="12">
<el-form-item label="采购物料编码" prop="materialCode">
<el-input v-model="materialForm.materialCode" placeholder="请输入采购物料编码" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="采购物料编码" prop="materialCode"> <el-form-item label="采购物料名称" prop="materialName">
<el-input v-model="materialForm.materialCode" placeholder="请输入采购物料编码" /> <el-input v-model="materialForm.materialName" placeholder="请输入采购物料名称" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">--> <!-- <el-col :span="12">-->
@ -207,11 +228,6 @@
<!-- <el-input v-model="materialForm.relationMaterialId" placeholder="请输入销售物料ID" />--> <!-- <el-input v-model="materialForm.relationMaterialId" placeholder="请输入销售物料ID" />-->
<!-- </el-form-item>--> <!-- </el-form-item>-->
<!-- </el-col>--> <!-- </el-col>-->
<el-col :span="24">
<el-form-item label="详细参数" prop="specificationDescription">
<el-input v-model="materialForm.specificationDescription" placeholder="请输入详细参数" />
</el-form-item>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="单位" prop="unitId"> <el-form-item label="单位" prop="unitId">
<el-select v-model="materialForm.unitId" placeholder="请选择单位" clearable filterable> <el-select v-model="materialForm.unitId" placeholder="请选择单位" clearable filterable>
@ -221,27 +237,45 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="需采购数量" prop="needPurchaseAmount"> <el-form-item label="需采购数量" prop="needPurchaseAmount">
<el-input-number v-model="materialForm.needPurchaseAmount" :min="0" :controls="false" class="w-full" placeholder="请输入数量" /> <el-input-number
v-model="materialForm.needPurchaseAmount"
:min="0"
class="w-full"
placeholder="请输入数量"
:precision="2"
:disabled="true"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="本次采购数量" prop="purchaseAmount"> <el-form-item label="本次采购数量" prop="purchaseAmount">
<el-input-number v-model="materialForm.purchaseAmount" :min="0" :controls="false" class="w-full" placeholder="请输入数量" /> <el-input-number
v-model="materialForm.purchaseAmount"
:min="0"
class="w-full"
placeholder="请输入数量"
:precision="2"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="需求到货时间" prop="arrivalTime"> <el-form-item label="需求到货时间" prop="arrivalTime">
<el-date-picker <el-date-picker
v-model="materialForm.arrivalTime" v-model="materialForm.arrivalTime"
type="datetime" type="date"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择需求到货时间" placeholder="请选择需求到货时间"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24">
<el-form-item label="详细参数" prop="specificationDescription">
<el-input v-model="materialForm.specificationDescription" type="textarea" placeholder="请输入详细参数" />
</el-form-item>
</el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="materialForm.remark" type="textarea" :rows="3" placeholder="请输入备注" /> <el-input v-model="materialForm.remark" type="textarea" placeholder="请输入备注" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -265,7 +299,7 @@ import { computed, reactive, ref, watch, getCurrentInstance, toRefs, onMounted,
import type { ComponentInternalInstance } from 'vue'; import type { ComponentInternalInstance } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import type { FormInstance } from 'element-plus'; import type { FormInstance } from 'element-plus';
import { addProjectPurchase, getProjectPurchase, updateProjectPurchase, projectPurchaseSubmitAndFlowStart } from '@/api/oa/erp/projectPurchase'; import { addProjectPurchase, getProjectPurchase, updateProjectPurchase, projectPurchaseSubmitAndFlowStart, getProjectMaterialsByProjectId } from '@/api/oa/erp/projectPurchase';
import type { ProjectPurchaseForm } from '@/api/oa/erp/projectPurchase/types'; import type { ProjectPurchaseForm } from '@/api/oa/erp/projectPurchase/types';
import { getErpProjectPurchaseMaterialList } from '@/api/oa/erp/projectPurchaseMaterial'; import { getErpProjectPurchaseMaterialList } from '@/api/oa/erp/projectPurchaseMaterial';
import type { ProjectPurchaseMaterialVO, ProjectPurchaseMaterialForm } from '@/api/oa/erp/projectPurchaseMaterial/types'; import type { ProjectPurchaseMaterialVO, ProjectPurchaseMaterialForm } from '@/api/oa/erp/projectPurchaseMaterial/types';
@ -346,6 +380,26 @@ const nonStandardMaterialList = computed(() => {
return materialList.value.filter((item) => item.materialFlag === '2'); return materialList.value.filter((item) => item.materialFlag === '2');
}); });
const loadProjectMaterialsFromSource = async (projectId?: string | number, spareFlag?: string) => {
if (!projectId || !spareFlag) {
return;
}
materialLoading.value = true;
try {
const res = await getProjectMaterialsByProjectId(projectId, spareFlag);
let data = res?.data || [];
data = data.map((item, index) => ({
...item,
purchasePlanFlag: '1',
serialNumber: index + 1 //
}));
materialList.value = data;
} finally {
materialLoading.value = false;
}
};
// //
const getRowClassName = ({ row }: { row: ProjectPurchaseMaterialVO }) => { const getRowClassName = ({ row }: { row: ProjectPurchaseMaterialVO }) => {
// '2' '' // '2' ''
@ -493,15 +547,22 @@ const submitForm = async (status: string, mode?: boolean) => {
buttonLoading.value = true; buttonLoading.value = true;
try { try {
const normalizedMaterials: ProjectPurchaseMaterialForm[] = materialList.value.map((item) => {
const payloadItem: ProjectPurchaseMaterialForm = {
...item,
projectPurchaseId: form.value.projectPurchaseId
};
if (typeof payloadItem.purchaseMaterialId === 'string') {
payloadItem.purchaseMaterialId = undefined;
}
return payloadItem;
});
const payload: ProjectPurchaseForm & { const payload: ProjectPurchaseForm & {
projectName?: string; projectName?: string;
purchaseMaterialList?: ProjectPurchaseMaterialForm[]; purchaseMaterialList?: ProjectPurchaseMaterialForm[];
} = { } = {
...form.value, ...form.value,
purchaseMaterialList: materialList.value.map((item) => ({ purchaseMaterialList: normalizedMaterials
...item,
projectPurchaseId: form.value.projectPurchaseId
}))
}; };
if (status !== 'draft') { if (status !== 'draft') {
payload.flowCode = FlowCodeEnum.PROJECT_PURCHASE_CODE; payload.flowCode = FlowCodeEnum.PROJECT_PURCHASE_CODE;
@ -548,29 +609,6 @@ const submitForm = async (status: string, mode?: boolean) => {
} }
}; };
/**
* 打开新增物料弹窗
* @param isStandard 是否标准物料
*/
const handleAddMaterial = (isStandard: boolean) => {
if (isReadOnly.value) {
return;
}
resetMaterialForm();
materialForm.value.purchasePlanFlag = materialForm.value.purchasePlanFlag || (purchase_plan_flag.value?.[0]?.value ?? undefined);
// isStandard materialFlag
if (isStandard) {
materialForm.value.materialFlag = '1'; //
materialDialog.title = '添加标准物料';
} else {
materialForm.value.materialFlag = '2'; //
materialDialog.title = '添加非标准物料';
}
materialForm.value.spareFlag = materialForm.value.spareFlag || (spare_flag.value?.[0]?.value ?? undefined);
materialForm.value.activeFlag = materialForm.value.activeFlag || (active_flag.value?.[0]?.value ?? undefined);
materialDialog.visible = true;
};
/** /**
* 编辑物料 * 编辑物料
*/ */
@ -625,6 +663,31 @@ const handleDeleteMaterial = async (row: ProjectPurchaseMaterialVO) => {
} }
}; };
/**
* 拆分物料为计划外
*/
const splitMaterial = (row: ProjectPurchaseMaterialVO) => {
if (isReadOnly.value) {
return;
}
const newItem: ProjectPurchaseMaterialVO = {
...row,
purchaseMaterialId: `split-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
projectPurchaseId: form.value.projectPurchaseId ?? row.projectPurchaseId,
purchasePlanFlag: '2',
needPurchaseAmount: row.needPurchaseAmount,
purchaseAmount: row.purchaseAmount,
serialNumber: (materialList.value?.length || 0) + 1
} as ProjectPurchaseMaterialVO;
const index = materialList.value.indexOf(row);
if (index >= 0) {
materialList.value.splice(index + 1, 0, newItem);
} else {
materialList.value.push(newItem);
}
proxy?.$modal?.msgSuccess('拆分成功');
};
/** /**
* 打开审批弹窗 * 打开审批弹窗
*/ */
@ -658,9 +721,6 @@ const submitCallback = async () => {
}; };
// //
/**
* 打开项目选择弹窗
*/
const openProjectSelect = () => { const openProjectSelect = () => {
if (isReadOnly.value) { if (isReadOnly.value) {
return; return;
@ -669,9 +729,6 @@ const openProjectSelect = () => {
}; };
// //
/**
* 项目选择回调
*/
const projectInfoSelectCallBack = (data: ProjectInfoVO[]) => { const projectInfoSelectCallBack = (data: ProjectInfoVO[]) => {
if (data && data.length > 0) { if (data && data.length > 0) {
const project = data[0]; const project = data[0];
@ -682,13 +739,11 @@ const projectInfoSelectCallBack = (data: ProjectInfoVO[]) => {
form.value.chargeId = project.chargeId; form.value.chargeId = project.chargeId;
form.value.deputyId = project.deputyId; form.value.deputyId = project.deputyId;
form.value.spareFlag = project.spareFlag; form.value.spareFlag = project.spareFlag;
loadProjectMaterialsFromSource(project.projectId as string | number, project.spareFlag);
} }
}; };
// //
/**
* 初始化下拉数据
*/
const loadSelectOptions = () => { const loadSelectOptions = () => {
getUserList(); getUserList();
getUnitInfoListSelect(); getUnitInfoListSelect();
@ -720,10 +775,37 @@ onMounted(async () => {
<style scoped> <style scoped>
:deep(.plan-out-row) { :deep(.plan-out-row) {
background-color: #fffbe6 !important; background-color: #e9d571 !important;
} }
:deep(.plan-out-row:hover) { :deep(.plan-out-row:hover) {
background-color: #fff8d1 !important; background-color: #d8bb3a !important;
}
.table-header {
display: flex;
align-items: center;
font-weight: 600;
font-size: 16px;
}
.title-dot {
width: 8px;
height: 24px;
border-radius: 4px;
margin-right: 8px;
display: inline-block;
}
.standard-dot {
background-color: #409eff;
}
.non-standard-dot {
background-color: #f56c6c;
}
.title-text {
color: #333;
} }
</style> </style>

Loading…
Cancel
Save