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.

630 lines
24 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 #append>
<el-button icon="Search" @click="openProjectSelect" :disabled="isReadOnly">选择</el-button>
</template>
</el-input>
</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="flex items-center justify-between">
<span>采购物料</span>
<div>
<el-button icon="Refresh" @click="loadMaterials" class="mr-2" :disabled="materialLoading">刷新</el-button>
<el-button
type="primary"
icon="Plus"
@click="handleAddMaterial"
:disabled="disableMaterialActions"
v-if="!isReadOnly"
>
新增物料
</el-button>
</div>
</div>
</template>
<el-table v-loading="materialLoading" :data="materialList" border>
<el-table-column label="物料名称" prop="materialName" min-width="160" show-overflow-tooltip />
<el-table-column label="物料编码" prop="materialCode" min-width="140" show-overflow-tooltip />
<el-table-column label="规格参数" prop="specificationDescription" min-width="160" show-overflow-tooltip />
<el-table-column label="计划标识" prop="purchasePlanFlag" width="100">
<template #default="scope">
<dict-tag :options="purchase_plan_flag" :value="scope.row.purchasePlanFlag" />
</template>
</el-table-column>
<el-table-column label="标准物料" prop="materialFlag" width="100">
<template #default="scope">
<dict-tag :options="material_flag" :value="scope.row.materialFlag" />
</template>
</el-table-column>
<el-table-column label="需采购数量" prop="needPurchaseAmount" width="120" />
<el-table-column label="本次采购数量" prop="purchaseAmount" width="140" />
<el-table-column label="需求到货时间" prop="arrivalTime" width="180">
<template #default="scope">
<span>{{ proxy?.parseTime?.(scope.row.arrivalTime, '{y}-{m}-{d} {h}:{i}') || scope.row.arrivalTime }}</span>
</template>
</el-table-column>
<el-table-column label="是否备件" prop="spareFlag" width="100">
<template #default="scope">
<dict-tag :options="spare_flag" :value="scope.row.spareFlag" />
</template>
</el-table-column>
<el-table-column label="激活标识" prop="activeFlag" width="100">
<template #default="scope">
<dict-tag :options="active_flag" :value="scope.row.activeFlag" />
</template>
</el-table-column>
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" fixed="right" width="160" align="center" v-if="!isReadOnly">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleEditMaterial(scope.row)"></el-button>
<el-button link type="danger" icon="Delete" @click="handleDeleteMaterial(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog :title="materialDialog.title" v-model="materialDialog.visible" width="600px" 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 :span="12">
<el-form-item label="标准物料" prop="materialFlag">
<el-radio-group v-model="materialForm.materialFlag">
<el-radio v-for="dict in material_flag" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</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="物料编码" prop="materialCode">
<el-input v-model="materialForm.materialCode" 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="24">
<el-form-item label="详细参数" prop="specificationDescription">
<el-input v-model="materialForm.specificationDescription" placeholder="请输入详细参数" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位ID" prop="unitId">
<el-input v-model="materialForm.unitId" placeholder="请输入单位ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否备件" prop="spareFlag">
<el-radio-group v-model="materialForm.spareFlag">
<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="12">
<el-form-item label="需采购数量" prop="needPurchaseAmount">
<el-input-number v-model="materialForm.needPurchaseAmount" :min="0" :controls="false" class="w-full" placeholder="请输入数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="本次采购数量" prop="purchaseAmount">
<el-input-number v-model="materialForm.purchaseAmount" :min="0" :controls="false" class="w-full" placeholder="请输入数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="需求到货时间" prop="arrivalTime">
<el-date-picker v-model="materialForm.arrivalTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择需求到货时间" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="序号" prop="serialNumber">
<el-input-number v-model="materialForm.serialNumber" :min="0" :controls="false" class="w-full" placeholder="请输入序号" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="materialForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="激活标识" prop="activeFlag">
<el-radio-group v-model="materialForm.activeFlag">
<el-radio v-for="dict in active_flag" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</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" />
</div>
</template>
<script setup name="ProjectPurchaseEdit" lang="ts">
import { computed, reactive, ref, watch, getCurrentInstance, toRefs } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import type { FormInstance } from 'element-plus';
import { addProjectPurchase, getProjectPurchase, updateProjectPurchase, projectPurchaseSubmitAndFlowStart } from '@/api/oa/erp/projectPurchase';
import type { ProjectPurchaseForm } from '@/api/oa/erp/projectPurchase/types';
import {
listProjectPurchaseMaterial,
addProjectPurchaseMaterial,
updateProjectPurchaseMaterial,
delProjectPurchaseMaterial
} 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 { 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';
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 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 taskVariables = ref<Record<string, any>>({});
const initFormData: ProjectPurchaseForm & { projectName?: string } = {
projectPurchaseId: undefined,
projectId: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
chargeId: undefined,
deputyId: 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);
const disableMaterialActions = computed(() => isReadOnly.value || !form.value.projectPurchaseId || materialLoading.value);
// 用户列表
const userList = ref<UserVO[]>([]);
const getUserList = async () => {
const params: UserQuery = { pageNum: 1, pageSize: 9999 };
const res = await listUser(params);
userList.value = res.rows || [];
};
// 提报人名称(当前登录用户)
const reporterName = computed(() => {
return userStore.nickname || '';
});
const materialDialog = reactive({
visible: false,
title: ''
});
const materialFormRef = ref<FormInstance>();
const initMaterialFormData: ProjectPurchaseMaterialForm = {
purchaseMaterialId: undefined,
projectPurchaseId: undefined,
relationDetailsId: undefined,
spareFlag: undefined,
purchasePlanFlag: undefined,
materialFlag: undefined,
materialId: undefined,
relationMaterialId: undefined,
materialCode: undefined,
materialName: 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);
};
const loadDetail = async (projectPurchaseId: string | number) => {
if (!projectPurchaseId) {
resetForm();
materialList.value = [];
return;
}
pageLoading.value = true;
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();
}
await loadMaterials();
} finally {
pageLoading.value = false;
}
};
// 初始化时加载用户列表
getUserList();
const loadMaterials = async () => {
if (!form.value.projectPurchaseId) {
materialList.value = [];
return;
}
materialLoading.value = true;
try {
const res = await listProjectPurchaseMaterial({ projectPurchaseId: form.value.projectPurchaseId, pageNum: 1, pageSize: 999 });
materialList.value = res?.rows || [];
} finally {
materialLoading.value = false;
}
};
const submitForm = async (status: string, mode?: boolean) => {
if (!projectPurchaseFormRef.value) return;
try {
await projectPurchaseFormRef.value.validate();
} catch (error) {
return;
}
buttonLoading.value = true;
try {
const payload: ProjectPurchaseForm & { projectName?: string } = { ...form.value };
if (status !== 'draft') {
payload.flowCode = FlowCodeEnum.PROJECT_PURCHASE_CODE;
payload.variables = {
projectPurchaseId: payload.projectPurchaseId,
projectCode: payload.projectCode,
projectName: payload.projectName
};
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 handleAddMaterial = () => {
if (isReadOnly.value) {
return;
}
if (!form.value.projectPurchaseId) {
proxy?.$modal.msgWarning('请先保存项目采购信息');
return;
}
resetMaterialForm();
materialForm.value.projectPurchaseId = form.value.projectPurchaseId;
materialForm.value.purchasePlanFlag = materialForm.value.purchasePlanFlag || (purchase_plan_flag.value?.[0]?.value ?? undefined);
materialForm.value.materialFlag = materialForm.value.materialFlag || (material_flag.value?.[0]?.value ?? undefined);
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.title = '新增物料';
materialDialog.visible = true;
};
const handleEditMaterial = (row: ProjectPurchaseMaterialVO) => {
if (isReadOnly.value) {
return;
}
resetMaterialForm();
Object.assign(materialForm.value, row);
materialDialog.title = '编辑物料';
materialDialog.visible = true;
};
const submitMaterialForm = () => {
if (isReadOnly.value) {
return;
}
materialFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
materialLoading.value = true;
try {
materialForm.value.projectPurchaseId = form.value.projectPurchaseId;
if (materialForm.value.purchaseMaterialId) {
await updateProjectPurchaseMaterial(materialForm.value);
} else {
await addProjectPurchaseMaterial(materialForm.value);
}
proxy?.$modal.msgSuccess('操作成功');
materialDialog.visible = false;
await loadMaterials();
} finally {
materialLoading.value = false;
}
});
};
const handleDeleteMaterial = async (row: ProjectPurchaseMaterialVO) => {
if (isReadOnly.value) {
return;
}
await proxy?.$modal.confirm(`是否确认删除物料"${row.materialName}"`);
await delProjectPurchaseMaterial(row.purchaseMaterialId);
proxy?.$modal.msgSuccess('删除成功');
await loadMaterials();
};
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 () => {
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;
}
};
watch(
() => ({
id: route.query.id,
type: route.query.type,
taskId: route.query.taskId
}),
async (val) => {
Object.assign(routeParams, val);
pageType.value = (val.type as string) || 'add';
const projectPurchaseId = val.id as string | number | undefined;
if (projectPurchaseId) {
await loadDetail(projectPurchaseId);
} else {
resetForm();
materialList.value = [];
}
},
{ immediate: true }
);
</script>