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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>