Files
maintenance_system/src/views/materialManagement/procurementPlan.vue
re-JZzzz febbcb3241 fix: 修复样式和组件问题并增强数据图表功能
修复多个组件中的::v-deep语法错误和el-row的gutter属性绑定
将el-button的type="text"更新为link类型
在AppMain.vue中添加div包裹keep-alive以解决过渡问题
增强DataAnalysis组件的数据处理能力,添加计算属性和默认值
添加fetchChuRuKuCountBarData调用以完善库存管理功能
2025-10-09 20:22:04 +08:00

1285 lines
49 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="procurementPlan">
<el-row :gutter="20">
<el-col :span="13">
<el-card>
<div style="display: flex;align-items: center;height: 120px;justify-content: space-around;">
<div class="img">
<img src="/assets/caigou.png" alt="">
</div>
<div class="item">
<div class="text">
待审批计划
</div>
<div class="count" style="color: rgba(255, 178, 30, 1);">
{{ pendingCount }}
</div>
</div>
<div class="item">
<div class="text">
已批准计划
</div>
<div class="count" style="color: rgba(67, 101, 220, 1);">
{{ approvedCount }}
</div>
</div>
<div class="item">
<div class="text">
采购中计划
</div>
<div class="count" style="color: rgba(113, 214, 213, 1);">
{{ purchasingCount }}
</div>
</div>
<div class="item">
<div class="text">
已完成计划
</div>
<div class="count" style="color: rgba(0, 184, 122, 1);">
{{ completedCount }}
</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="11">
<el-card>
<div style="display: flex;align-items: center;height: 120px;justify-content: space-around;">
<div class="img">
<img src="/assets/qian.jpg" alt="">
</div>
<div class="item">
<div class="text">
本年度已采购金额
</div>
<div class="count" style="color: rgba(255, 153, 0, 1);">
{{ yearlyAmount.shijiJine }}
</div>
</div>
<div class="item">
<div class="text">
本年度采购预算金额
</div>
<div class="count" style="color: rgba(67, 101, 220, 1);">
{{ yearlyAmount.yujiJine }}
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-row style="margin-top: 20px;">
<el-col :span="24">
<el-card style="border-radius: 10px;">
<div class="content">
<div class="tabs">
<!-- <el-button type="success">导出</el-button> -->
<el-button type="primary" @click="handleAdd">新建采购申请单</el-button>
</div>
<!-- 标签页导航 -->
<div class="tabs">
<el-badge :value="total" type="success">
<el-button :type="activeTab === 'all' ? 'primary' : ''"
@click="changeTab('all')">全部</el-button>
</el-badge>
<el-badge :value="pendingCount" type="warning">
<el-button :type="activeTab === '3' ? 'primary' : ''"
@click="changeTab('3')">待审批</el-button>
</el-badge>
<el-badge :value="purchasingCount" type="primary">
<el-button :type="activeTab === '5' ? 'primary' : ''"
@click="changeTab('5')">采购中</el-button>
</el-badge>
<el-badge :value="rejectedCount" type="danger">
<el-button :type="activeTab === '7' ? 'primary' : ''"
@click="changeTab('7')">
未通过
</el-button>
</el-badge>
<el-badge :value="approvedCount" type="success">
<el-button :type="activeTab === '9' ? 'primary' : ''"
@click="changeTab('9')">已通过</el-button>
</el-badge>
<el-badge :value="completedCount" type="success">
<el-button :type="activeTab === '11' ? 'primary' : ''"
@click="changeTab('11')">已完成</el-button>
</el-badge>
</div>
<!-- 表格 -->
<el-table :data="caigouPlanList" border style="width: 100%;margin-top: 15px;">
<el-table-column label="计划编号" align="center" prop="jihuaBianhao" />
<el-table-column label="计划名称" align="center" prop="jihuaName" />
<el-table-column label="申请部门" align="center" prop="caigouDanweiName" />
<el-table-column label="申请人" align="center" prop="jingbanrenName" />
<el-table-column prop="createTime" label="申请日期" align="center" />
<el-table-column label="预计金额" align="center" prop="yujiJine" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="wz_caigou_examine" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button link @click="handleView(scope.row)">查看</el-button>
<el-button link @click="handleUpdate(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-section">
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 添加修改弹窗 -->
<el-dialog v-model="isDialogVisible" :title="dialogTitle" width="60%" :close-on-click-modal="false">
<div class="new-procurement-form">
<!-- 基础信息 -->
<div class="form-section">
<h3>基础信息</h3>
<!-- 输入框行 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="计划名称">
<el-input v-model="form.jihuaName" placeholder="请填写计划名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同名称">
<el-input v-model="form.hetonName" placeholder="请填写合同名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同类型">
<el-select v-model="form.hetonType" placeholder="请选择">
<el-option v-for="option in wz_contract_type" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 下拉框行 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="采购类型">
<el-select v-model="form.caigouType" placeholder="请选择">
<el-option v-for="option in wz_purchase_type" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="仓库地址">
<el-input v-model="form.cangkuUrl" placeholder="请输入仓库地址" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="申请原因">
<el-input v-model="form.reason" placeholder="请输入申请原因" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 供应商信息 -->
<div class="form-section">
<h3>供应商信息</h3>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="供应商单位">
<el-select v-model="form.gonyingshangId" placeholder="请选择">
<el-option v-for="option in supplierList" :key="option.id" :label="option.supplierName"
:value="option.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="送货时间">
<el-date-picker v-model="form.chuhuoTime" type="date" placeholder="请选择送货日期"
value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 产品信息 -->
<div class="form-section">
<h3>产品信息</h3>
<el-table :data="form.opsCaigouPlanChanpinBos" border style="width: 100%">
<el-table-column prop="chanpinName" label="产品名称">
<template #default="scope">
<el-input v-model="scope.row.chanpinName" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column prop="chanpinType" label="产品型号">
<template #default="scope">
<el-input v-model="scope.row.chanpinType" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column prop="shebeiType" label="设备类型">
<template #default="scope">
<el-select v-model="scope.row.shebeiType" placeholder="请选择">
<el-option v-for="option in wz_device_type" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="chanpinMonovalent" label="产品单价">
<template #default="scope">
<el-input v-model="scope.row.chanpinMonovalent" placeholder="请填写" type="number"
@change="calculateTotalPrice(scope.row)" min="0" />
</template>
</el-table-column>
<el-table-column prop="goumaiNumber" label="购买数量">
<template #default="scope">
<el-input v-model="scope.row.goumaiNumber" placeholder="请填写" type="number"
@change="calculateTotalPrice(scope.row)" min="0" />
</template>
</el-table-column>
<el-table-column prop="danwei" label="单位">
<template #default="scope">
<el-input v-model="scope.row.danwei" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column prop="yontu" label="用途(简要描述)">
<template #default="scope">
<el-input v-model="scope.row.yontu" placeholder="请填写" />
</template>
</el-table-column>
<el-table-column prop="totalPrice" label="合计" :formatter="calculateTotalPrice">
<template #default="scope">
<span>{{ calculateTotalPrice(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="80">
<template #default="scope">
<el-button type="text" @click="removeProduct(scope.$index)"
:disabled="form.opsCaigouPlanChanpinBos.length <= 1">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="primary" size="small" @click="addProduct" style="margin-top: 10px">添加产品</el-button>
</div>
<!-- 合同条款 -->
<div class="form-section">
<h3>合同条款</h3>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="付款方式">
<el-select v-model="form.fukuantiaojian" placeholder="请选择">
<el-option v-for="option in wz_payment_terms" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发票开具方式">
<el-select v-model="form.fapiaoKjfs" placeholder="请选择">
<el-option v-for="option in wz_invoicing_way" :key="option.value"
:label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 附件上传 -->
<div class="form-section">
<h3>附件上传</h3>
<FileUpload ref="fileUploadRef" :isDrag="true" v-model="form.opsCaigouPlanFilesBos"
:is-show-tip="false"
:file-type="['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'pdf', 'png', 'jpg', 'jpeg']" />
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelOperation">取消</el-button>
<el-button v-if="currentOperation !== 'update'" @click="saveDraft" :loading="buttonLoading">保存草稿</el-button>
<el-button type="primary" @click="getSubmitFunction()" :loading="buttonLoading">
{{ currentOperation === 'add' ? '提交申请' : '更新' }}
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.procurementPlan {
background-color: #F2F8FC;
padding: 20px;
}
.img {
img {
display: block;
width: 80px;
height: 80px;
}
}
.item {
text-align: center;
.text {
font-size: 14px;
}
.count {
font-size: 25px;
font-weight: 600;
text-align: left;
margin-top: 10px;
}
}
.tabs {
display: flex;
gap: 10px;
padding: 10px 0;
}
.content {
padding: 10px 0;
}
/* 分页区域样式 */
.pagination-section {
background-color: #fff;
border-radius: 8px;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 15px;
padding: 10px 0;
}
.pagination-controls .el-pagination {
margin: 0;
}
.pagination-controls .el-pagination button {
min-width: 32px;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.pagination-controls .el-pagination .el-pager li {
min-width: 32px;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.pagination-controls .el-pagination .el-pager li.active {
background-color: #409eff;
color: #fff;
}
</style>
<script setup lang="ts">
import { ref, reactive, computed, getCurrentInstance, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useProcurementDraftStore } from '@/store/modules/procurementDraft';
import { } from '@/views/materialManagement/components/upload.vue'
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { wz_invoicing_way, wz_payment_terms, wz_purchase_type, wz_contract_type, wz_caigou_examine,wz_device_type } = toRefs<any>(proxy?.useDict('wz_device_type','wz_invoicing_way', 'wz_payment_terms', 'wz_purchase_type', 'wz_contract_type', 'wz_caigou_examine'));
import { listCaigouPlan, getSupplierList, addCaigouPlan, caigouPlanDetail, updateCaigouPlan,getCount } from '@/api/wuziguanli/caigouPlan';
import { CaigouPlanVO, CaigouPlanQuery, CaigouPlanForm } from '@/api/wuziguanli/caigouPlan/types';
import { useRouter } from 'vue-router';
const router = useRouter();
import FileUpload from '@/views/materialManagement/components/upload.vue';
// 导入用户store
import { useUserStore } from '@/store/modules/user';
// 获取用户store
const userStore = useUserStore();
const caigouPlanList = ref<CaigouPlanVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const total = ref(0);
// 文件上传组件引用
const fileUploadRef = ref<InstanceType<typeof FileUpload>>();
// 标签页状态变量
const activeTab = ref('all');
// 各状态数量变量
const pendingCount = ref(0);
const purchasingCount = ref(0);
const rejectedCount = ref(0);
const approvedCount = ref(0);
const completedCount = ref(0);
// 标签页切换函数
const changeTab = (tab: string) => {
activeTab.value = tab;
// 重置页码
queryParams.value.pageNum = 1;
// 重新获取列表数据
getList();
};
const initFormData: CaigouPlanForm = {
id: undefined,
projectId: undefined,
jihuaName: undefined,
jihuaBianhao: undefined,
caigouDanwei: undefined,
caigouDanweiName: undefined,
jingbanren: undefined,
jingbanrenName: undefined,
hetonType: undefined,
caigouType: undefined,
cangkuUrl: undefined,
hetonName: undefined,
gonyingshangId: undefined,
chuhuoTime: undefined,
fukuantiaojian: undefined,
fapiaoKjfs: undefined,
status: undefined,
shenheStatus: undefined,
yujiJine: undefined,
shijiJine: undefined,
opsCaigouPlanFilesBos: undefined,
opsCaigouPlanChanpinBos: [
{
chanpinName: '',
chanpinType: '',
chanpinMonovalent: 0,
goumaiNumber: 0,
danwei: '',
yontu: '',
totalPrice: 0
}
],
reason: undefined,
}
const data = reactive<PageData<CaigouPlanForm, CaigouPlanQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
projectId: undefined,
jihuaName: undefined,
jihuaBianhao: undefined,
caigouDanwei: undefined,
caigouDanweiName: undefined,
jingbanren: undefined,
jingbanrenName: undefined,
hetonType: undefined,
caigouType: undefined,
cangkuUrl: undefined,
hetonName: undefined,
gonyingshangId: undefined,
chuhuoTime: undefined,
fukuantiaojian: undefined,
fapiaoKjfs: undefined,
status: undefined,
shenheStatus: undefined,
yujiJine: undefined,
shijiJine: undefined,
reason: undefined,
opsCaigouPlanChanpinBos: [
{
chanpinName: '',
chanpinType: '',
chanpinMonovalent: 0,
goumaiNumber: 0,
danwei: '',
yontu: '',
totalPrice: 0
}
],
opsCaigouPlanFilesBos: undefined,
params: {
}
},
rules: {}
});
const { queryParams, form, rules } = toRefs(data);
// 查询运维-物资-采购计划单年度金额
const yearlyAmount=ref({
yujiJine: 0,
shijiJine: 0,
})
const getYearlyAmount = async () => {
try {
// 检查项目ID是否存在
if (!queryParams.value.projectId) {
ElMessage({ message: '项目ID不能为空', type: 'warning' });
// 重置年度金额数据
yearlyAmount.value = {
yujiJine: 0,
shijiJine: 0
};
return;
}
const res = await getCount(queryParams.value.projectId);
// 检查响应数据结构
if (res && res.code === 200 && res.data) {
// 确保金额数据有效
yearlyAmount.value.shijiJine = res.data.shijiJine || 0;
yearlyAmount.value.yujiJine = res.data.yujiJine || 0;
} else {
ElMessage({ message: '获取年度金额数据格式异常', type: 'error' });
console.error('获取年度金额响应数据异常:', res);
// 重置年度金额数据
yearlyAmount.value = {
yujiJine: 0,
shijiJine: 0
};
}
} catch (error) {
console.error('获取年度金额失败:', error);
ElMessage({ message: '获取年度金额失败,请重试', type: 'error' });
// 重置年度金额数据,避免显示错误数据
yearlyAmount.value = {
yujiJine: 0,
shijiJine: 0
};
}
}
/** 查询运维-物资-采购计划单列表 */
const getList = async () => {
loading.value = true;
// 创建查询参数的副本,避免直接修改原参数
const queryParamsCopy = { ...queryParams.value };
// 根据当前选中的标签页设置状态过滤条件
if (activeTab.value !== 'all') {
queryParamsCopy.status = activeTab.value;
} else {
// 如果是'全部'标签页,不设置状态过滤条件
queryParamsCopy.status = undefined;
}
try {
// 检查项目ID是否存在
if (!queryParamsCopy.projectId) {
ElMessage({ message: '项目ID不能为空', type: 'warning' });
// 重置统计数据
pendingCount.value = 0;
purchasingCount.value = 0;
rejectedCount.value = 0;
approvedCount.value = 0;
completedCount.value = 0;
total.value = 0;
caigouPlanList.value = [];
return;
}
// 先查询所有数据来统计各状态数量
const allDataRes = await listCaigouPlan({ ...queryParamsCopy, status: undefined });
// 检查响应数据结构
if (allDataRes && allDataRes.code === 200 && Array.isArray(allDataRes.rows)) {
// 统计各状态数量
pendingCount.value = allDataRes.rows.filter(item => item.status === '3').length;
purchasingCount.value = allDataRes.rows.filter(item => item.status === '5').length;
rejectedCount.value = allDataRes.rows.filter(item => item.status === '7').length;
approvedCount.value = allDataRes.rows.filter(item => item.status === '9').length;
completedCount.value = allDataRes.rows.filter(item => item.status === '11').length;
total.value = allDataRes.total || 0;
// 然后查询当前标签页的数据
const filteredRes = await listCaigouPlan(queryParamsCopy);
// 检查过滤后的数据结构
if (filteredRes && filteredRes.code === 200 && Array.isArray(filteredRes.rows)) {
caigouPlanList.value = filteredRes.rows;
} else {
ElMessage({ message: '获取过滤后的数据格式异常', type: 'error' });
console.error('获取过滤后的数据响应异常:', filteredRes);
caigouPlanList.value = [];
}
} else {
ElMessage({ message: '获取统计数据格式异常', type: 'error' });
console.error('获取统计数据响应异常:', allDataRes);
// 重置统计数据
pendingCount.value = 0;
purchasingCount.value = 0;
rejectedCount.value = 0;
approvedCount.value = 0;
completedCount.value = 0;
total.value = 0;
caigouPlanList.value = [];
}
} catch (error) {
console.error('获取采购计划列表失败:', error);
ElMessage({ message: '获取数据失败,请重试', type: 'error' });
// 重置统计数据,避免显示错误数据
pendingCount.value = 0;
purchasingCount.value = 0;
rejectedCount.value = 0;
approvedCount.value = 0;
completedCount.value = 0;
total.value = 0;
caigouPlanList.value = [];
} finally {
loading.value = false;
}
}
// 新增采购计划单提交函数
const addCaigouPlanSubmit = async () => {
buttonLoading.value = true; // 显示按钮加载状态
try {
// 创建表单数据的深拷贝,避免直接修改原表单数据
const submitData = JSON.parse(JSON.stringify(form.value));
// 数据完整性检查
if (!submitData || !submitData.projectId) {
ElMessage({ message: '项目信息不完整,无法提交', type: 'error' });
return;
}
// 提交数据前确保文件列表格式正确
if (submitData.opsCaigouPlanFilesBos === undefined) {
submitData.opsCaigouPlanFilesBos = [];
}
const res = await addCaigouPlan(submitData);
// 检查响应数据结构
if (res && res.code === 200) {
ElMessage({ message: '采购申请单已成功提交,等待审批!', type: 'success' });
try {
// 提交成功后,删除相关的草稿
const procurementDraftStore = useProcurementDraftStore();
const draftList = procurementDraftStore.getDraftList();
// 查找并删除与当前计划名称匹配的草稿
if (form.value.jihuaName && draftList && draftList.length > 0) {
const matchingDraft = draftList.find(draft =>
draft.name === form.value.jihuaName
);
if (matchingDraft) {
procurementDraftStore.deleteDraft(matchingDraft.id);
}
}
} catch (draftError) {
console.error('删除草稿时发生错误:', draftError);
// 草稿删除失败不应影响主流程
}
// 刷新列表数据
await getList();
// 关闭对话框并重置表单
resetNewProcurementForm();
isDialogVisible.value = false;
} else {
// 显示详细的错误信息
console.error('新增采购计划单失败:', res);
ElMessage({
message: res?.msg || '新增采购计划单失败,请重试',
type: 'error'
});
}
} catch (error) {
console.error('新增采购计划单操作异常:', error);
// 根据错误类型提供更具体的错误信息
let errorMessage = '操作失败';
if (error.message) {
errorMessage = `操作失败: ${error.message}`;
}
ElMessage({ message: errorMessage, type: 'error' });
} finally {
buttonLoading.value = false; // 无论成功失败,都关闭加载状态
}
}
// 更新采购计划单提交函数
const updateCaigouPlanSubmit = async () => {
buttonLoading.value = true; // 显示按钮加载状态
try {
// 创建表单数据的深拷贝,避免直接修改原表单数据
const submitData = JSON.parse(JSON.stringify(form.value));
// 数据完整性检查
if (!submitData || !submitData.id || !submitData.projectId) {
ElMessage({ message: '计划ID或项目信息不完整无法更新', type: 'error' });
return;
}
// 提交数据前确保文件列表格式正确
if (submitData.opsCaigouPlanFilesBos === undefined) {
submitData.opsCaigouPlanFilesBos = [];
}
const res = await updateCaigouPlan(submitData);
// 检查响应数据结构
if (res && res.code === 200) {
ElMessage({ message: '采购申请单已成功更新!', type: 'success' });
// 刷新列表数据
await getList();
// 关闭对话框并重置表单
resetNewProcurementForm();
isDialogVisible.value = false;
} else {
// 显示详细的错误信息
console.error('更新采购计划单失败:', res);
ElMessage({
message: res?.msg || '更新采购计划单失败,请重试',
type: 'error'
});
}
} catch (error) {
console.error('更新采购计划单操作异常:', error);
// 根据错误类型提供更具体的错误信息
let errorMessage = '操作失败';
if (error.message) {
errorMessage = `操作失败: ${error.message}`;
}
ElMessage({ message: errorMessage, type: 'error' });
} finally {
buttonLoading.value = false; // 无论成功失败,都关闭加载状态
}
}
// 采购商列表
const supplierList = ref([]);
const getSupplierLists = async () => {
try {
// 检查项目ID是否存在
if (!userStore.selectedProject || !userStore.selectedProject.id) {
ElMessage({ message: '请先选择项目', type: 'warning' });
return;
}
const res = await getSupplierList({
projectId: userStore.selectedProject.id
});
// 检查响应数据结构
if (res && res.code === 200 && Array.isArray(res.data)) {
supplierList.value = res.data;
} else {
ElMessage({ message: '获取供应商列表数据格式异常', type: 'error' });
console.error('获取供应商列表响应数据异常:', res);
}
} catch (error) {
console.error('获取供应商列表失败:', error);
ElMessage({ message: '获取供应商列表失败,请重试', type: 'error' });
// 在严重错误情况下清空列表,避免显示错误数据
supplierList.value = [];
}
}
onMounted(() => {
getList();
getSupplierLists();
getYearlyAmount();
});
// 监听用户选择的项目变化
watch(() => userStore.selectedProject, (newProject) => {
if (newProject && newProject.id) {
queryParams.value.projectId = newProject.id;
// 只在新增表单时设置projectId编辑表单保留原有值
if (!form.value.id) {
form.value.projectId = newProject.id;
}
// 调用getList刷新数据
getList();
getYearlyAmount();
}
}, { immediate: true, deep: true });
// 对话框是否可见
const isDialogVisible = ref(false);
// 当前操作类型:'add' 或 'update'
const currentOperation = ref('add');
// 对话框标题
const dialogTitle = computed(() => {
return currentOperation.value === 'add' ? '新建采购申请单' : '编辑采购申请单';
});
// 跳转查看
const handleView = (row) => {
router.push({
path: '/materialManagement/planDetails',
query: {
id: row.id
}
});
};
// 处理新增
const handleAdd = async () => {
currentOperation.value = 'add';
resetNewProcurementForm();
// 检查是否有保存的草稿
const procurementDraftStore = useProcurementDraftStore();
const draftList = procurementDraftStore.getDraftList();
if (draftList && draftList.length > 0) {
try {
// 提示用户是否恢复上次保存的草稿
await ElMessageBox.confirm(
'检测到您有未提交的草稿,是否恢复?',
'恢复草稿',
{
confirmButtonText: '恢复',
cancelButtonText: '不恢复',
type: 'info'
}
);
// 获取最新的草稿并恢复
const latestDraft = draftList[0]; // 假设列表是按创建时间降序排列的
const draftData = procurementDraftStore.getDraft(latestDraft.id);
if (draftData) {
form.value = JSON.parse(JSON.stringify(draftData.content));
// 确保产品列表和附件列表有默认值
if (!form.value.opsCaigouPlanChanpinBos || form.value.opsCaigouPlanChanpinBos.length === 0) {
form.value.opsCaigouPlanChanpinBos = [{chanpinName: '',chanpinType: '',chanpinMonovalent: '',goumaiNumber: '',danwei: '',yontu: '',totalPrice: ''}];
}
if (!form.value.opsCaigouPlanFilesBos) {
form.value.opsCaigouPlanFilesBos = [];
}
}
} catch (error) {
// 用户选择不恢复草稿,继续使用空表单
if (error !== 'cancel') {
console.error('恢复草稿时发生错误:', error);
}
}
}
isDialogVisible.value = true;
};
// 根据操作类型获取对应的提交函数
const getSubmitFunction = computed(() => {
return currentOperation.value === 'add' ? submitAddProcurement : submitUpdateProcurement;
});
// 处理编辑
const handleUpdate = async (row) => {
currentOperation.value = 'update';
buttonLoading.value = true;
try {
// 获取采购计划详情
const res = await caigouPlanDetail(row.id);
if (res.code === 200) {
// 深拷贝数据,避免直接修改原始数据
form.value = JSON.parse(JSON.stringify(res.data));
// 将数据字段从Vos后缀改为Bos后缀
if (form.value.opsCaigouPlanChanpinVos) {
form.value.opsCaigouPlanChanpinBos = form.value.opsCaigouPlanChanpinVos;
delete form.value.opsCaigouPlanChanpinVos;
}
if (form.value.opsCaigouPlanFilesVos) {
// 转换文件数据格式为FileUpload组件期望的格式
form.value.opsCaigouPlanFilesBos = form.value.opsCaigouPlanFilesVos.map(file => ({
ossId: file.fileId,
name: file.fileName,
url: file.fileUrl
}));
delete form.value.opsCaigouPlanFilesVos;
}
// 确保产品列表和附件列表有默认值
if (!form.value.opsCaigouPlanChanpinBos || form.value.opsCaigouPlanChanpinBos.length === 0) {
form.value.opsCaigouPlanChanpinBos = [{
chanpinName: '',
chanpinType: '',
chanpinMonovalent: 0,
goumaiNumber: 0,
danwei: '',
yontu: '',
totalPrice: 0
}];
}
if (!form.value.opsCaigouPlanFilesBos || form.value.opsCaigouPlanFilesBos.length === 0) {
form.value.opsCaigouPlanFilesBos = [];
}
// 显示对话框
isDialogVisible.value = true;
} else {
ElMessage({ message: res.msg || '获取采购计划详情失败', type: 'error' });
}
} catch (error) {
ElMessage({ message: '获取采购计划详情失败', type: 'error' });
} finally {
buttonLoading.value = false;
}
};
// 计算产品总价
const calculateTotalPrice = (row) => {
if (!row.chanpinMonovalent || !row.goumaiNumber) {
row.totalPrice = '0.00'; // 保存计算结果到对象中
return '0.00';
}
const price = parseFloat(row.chanpinMonovalent);
const quantity = parseInt(row.goumaiNumber);
if (isNaN(price) || isNaN(quantity)) {
row.totalPrice = '0.00'; // 保存计算结果到对象中
return '0.00';
}
const result = (price * quantity).toFixed(2);
row.totalPrice = result; // 保存计算结果到对象中
return result;
};
// 添加产品
const addProduct = () => {
form.value.opsCaigouPlanChanpinBos.push({
chanpinName: '',
chanpinType: '',
chanpinMonovalent: 0,
goumaiNumber: 0,
danwei: '',
totalPrice: 0
});
};
// 删除产品
const removeProduct = (index) => {
if (form.value.opsCaigouPlanChanpinBos.length <= 1) {
ElMessage({ message: '至少保留一个产品信息', type: 'warning' });
return;
}
form.value.opsCaigouPlanChanpinBos.splice(index, 1);
};
// 重置新建采购申请表单
const resetNewProcurementForm = () => {
form.value.jihuaName = '';
form.value.hetonName = '';
form.value.hetonType = '';
form.value.caigouType = '';
form.value.cangkuUrl = '';
form.value.danwei = '';
form.value.chuhuoTime = '';
form.value.fukuantiaojian = '';
form.value.fapiaoKjfs = '';
form.value.gonyingshangId = '';
form.value.gonyingshangName = '';
form.value.reason = '';
form.value.opsCaigouPlanChanpinBos = [{
chanpinName: '',
chanpinType: '',
chanpinMonovalent: '',
goumaiNumber: '',
danwei: '',
yontu: '',
totalPrice: ''
}];
form.value.opsCaigouPlanFilesBos = [];
// 清空文件上传组件中的文件列表
if (fileUploadRef.value) {
fileUploadRef.value.clearAllFiles();
}
};
// 表单校验函数
const validateForm = () => {
// 基础信息校验
if (!form.value.jihuaName.trim()) {
ElMessage({ message: '请填写计划名称', type: 'error' });
return false;
}
if (!form.value.hetonName.trim()) {
ElMessage({ message: '请填写合同名称', type: 'error' });
return false;
}
if (!form.value.hetonType) {
ElMessage({ message: '请选择合同类型', type: 'error' });
return false;
}
if (!form.value.caigouType) {
ElMessage({ message: '请选择采购类型', type: 'error' });
return false;
}
if (!form.value.cangkuUrl) {
ElMessage({ message: '请选择仓库地址', type: 'error' });
return false;
}
if (!form.value.gonyingshangId) {
ElMessage({ message: '请选择供应商单位', type: 'error' });
return false;
}
if (!form.value.reason) {
ElMessage({ message: '请填写申请原因', type: 'error' });
return false;
}
// 附件校验
if (!form.value.opsCaigouPlanFilesBos || form.value.opsCaigouPlanFilesBos.length === 0) {
ElMessage({ message: '请至少上传一个附件', type: 'error' });
return false;
}
// 产品信息校验
const hasValidProduct = form.value.opsCaigouPlanChanpinBos.some(product => {
return product.chanpinName &&
product.chanpinType &&
product.chanpinMonovalent && parseFloat(product.chanpinMonovalent) > 0 &&
product.goumaiNumber && parseInt(product.goumaiNumber) > 0 &&
product.danwei &&
product.yontu;
});
if (!hasValidProduct) {
ElMessage({ message: '请至少填写一个有效的产品信息', type: 'error' });
return false;
}
// 检查每个产品的有效性
for (let i = 0; i < form.value.opsCaigouPlanChanpinBos.length; i++) {
const product = form.value.opsCaigouPlanChanpinBos[i];
if (product.chanpinName || product.chanpinType || product.chanpinMonovalent || product.goumaiNumber) {
if (!product.chanpinName) {
ElMessage({ message: `${i + 1}行产品:请填写产品名称`, type: 'error' });
return false;
}
if (!product.chanpinType) {
ElMessage({ message: `${i + 1}行产品:请填写产品型号`, type: 'error' });
return false;
}
if (!product.chanpinMonovalent) {
ElMessage({ message: `${i + 1}行产品:请填写产品单价`, type: 'error' });
return false;
}
if (parseFloat(product.chanpinMonovalent) <= 0) {
ElMessage({ message: `${i + 1}行产品产品单价必须大于0`, type: 'error' });
return false;
}
if (!product.goumaiNumber) {
ElMessage({ message: `${i + 1}行产品:请填写购买数量`, type: 'error' });
return false;
}
if (!product.shebeiType) {
ElMessage({ message: `${i + 1}行产品:请填写设备类型`, type: 'error' });
return false;
}
if (parseInt(product.goumaiNumber) <= 0) {
ElMessage({ message: `${i + 1}行产品购买数量必须大于0`, type: 'error' });
return false;
}
if (!product.danwei) {
ElMessage({ message: `${i + 1}行产品:请填写单位`, type: 'error' });
return false;
}
if (!product.yontu) {
ElMessage({ message: `${i + 1}行产品:请填写用途`, type: 'error' });
return false;
}
}
}
return true;
};
// 新增申请提交
const submitAddProcurement = async () => {
// 在提交前,为所有产品行重新计算并保存总价
form.value.opsCaigouPlanChanpinBos.forEach(product => {
calculateTotalPrice(product);
});
// 表单验证
if (!validateForm()) {
return;
}
try {
// 确认提交
await ElMessageBox.confirm(
'确定要提交采购申请单吗?提交后将进入审批流程,不可撤销。',
'确认操作',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
);
// 调用新增提交函数
await addCaigouPlanSubmit();
} catch (error) {
// 处理用户取消或其他错误
if (error !== 'cancel') {
ElMessage({ message: '提交过程中发生错误,请重试', type: 'error' });
}
}
}
// 更新申请提交
const submitUpdateProcurement = async () => {
// 在提交前,为所有产品行重新计算并保存总价
form.value.opsCaigouPlanChanpinBos.forEach(product => {
calculateTotalPrice(product);
});
// 表单验证
if (!validateForm()) {
return;
}
try {
// 确认提交
await ElMessageBox.confirm(
'确定要更新采购申请单吗?',
'确认操作',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
);
// 调用更新提交函数
await updateCaigouPlanSubmit();
} catch (error) {
// 处理用户取消或其他错误
if (error !== 'cancel') {
ElMessage({ message: '更新过程中发生错误,请重试', type: 'error' });
}
}
}
// 检查表单是否有内容
const hasFormContent = () => {
// 检查基础信息字段
if (form.value.jihuaName ||
form.value.hetonName ||
form.value.hetonType ||
form.value.caigouType ||
form.value.cangkuUrl ||
form.value.danwei ||
form.value.chuhuoTime ||
form.value.fukuantiaojian ||
form.value.fapiaoKjfs ||
form.value.reason) {
return true;
}
// 检查产品信息
if (form.value.opsCaigouPlanChanpinBos) {
for (const product of form.value.opsCaigouPlanChanpinBos) {
if (product.chanpinName ||
product.chanpinType ||
product.chanpinMonovalent ||
product.goumaiNumber ||
product.danwei ||
product.yontu) {
return true;
}
}
}
// 检查附件
if (form.value.opsCaigouPlanFilesBos && form.value.opsCaigouPlanFilesBos.length > 0) {
return true;
}
return false;
};
// 取消操作
const cancelOperation = async () => {
// 检查是否有表单内容
if (hasFormContent()) {
try {
// 提示用户确认是否放弃编辑
await ElMessageBox.confirm(
'您有未保存的内容,确定要取消吗?',
'确认取消',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
// 重置表单
resetNewProcurementForm();
// 关闭对话框
isDialogVisible.value = false;
} catch (error) {
// 用户取消了确认操作
if (error !== 'cancel') {
console.error('取消操作时发生错误:', error);
}
// 不执行任何操作,保持对话框打开
}
} else {
// 没有表单内容,直接关闭对话框并重置
resetNewProcurementForm();
isDialogVisible.value = false;
}
};
// 保存草稿
const saveDraft = async () => {
buttonLoading.value = true;
try {
// 检查是否有计划名称
if (!form.value.jihuaName || !form.value.jihuaName.trim()) {
ElMessage({ message: '请填写计划名称后再保存草稿', type: 'warning' });
return;
}
// 获取草稿存储
const procurementDraftStore = useProcurementDraftStore();
// 保存草稿到pinia存储
const savedDraft = procurementDraftStore.saveDraft(form.value.jihuaName, form.value);
// 显示成功消息
ElMessage({ message: '草稿保存成功', type: 'success' });
// 关闭对话框
isDialogVisible.value = false;
} catch (error) {
ElMessage({ message: '草稿保存失败,请重试', type: 'error' });
} finally {
buttonLoading.value = false;
}
};
// 组件卸载时清除草稿
onUnmounted(() => {
try {
// 获取草稿存储
const procurementDraftStore = useProcurementDraftStore();
// 清除所有草稿
procurementDraftStore.clearAllDrafts();
} catch (error) {
console.error('组件卸载时清除草稿失败:', error);
// 这里不显示错误消息,因为组件已经卸载
}
});
</script>