refactor(crmQuoteInfo): 统一和优化报价单编辑及列表功能

- 统一路由参数管理,改用 routeParams 代替 route.query,增强一致性
- 将审批状态和页面类型获取逻辑调整为从路由参数和表单中读取
- submitVerify 组件新增 taskVariables 绑定,用于传递流程变量
- 删除不再使用的状态规范化方法及相关计算属性,精简代码
- 新增当前编辑物料行索引 currentMaterialIndex,优化物料编辑逻辑
- 物料编辑表单提交和删除逻辑针对索引进行操作,提升稳定性
- 暂存和提交审批流程中新增流程变量赋值及统一成功后行为
- 审批回调和弹窗打开逻辑调用改为统一接口,提升复用性
- 报价单列表操作列宽扩大,调整按钮展示和权限控制逻辑
- 编辑权限限制为仅草稿和退回状态,查看权限限制为非草稿状态
- 删除操作限制为草稿状态,新增模板导出按钮及审批记录组件引用
- 优化审批记录查看方式,统一通过新引入的 ApprovalRecord 组件展示
- 删除废弃的查看详情权限判断方法及相关注释,代码更简洁明了
dev
zangch@mesnac.com 5 days ago
parent 28dd7ab843
commit 38c215671a

@ -7,8 +7,8 @@
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.quoteId as any"
:status="approvalStatus"
:pageType="pageTypeForButton"
:status="form.flowStatus as any"
:pageType="routeParams.type"
:mode="false"
/>
</el-card>
@ -397,7 +397,7 @@
</div>
</el-card>
<ApprovalRecord ref="approvalRecordRef" />
<SubmitVerify ref="submitVerifyRef" @submit-callback="submitCallback" />
<SubmitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 附件上传对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form label-width="80px">
@ -440,8 +440,14 @@ const { business_direction, currency_type, quote_category, contract_type, materi
const router = useRouter();
const route = useRoute();
// projectInfo/projectPurchase
const routeParams = ref<Record<string, any>>({});
// /type
const isView = computed(() => route.query.type === 'view' || route.query.type === 'approval');
const isView = computed(() => routeParams.value.type === 'view' || routeParams.value.type === 'approval');
// submitVerify
const taskVariables = ref<Record<string, any>>({});
const buttonLoading = ref(false);
const quoteFormRef = ref<ElFormInstance>();
@ -496,27 +502,6 @@ const getUnitInfoListSelect = async () => {
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const normalizeFlowStatus = (status?: string) => {
if (!status) return 'draft';
const dictList = wf_business_status.value || [];
const match = dictList.find((item: any) => item?.value === status || item?.label === status);
return match?.value || status;
};
const approvalStatus = computed(() => normalizeFlowStatus((form as any).flowStatus as any));
const pageTypeForButton = computed(() => {
const taskId = route.query.taskId as any;
const type = route.query.type as string | undefined;
if (taskId) {
return 'approval';
}
if (type) {
return type;
}
return (form.quoteId ? 'update' : 'add') as string;
});
//
const materialDialog = reactive({ visible: false, title: '' });
const materialFormRef = ref<ElFormInstance>();
@ -542,6 +527,8 @@ const initMaterialFormData: CrmQuoteMaterialForm & { materialFlag?: string } = {
activeFlag: '1'
};
const materialForm = ref<CrmQuoteMaterialForm & { materialFlag?: string }>({ ...initMaterialFormData });
// null
const currentMaterialIndex = ref<number | null>(null);
const materialRules = {
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
amount: [{ required: true, message: '数量不能为空', trigger: 'blur' }],
@ -567,6 +554,7 @@ const saleMaterialSelectCallBack = (data: any) => {
const resetMaterialForm = () => {
materialForm.value = { ...initMaterialFormData };
materialFormRef.value?.resetFields();
currentMaterialIndex.value = null;
};
const cancelMaterial = () => {
resetMaterialForm();
@ -593,6 +581,7 @@ const calculateSubtotal = () => {
const handleAddMaterial = () => {
resetMaterialForm();
materialForm.value.quoteId = form.quoteId as any;
currentMaterialIndex.value = null;
materialDialog.visible = true;
materialDialog.title = '新增报价物料';
};
@ -600,28 +589,27 @@ const handleEditMaterial = (row: CrmQuoteMaterialForm & { materialFlag?: string
resetMaterialForm();
// 使 materialFlagID/
const flag = (row as any).materialFlag ?? ((row as any).materialId ? '1' : '2');
const idx = materialRows.value.indexOf(row as any);
currentMaterialIndex.value = idx !== -1 ? idx : null;
materialForm.value = { ...(row as any), materialFlag: flag };
materialDialog.visible = true;
materialDialog.title = '编辑报价物料';
};
const handleDeleteMaterial = async (row: CrmQuoteMaterialForm) => {
await proxy?.$modal.confirm('是否确认删除该报价物料?');
const idx = materialRows.value.findIndex((x: any) => x.quoteMaterialId === (row as any).quoteMaterialId);
if (idx !== -1) materialRows.value.splice(idx, 1);
else {
const i2 = materialRows.value.indexOf(row);
if (i2 !== -1) materialRows.value.splice(i2, 1);
const idx = materialRows.value.indexOf(row as any);
if (idx !== -1) {
materialRows.value.splice(idx, 1);
}
proxy?.$modal.msgSuccess('删除成功');
};
const submitMaterialForm = () => {
materialFormRef.value?.validate((valid: boolean) => {
if (!valid) return;
if ((materialForm.value as any).quoteMaterialId) {
const idx = materialRows.value.findIndex((x: any) => x.quoteMaterialId === (materialForm.value as any).quoteMaterialId);
if (idx !== -1) materialRows.value[idx] = { ...materialForm.value } as any;
if (currentMaterialIndex.value !== null && currentMaterialIndex.value >= 0) {
materialRows.value[currentMaterialIndex.value] = { ...materialForm.value } as any;
} else {
const newItem = { ...materialForm.value, quoteMaterialId: Date.now() } as any;
const newItem = { ...materialForm.value } as any;
materialRows.value.push(newItem);
}
proxy?.$modal.msgSuccess('操作成功');
@ -801,6 +789,7 @@ const submitForm = (status: string, mode: boolean) => {
}))
};
if (status === 'draft') {
// === ===
payload.quoteStatus = '1';
payload.flowStatus = 'draft';
if (!form.quoteId) {
@ -810,6 +799,7 @@ const submitForm = (status: string, mode: boolean) => {
}
proxy?.$modal.msgSuccess('暂存成功');
} else {
// === ===
payload.flowCode = FlowCodeEnum.QUOTE_CODE; //OACQ
payload.variables = {
quoteId: form.quoteId,
@ -827,8 +817,9 @@ const submitForm = (status: string, mode: boolean) => {
if (res?.data) {
Object.assign(form, res.data as any);
}
proxy?.$modal.msgSuccess('已提交并发起流程');
proxy?.$modal.msgSuccess('提交成功');
}
// projectInfo
proxy?.$tab.closePage(route as any);
router.go(-1);
} finally {
@ -837,7 +828,17 @@ const submitForm = (status: string, mode: boolean) => {
});
};
const approvalVerifyOpen = async () => {
await submitVerifyRef.value?.openDialog(route.query.taskId as any);
//
taskVariables.value = {
quoteId: form.quoteId,
quoteName: form.quoteName,
quoteCode: form.quoteCode,
totalPrice: totalIncludingTax.value
};
const taskId = routeParams.value.taskId;
if (taskId) {
await submitVerifyRef.value?.openDialog(taskId);
}
};
const handleApprovalRecord = () => {
@ -846,12 +847,16 @@ const handleApprovalRecord = () => {
}
};
/** 提交审批回调(与 projectInfo 保持一致) */
const submitCallback = async () => {
await proxy?.$tab.closePage(route as any);
router.go(-1);
};
onMounted(async () => {
// projectInfo/projectPurchase
routeParams.value = { ...route.query };
//
const contactRes = await getCrmCustomerContactList({});
customerContactList.value = contactRes.data || [];

@ -262,22 +262,29 @@
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="150" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" fixed="right" width="180" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="查看详情" placement="top" v-if="canViewDetail(scope.row)">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="修改" placement="top" v-if="scope.row.quoteStatus === '1'">
<!-- 修改仅草稿或退回状态可编辑 projectInfo 保持一致使用 flowStatus 判断 -->
<el-tooltip content="修改" placement="top" v-if="scope.row.flowStatus === 'draft' || scope.row.flowStatus === 'back'">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa/crm:crmQuoteInfo:edit']"></el-button>
</el-tooltip>
<!-- 审批记录统一通过编辑页顶部组件查看这里暂不展示 -->
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['oa/crm:crmQuoteInfo:remove']"></el-button>
<!-- 查看非草稿状态可查看 -->
<el-tooltip content="查看详情" placement="top" v-if="scope.row.flowStatus && scope.row.flowStatus !== 'draft'">
<el-button link type="info" icon="DocumentChecked" @click="handleView(scope.row)"></el-button>
</el-tooltip>
<!-- 审批记录非草稿状态可查看 -->
<el-tooltip content="审批记录" placement="top" v-if="scope.row.flowStatus && scope.row.flowStatus !== 'draft'">
<el-button link type="warning" icon="Tickets" @click="handleApprovalRecord(scope.row)"></el-button>
</el-tooltip>
<!-- 删除仅草稿状态可删除 -->
<el-tooltip content="删除" placement="top" v-if="!scope.row.flowStatus || scope.row.flowStatus === 'draft'">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['oa/crm:crmQuoteInfo:remove']"></el-button>
</el-tooltip>
<!-- 模板导出 -->
<el-tooltip content="模板导出" placement="top">
<el-button
link
type="warning"
type="success"
icon="Document"
@click="handleExportTemplate(scope.row)"
v-hasPermi="['oa/crm:crmQuoteInfo:exportTemplate']"
@ -468,6 +475,8 @@
</div>
</template>
</el-dialog>-->
<!-- 审批记录 -->
<ApprovalRecord ref="approvalRecordRef" />
</div>
</template>
@ -478,6 +487,7 @@ import { CrmQuoteInfoForm, CrmQuoteInfoQuery, CrmQuoteInfoVO } from '@/api/oa/cr
import { getCrmCustomerContactList } from '@/api/oa/crm/customerContact';
import FileSaver from 'file-saver';
import { blobValidate } from '@/utils/ruoyi';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
@ -497,6 +507,7 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const crmQuoteInfoFormRef = ref<ElFormInstance>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const dialog = reactive<DialogOption>({
visible: false,
@ -703,11 +714,6 @@ const handleView = (row?: CrmQuoteInfoVO) => {
});
};
/** 判断是否可以查看详情:仅非草稿状态可进入查看页 */
const canViewDetail = (row: CrmQuoteInfoVO) => {
return row.quoteStatus !== '1';
};
/** 提交按钮 */
const submitForm = () => {
crmQuoteInfoFormRef.value?.validate(async (valid: boolean) => {
@ -745,6 +751,11 @@ const handleExport = () => {
);
};
/** 审批记录按钮操作 */
const handleApprovalRecord = (row: CrmQuoteInfoVO) => {
approvalRecordRef.value?.init(row.quoteId as any);
};
/** 模板导出支持选中或行内按钮GET Blob 下载) */
const handleExportTemplate = async (row?: CrmQuoteInfoVO) => {
const _quoteId = row?.quoteId || ids.value[0];

Loading…
Cancel
Save