You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

871 lines
33 KiB
Vue

<template>
<div class="p-2" v-loading="pageLoading">
<approvalButton
class="mb-4"
@submitForm="submitForm"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.projectPurchaseId as any"
:status="form.flowStatus as any"
:pageType="pageType"
:mode="false"
/>
<el-card shadow="never" class="mb-4">
<el-form ref="projectPurchaseFormRef" :model="form" :rules="rules" label-width="120px" :disabled="isReadOnly">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="请选择项目" readonly>
<template #suffix>
<el-icon style="cursor: pointer" @click="openProjectSelect" :disabled="isReadOnly">
<Search />
</el-icon>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采购编号" prop="purchaseCode">
<el-input v-model="form.purchaseCode" placeholder="自动生成" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" placeholder="请选择项目后自动填充" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理" prop="managerId">
<el-select v-model="form.managerId" placeholder="请选择项目后自动填充" disabled>
<el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门负责人" prop="chargeId">
<el-select v-model="form.chargeId" placeholder="请选择项目后自动填充" disabled>
<el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分管副总" prop="deputyId">
<el-select v-model="form.deputyId" placeholder="请选择项目后自动填充" disabled>
<el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="提报人" prop="reporterId">
<el-input v-model="reporterName" placeholder="当前登录用户" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收货人" prop="consigneeUser">
<el-select v-model="form.consigneeUser" placeholder="请选择收货人" clearable filterable :disabled="isReadOnly">
<el-option v-for="user in userList" :key="user.nickName" :label="user.nickName" :value="user.nickName" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="收货地址" prop="consigneeAddress">
<el-input type="textarea" v-model="form.consigneeAddress" placeholder="请输入收货地址" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="consigneeContact">
<el-input v-model="form.consigneeContact" placeholder="请输入收货人联系方式" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否备件类" prop="spareFlag">
<el-radio-group v-model="form.spareFlag" disabled>
<el-radio v-for="dict in spare_flag" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目采购状态" prop="projectPurchaseStatus">
<el-radio-group v-model="form.projectPurchaseStatus" disabled>
<el-radio v-for="dict in project_purchase_status" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<el-card shadow="never">
<template #header>
<div class="table-header standard">
<div class="title-wrapper">
<span class="title-dot standard-dot"></span>
<span class="title-text">标准物料</span>
</div>
<el-button v-if="canEditMaterial" type="primary" icon="Plus" size="small" @click="handleAddMaterial('1')"></el-button>
</div>
</template>
<el-table v-loading="materialLoading" :data="standardMaterialList" border :row-class-name="getRowClassName">
<el-table-column label="序号" width="70" align="center">
<template #default="scope">
{{ scope.$index + 1 }}
</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">
<dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" />
</template>
</el-table-column>
<el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" align="center" />
<el-table-column label="本次采购数量" prop="purchaseAmount" width="140" align="center" />
<el-table-column label="需求到货时间" prop="arrivalTime" width="180" align="center">
<template #default="scope">
<span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d}') || scope.row.arrivalTime }}</span>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="操作" fixed="right" width="150" align="center" v-if="canEditMaterial">
<template #default="scope">
<el-button link type="warning" @click="splitMaterial(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>
</el-table-column>
</el-table>
</el-card>
<el-card shadow="never">
<template #header>
<div class="table-header non-standard">
<div class="title-wrapper">
<span class="title-dot non-standard-dot"></span>
<span class="title-text">非标准物料</span>
</div>
<el-button v-if="canEditMaterial" type="primary" icon="Plus" size="small" @click="handleAddMaterial('2')"></el-button>
</div>
</template>
<el-table v-loading="materialLoading" :data="nonStandardMaterialList" border :row-class-name="getRowClassName">
<el-table-column label="序号" width="70" align="center">
<template #default="scope">
{{ scope.$index + 1 }}
</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">
<dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" />
</template>
</el-table-column>
<el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" align="center" />
<el-table-column label="本次采购数量" prop="purchaseAmount" width="140" align="center" />
<el-table-column label="需求到货时间" prop="arrivalTime" width="180" align="center">
<template #default="scope">
<span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d}') || scope.row.arrivalTime }}</span>
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="操作" fixed="right" width="150" align="center" v-if="canEditMaterial">
<template #default="scope">
<el-button link type="warning" @click="splitMaterial(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>
</el-table-column>
</el-table>
</el-card>
<el-dialog :title="materialDialog.title" v-model="materialDialog.visible" width="800px" append-to-body>
<el-form ref="materialFormRef" :model="materialForm" :rules="materialRules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="计划标识" prop="purchasePlanFlag">
<el-radio-group v-model="materialForm.purchasePlanFlag">
<el-radio v-for="dict in purchase_plan_flag" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="materialForm.materialFlag === '1'" :span="12">
<el-form-item label="关联物料编码">
<el-input v-model="materialForm.relationMaterialCode" disabled />
</el-form-item>
</el-col>
<el-col v-if="materialForm.materialFlag === '1'" :span="12">
<el-form-item label="关联物料名称">
<el-input v-model="materialForm.relationMaterialName" disabled />
</el-form-item>
</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="请选择采购标准物料" readonly>
<template #suffix>
<el-icon
style="cursor: pointer"
:disabled="!canEditMaterial"
@click="openMaterialSelect"
>
<Search />
</el-icon>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采购物料名称" prop="materialName">
<el-input v-model="materialForm.materialName" placeholder="请输入采购物料名称" />
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item label="物料ID" prop="materialId">-->
<!-- <el-input v-model="materialForm.materialId" placeholder="请输入物料ID" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <el-form-item label="销售物料ID" prop="relationMaterialId">-->
<!-- <el-input v-model="materialForm.relationMaterialId" placeholder="请输入销售物料ID" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<el-form-item label="单位" prop="unitId">
<el-select v-model="materialForm.unitId" placeholder="请选择单位" clearable filterable>
<el-option v-for="item in unitInfoList" :key="item.unitId" :label="item.unitName" :value="item.unitId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="需采购数量" prop="needPurchaseAmount">
<el-input-number
v-model="materialForm.needPurchaseAmount"
:min="0"
class="w-full"
placeholder="请输入数量"
:precision="2"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="本次采购数量" prop="purchaseAmount">
<el-input-number v-model="materialForm.purchaseAmount" :min="0" class="w-full" placeholder="请输入数量" :precision="2" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="需求到货时间" prop="arrivalTime">
<el-date-picker v-model="materialForm.arrivalTime" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择需求到货时间" />
</el-form-item>
</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-form-item label="备注" prop="remark">
<el-input v-model="materialForm.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="materialLoading" @click="submitMaterialForm"> </el-button>
<el-button @click="materialDialog.visible = false"> </el-button>
</div>
</template>
</el-dialog>
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<approvalRecord ref="approvalRecordRef" />
<ProjectSelect ref="projectSelectRef" :multiple="false" @confirm-call-back="projectInfoSelectCallBack" />
<MaterialSelect ref="materialSelectRef" :multiple="false" @confirm-call-back="materialSelectCallBack" />
</div>
</template>
<script setup name="ProjectPurchaseEdit" lang="ts">
import { computed, reactive, ref, watch, getCurrentInstance, toRefs, onMounted, nextTick } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import type { FormInstance } from 'element-plus';
import {
addProjectPurchase,
getProjectPurchase,
updateProjectPurchase,
projectPurchaseSubmitAndFlowStart,
getProjectMaterialsByProjectId
} from '@/api/oa/erp/projectPurchase';
import type { ProjectPurchaseForm } from '@/api/oa/erp/projectPurchase/types';
import { getErpProjectPurchaseMaterialList } from '@/api/oa/erp/projectPurchaseMaterial';
import type { ProjectPurchaseMaterialVO, ProjectPurchaseMaterialForm } from '@/api/oa/erp/projectPurchaseMaterial/types';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import ApprovalButton from '@/components/Process/approvalButton.vue';
import ProjectSelect from '@/components/ProjectSelect/index.vue';
import MaterialSelect from '@/components/MaterialSelect/index.vue';
import { FlowCodeEnum } from '@/enums/OAEnum';
import type { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
import { listUser } from '@/api/system/user';
import type { UserVO, UserQuery } from '@/api/system/user/types';
import { useUserStore } from '@/store/modules/user';
import { getBaseUnitInfoList } from '@/api/oa/base/unitInfo';
import { getInfo } from '@/api/login';
import type { MaterialInfoVO } from '@/api/oa/base/materialInfo/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const { active_flag, project_purchase_status, spare_flag, purchase_plan_flag, material_flag } = toRefs<any>(
proxy?.useDict('active_flag', 'project_purchase_status', 'spare_flag', 'purchase_plan_flag', 'material_flag')
);
const routeParams = reactive<Record<string, any>>({});
const pageType = ref<string>((route.query.type as string) || 'add');
const isReadOnly = computed(() => pageType.value === 'view' || pageType.value === 'approval');
const userRoles = ref<string[]>([]);
const hasCgRole = computed(() => userRoles.value?.some((role) => role === 'CGJL'));
const canEditMaterial = computed(() => !isReadOnly.value || hasCgRole.value);
const pageLoading = ref(false);
const buttonLoading = ref(false);
const projectPurchaseFormRef = ref<FormInstance>();
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const projectSelectRef = ref<InstanceType<typeof ProjectSelect>>();
const materialSelectRef = ref<InstanceType<typeof MaterialSelect>>();
const taskVariables = ref<Record<string, any>>({});
const initFormData: ProjectPurchaseForm & { projectName?: string } = {
projectPurchaseId: undefined,
projectId: undefined,
projectCode: undefined,
purchaseCode: undefined,
projectName: undefined,
managerId: undefined,
chargeId: undefined,
deputyId: undefined,
deptId: undefined,
reporterId: undefined,
consigneeUser: undefined,
consigneeAddress: undefined,
consigneeContact: undefined,
projectPurchaseStatus: undefined,
flowStatus: undefined,
sortOrder: undefined,
relationId: undefined,
remark: undefined,
spareFlag: undefined,
activeFlag: undefined
};
const form = ref<ProjectPurchaseForm & { projectName?: string }>({ ...initFormData });
const rules = {
projectCode: [{ required: true, message: '项目号不能为空', trigger: 'blur' }],
consigneeUser: [{ required: true, message: '收货人不能为空', trigger: 'blur' }],
consigneeAddress: [{ required: true, message: '收货地址不能为空', trigger: 'blur' }]
};
const materialList = ref<ProjectPurchaseMaterialVO[]>([]);
const materialLoading = ref(false);
// 标准物料列表materialFlag 为 '1'
const standardMaterialList = computed(() => {
return materialList.value.filter((item) => item.materialFlag === '1');
});
// 非标准物料列表materialFlag 为 '2'
const nonStandardMaterialList = computed(() => {
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 }) => {
// 计划外通常是 '2' 或 '否',根据实际字典值调整
if (row.purchasePlanFlag === '2' || row.purchasePlanFlag === '否' || row.purchasePlanFlag === '计划外') {
return 'plan-out-row';
}
return '';
};
const openMaterialSelect = () => {
if (!canEditMaterial.value) {
return;
}
materialSelectRef.value?.open();
};
const materialSelectCallBack = (data: MaterialInfoVO[]) => {
if (!data?.length) {
return;
}
const material = data[0];
materialForm.value.materialId = material.materialId;
materialForm.value.materialCode = material.materialCode;
materialForm.value.materialName = material.materialName;
materialForm.value.purchaseMaterialName = material.materialName;
// materialForm.value.relationMaterialCode = material.materialCode;
// materialForm.value.relationMaterialName = material.materialName;
};
// 用户列表
const userList = ref<UserVO[]>([]);
const getUserList = async () => {
const params: UserQuery = { pageNum: 1, pageSize: 9999 };
const res = await listUser(params);
userList.value = res.rows || [];
};
// 单位列表
const unitInfoList = ref<any[]>([]);
const getUnitInfoListSelect = async () => {
const res = await getBaseUnitInfoList(null);
unitInfoList.value = res.data || [];
};
// 提报人名称(当前登录用户)
const reporterName = computed(() => {
return userStore.nickname || '';
});
const materialDialog = reactive({
visible: false,
title: ''
});
const materialFormRef = ref<FormInstance>();
const editingMaterialIndex = ref<number>(-1);
const initMaterialFormData: ProjectPurchaseMaterialForm = {
purchaseMaterialId: undefined,
projectPurchaseId: undefined,
relationDetailsId: undefined,
spareFlag: undefined,
purchasePlanFlag: undefined,
materialFlag: undefined,
materialId: undefined,
relationMaterialId: undefined,
relationMaterialCode: undefined,
relationMaterialName: undefined,
materialCode: undefined,
materialName: undefined,
purchaseMaterialName: undefined,
specificationDescription: undefined,
unitId: undefined,
needPurchaseAmount: undefined,
purchaseAmount: undefined,
arrivalTime: undefined,
serialNumber: undefined,
remark: undefined,
activeFlag: undefined
};
const materialForm = ref<ProjectPurchaseMaterialForm>({ ...initMaterialFormData });
const materialRules = {
materialName: [{ required: true, message: '采购物料名称不能为空', trigger: 'blur' }],
purchaseAmount: [{ required: true, message: '采购数量不能为空', trigger: 'blur' }]
};
/**
* 重置主表表单
*/
const resetForm = () => {
Object.assign(form.value, initFormData);
form.value.projectPurchaseStatus = '1';
form.value.flowStatus = 'draft';
// 自动填充当前登录用户为提报人
if (userStore.userId) {
form.value.reporterId = userStore.userId;
}
if (!form.value.activeFlag && active_flag.value?.length) {
form.value.activeFlag = active_flag.value[0].value;
}
};
/**
* 重置物料表单
*/
const resetMaterialForm = () => {
Object.assign(materialForm.value, initMaterialFormData);
editingMaterialIndex.value = -1;
};
/**
* 根据项目采购ID加载详情
*/
const loadDetail = async (projectPurchaseId?: string | number) => {
if (!projectPurchaseId) {
resetForm();
materialList.value = [];
return;
}
try {
resetForm();
const res = await getProjectPurchase(projectPurchaseId);
Object.assign(form.value, res?.data || {});
if (!form.value.projectPurchaseStatus) {
form.value.projectPurchaseStatus = '1';
}
if (!form.value.flowStatus) {
form.value.flowStatus = 'draft';
}
// 确保用户列表已加载然后根据收货人名称查找对应的用户ID
if (userList.value.length === 0) {
await getUserList();
}
materialList.value = res?.data.purchaseMaterialList || [];
} finally {
pageLoading.value = false;
}
};
/**
* 提交主表单
*/
const buildProjectPurchasePayload = () => {
if (!projectPurchaseFormRef.value) return;
const normalizedMaterials: ProjectPurchaseMaterialForm[] = materialList.value.map((item) => {
const payloadItem: ProjectPurchaseMaterialForm = {
...item,
projectPurchaseId: form.value.projectPurchaseId
};
payloadItem.purchaseMaterialId = undefined;
return payloadItem;
});
return {
...form.value,
purchaseMaterialList: normalizedMaterials
} as ProjectPurchaseForm & {
projectName?: string;
purchaseMaterialList?: ProjectPurchaseMaterialForm[];
};
};
const submitForm = async (status: string, mode?: boolean) => {
await projectPurchaseFormRef.value.validate();
buttonLoading.value = true;
try {
const payload = buildProjectPurchasePayload();
if (status !== 'draft') {
payload.flowCode = FlowCodeEnum.PROJECT_PURCHASE_CODE;
payload.variables = {
projectPurchaseId: payload.projectPurchaseId,
projectCode: payload.projectCode,
projectName: payload.projectName,
deptId: form.value.deptId
};
payload.bizExt = {
businessTitle: '项目采购审批',
businessCode: payload.projectCode
};
payload.projectPurchaseStatus = '2';
payload.flowStatus = 'waiting';
const res: any = await projectPurchaseSubmitAndFlowStart(payload);
if (res?.data) {
Object.assign(form.value, res.data);
} else {
Object.assign(form.value, payload);
}
proxy?.$modal.msgSuccess('操作成功');
proxy?.$tab.closePage();
router.go(-1);
} else {
payload.projectPurchaseStatus = '1';
payload.flowStatus = 'draft';
if (payload.projectPurchaseId) {
await updateProjectPurchase(payload);
} else {
const res: any = await addProjectPurchase(payload);
const newId = res?.data?.projectPurchaseId ?? res?.data ?? res?.msg;
if (newId) {
payload.projectPurchaseId = newId;
form.value.projectPurchaseId = newId as any;
}
}
Object.assign(form.value, payload);
proxy?.$modal.msgSuccess('暂存成功');
proxy?.$tab.closePage();
router.go(-1);
}
} finally {
buttonLoading.value = false;
}
};
/**
* 编辑物料
*/
const handleEditMaterial = (row: ProjectPurchaseMaterialVO) => {
if (!canEditMaterial.value) {
return;
}
resetMaterialForm();
Object.assign(materialForm.value, row);
editingMaterialIndex.value = materialList.value.findIndex(
(item) => item === row || (!!row.purchaseMaterialId && item.purchaseMaterialId === row.purchaseMaterialId)
);
materialDialog.title = '编辑物料';
materialDialog.visible = true;
};
/**
* 新增物料
*/
const handleAddMaterial = (materialFlag) => {
if (!canEditMaterial.value) {
return;
}
resetMaterialForm();
materialForm.value.materialFlag = materialFlag;
materialForm.value.purchasePlanFlag = '2';
materialForm.value.projectPurchaseId = form.value.projectPurchaseId;
materialDialog.title = materialFlag === '1' ? '新增标准物料' : '新增非标准物料';
materialDialog.visible = true;
};
/**
* 提交物料表单仅操作本地列表
*/
const submitMaterialForm = () => {
if (!canEditMaterial.value) {
return;
}
materialFormRef.value?.validate((valid: boolean) => {
if (!valid) return;
if (materialForm.value.purchaseMaterialId) {
// 编辑:更新列表中的物料
const index = materialList.value.findIndex((item) => item.purchaseMaterialId === materialForm.value.purchaseMaterialId);
if (index !== -1) {
Object.assign(materialList.value[index], materialForm.value);
}
} else if (editingMaterialIndex.value !== -1) {
Object.assign(materialList.value[editingMaterialIndex.value], materialForm.value);
} else {
// 新增:添加到列表
materialList.value.push({ ...materialForm.value } as ProjectPurchaseMaterialVO);
}
proxy?.$modal.msgSuccess('操作成功');
materialDialog.visible = false;
editingMaterialIndex.value = -1;
});
};
/**
* 删除物料仅操作本地列表
*/
const handleDeleteMaterial = async (row: ProjectPurchaseMaterialVO) => {
if (!canEditMaterial.value) {
return;
}
await proxy?.$modal.confirm(`是否确认删除物料"${row.materialName}"`);
const index = materialList.value.findIndex(
(item) => (row.purchaseMaterialId && item.purchaseMaterialId === row.purchaseMaterialId) || (!row.purchaseMaterialId && item === row)
);
if (index !== -1) {
materialList.value.splice(index, 1);
proxy?.$modal.msgSuccess('删除成功');
}
};
/**
* 拆分物料为计划外
*/
const splitMaterial = (row: ProjectPurchaseMaterialVO) => {
if (!canEditMaterial.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('拆分成功');
};
/**
* 打开审批弹窗
*/
const approvalVerifyOpen = async () => {
taskVariables.value = {
projectPurchaseId: form.value.projectPurchaseId,
projectCode: form.value.projectCode,
projectName: form.value.projectName
};
const taskId = (routeParams.taskId as string) || (route.query.taskId as string);
if (taskId) {
await submitVerifyRef.value?.openDialog(taskId);
}
};
/**
* 查看审批记录
*/
const handleApprovalRecord = () => {
if (form.value.projectPurchaseId) {
approvalRecordRef.value?.init(form.value.projectPurchaseId);
}
};
/**
* 提交审批后的回调
*/
const submitCallback = async () => {
if (hasCgRole.value) {
await projectPurchaseFormRef.value?.validate();
const payload = buildProjectPurchasePayload();
if (payload?.projectPurchaseId) {
await updateProjectPurchase(payload);
Object.assign(form.value, payload);
}
}
await proxy?.$tab.closePage(route);
router.go(-1);
};
// 打开项目选择框
const openProjectSelect = () => {
if (isReadOnly.value) {
return;
}
projectSelectRef.value?.open();
};
// 项目选择回调,自动填充项目相关信息
const projectInfoSelectCallBack = (data: ProjectInfoVO[]) => {
if (data && data.length > 0) {
const project = data[0];
form.value.projectId = project.projectId;
form.value.projectCode = project.projectCode;
form.value.projectName = project.projectName;
form.value.managerId = project.managerId;
form.value.chargeId = project.chargeId;
form.value.deputyId = project.deputyId;
form.value.spareFlag = project.spareFlag;
form.value.deptId = project.deptId;
loadProjectMaterialsFromSource(project.projectId as string | number, project.spareFlag);
}
};
// 初始化调用
const loadSelectOptions = () => {
getUserList();
getUnitInfoListSelect();
};
onMounted(async () => {
nextTick(async () => {
// 获取路由参数
Object.assign(routeParams, route.query);
// pageLoading.value = true;
loadSelectOptions();
const id = routeParams.id as string | number;
const type = routeParams.type as string;
pageType.value = type || 'add';
try {
const userInfoRes = await getInfo();
userRoles.value = userInfoRes?.data?.roles || [];
} catch (error) {
userRoles.value = [];
}
const shouldLoadDetail = !!id && (type === 'update' || type === 'view' || type === 'approval');
await loadDetail(shouldLoadDetail ? (id as string | number) : undefined);
pageLoading.value = false;
});
});
</script>
<style scoped>
:deep(.plan-out-row) {
background-color: #e9d571 !important;
}
:deep(.plan-out-row:hover) {
background-color: #d8bb3a !important;
}
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 600;
font-size: 16px;
}
.title-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.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>