|
|
<template>
|
|
|
<div class="p-2">
|
|
|
<!-- 审批按钮组件(参考 contractInfo/edit.vue) -->
|
|
|
<el-card shadow="never" style="margin-top: 0">
|
|
|
<approvalButton
|
|
|
@submitForm="submitForm"
|
|
|
@approvalVerifyOpen="approvalVerifyOpen"
|
|
|
@handleApprovalRecord="handleApprovalRecord"
|
|
|
:buttonLoading="buttonLoading"
|
|
|
:id="form.contractChangeId"
|
|
|
:status="form.flowStatus"
|
|
|
:pageType="routeParams.type"
|
|
|
:mode="false"
|
|
|
/>
|
|
|
</el-card>
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
<template #header>
|
|
|
<span>{{ isEdit ? '编辑合同变更' : '合同变更申请' }}</span>
|
|
|
<el-button class="float-right" link @click="goBack">返回</el-button>
|
|
|
</template>
|
|
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
|
|
<el-form-item label="变更类型" prop="changeType">
|
|
|
<el-radio-group v-model="form.changeType" :disabled="isFormDisabled">
|
|
|
<el-radio v-for="dict in contract_change_type" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="选择合同" prop="contractId">
|
|
|
<el-input
|
|
|
v-model="selectedContractName"
|
|
|
placeholder="请选择已激活的合同(点击右侧图标选择)"
|
|
|
readonly
|
|
|
:disabled="isFormDisabled"
|
|
|
>
|
|
|
<template #suffix>
|
|
|
<el-icon style="cursor: pointer" @click="openContractSelect"><Search /></el-icon>
|
|
|
</template>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="变更原因" prop="changeReason">
|
|
|
<el-input
|
|
|
v-model="form.changeReason"
|
|
|
type="textarea"
|
|
|
:rows="3"
|
|
|
placeholder="请输入变更原因(解除合同时即解除原因)"
|
|
|
:disabled="isFormDisabled"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<!-- 内容变更时:下方按合同编辑页样式展示变更后合同信息、付款方式、物料,可编辑可删 -->
|
|
|
<template v-if="form.changeType === '1' && form.changeInfo">
|
|
|
<!-- 变更后合同信息(与合同编辑页同结构) -->
|
|
|
<el-card shadow="never" style="margin-top: 16px">
|
|
|
<template #header>
|
|
|
<div style="text-align: left; font-weight: bold; font-size: 18px">变更后合同信息</div>
|
|
|
</template>
|
|
|
<el-form :model="form.changeInfo" label-width="120px">
|
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="有无合同">
|
|
|
<el-radio-group v-model="form.changeInfo.contractFlag" :disabled="isFormDisabled">
|
|
|
<el-radio v-for="dict in contract_flag" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同编号">
|
|
|
<el-input v-model="form.changeInfo.contractCode" placeholder="请输入合同编号" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同名称">
|
|
|
<el-input v-model="form.changeInfo.contractName" placeholder="请输入合同名称" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同大类">
|
|
|
<el-select v-model="form.changeInfo.contractCategory" placeholder="请选择合同大类" :disabled="isFormDisabled" style="width: 100%">
|
|
|
<el-option v-for="dict in contract_category" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="业务方向">
|
|
|
<el-select v-model="form.changeInfo.businessDirection" placeholder="请选择业务方向" :disabled="isFormDisabled" style="width: 100%">
|
|
|
<el-option v-for="dict in business_direction" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同负责人">
|
|
|
<el-select v-model="form.changeInfo.contractManagerId" placeholder="请选择签订人" :disabled="isFormDisabled" filterable style="width: 100%">
|
|
|
<el-option v-for="item in userInfoList" :key="item.userId" :label="item.nickName" :value="item.userId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同签订日期">
|
|
|
<el-date-picker v-model="form.changeInfo.contractDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择合同时间" :disabled="isFormDisabled" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="付款账户">
|
|
|
<el-select v-model="form.changeInfo.paymentAccountId" placeholder="请选择付款账户" :disabled="isFormDisabled" clearable style="width: 100%">
|
|
|
<el-option v-for="item in paymentAccountList" :key="item.paymentAccountId" :label="`${item.accountType || ''} - ${item.accountNumber || ''}`" :value="item.paymentAccountId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="甲方公司">
|
|
|
<el-select v-model="form.changeInfo.oneCustomerId" placeholder="请选择甲方公司" :disabled="isFormDisabled" filterable style="width: 100%">
|
|
|
<el-option v-for="item in customerInfoList" :key="item.customerId" :label="item.customerName" :value="item.customerId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="乙方公司">
|
|
|
<el-select v-model="form.changeInfo.twoCustomerId" placeholder="请选择乙方公司" :disabled="isFormDisabled" filterable style="width: 100%">
|
|
|
<el-option v-for="item in customerInfoList" :key="item.customerId" :label="item.customerName" :value="item.customerId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="甲方授权代表">
|
|
|
<el-input v-model="form.changeInfo.oneRepresent" placeholder="甲方法人或授权代表" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="乙方授权代表">
|
|
|
<el-input v-model="form.changeInfo.twoRepresent" placeholder="乙方法人或授权代表" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="甲方签字日期">
|
|
|
<el-date-picker v-model="form.changeInfo.oneDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="甲方签字日期" :disabled="isFormDisabled" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="乙方签字日期">
|
|
|
<el-date-picker v-model="form.changeInfo.twoDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="乙方签字日期" :disabled="isFormDisabled" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同模板标识">
|
|
|
<el-radio-group v-model="form.changeInfo.contractTemplateFlag" :disabled="isFormDisabled">
|
|
|
<el-radio v-for="dict in contract_template_flag" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12" v-show="form.changeInfo.contractTemplateFlag !== '1'">
|
|
|
<el-form-item label="合同模板">
|
|
|
<el-select v-model="form.changeInfo.templateId" placeholder="请选择合同模板" :disabled="isFormDisabled" filterable style="width: 100%">
|
|
|
<el-option v-for="item in printTemplateList" :key="item.templateId" :label="item.templateName + '-' + (item.version || '')" :value="item.templateId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="合同总价">
|
|
|
<el-input v-model="form.changeInfo.totalPrice" placeholder="根据物料自动计算" disabled>
|
|
|
<template #append>元</template>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="质保期描述">
|
|
|
<el-input v-model="form.changeInfo.warrantyPeriodDescription" placeholder="如:验收合格后12个月" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="交货地点">
|
|
|
<el-input v-model="form.changeInfo.deliveryLocation" placeholder="如:甲方指定仓库" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="运输方式">
|
|
|
<el-input v-model="form.changeInfo.shipMethod" placeholder="如:汽运、空运" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="交付启动期限(天)">
|
|
|
<el-input-number v-model="form.changeInfo.deliveryStart" placeholder="天" :disabled="isFormDisabled" :min="0" controls-position="right" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="签订地点">
|
|
|
<el-input v-model="form.changeInfo.signingPlace" placeholder="签订地点" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="付款方式">
|
|
|
<el-select v-model="form.changeInfo.paymentMethod" placeholder="请选择付款方式" :disabled="isFormDisabled" clearable style="width: 100%">
|
|
|
<el-option v-for="item in paymentMethodOptions" :key="item" :label="item" :value="item" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="备注">
|
|
|
<el-input v-model="form.changeInfo.remark" type="textarea" placeholder="备注" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="附件">
|
|
|
<FileUpload
|
|
|
v-model="changeInfoOssId"
|
|
|
:limit="5"
|
|
|
:fileSize="20"
|
|
|
:fileType="['doc', 'docx', 'pdf', 'xls', 'xlsx']"
|
|
|
:disabled="isFormDisabled"
|
|
|
:isShowTip="true"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</el-form>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 变更后合同付款方式 -->
|
|
|
<el-card shadow="never" style="margin-top: 0">
|
|
|
<template #header>
|
|
|
<div style="text-align: left; font-weight: bold; font-size: 18px">合同付款方式</div>
|
|
|
</template>
|
|
|
<div style="margin-bottom: 16px">
|
|
|
<el-button type="primary" icon="Plus" v-if="!isFormDisabled" @click="handleAddPaymentMethod">新增付款方式</el-button>
|
|
|
</div>
|
|
|
<el-table :data="changePaymentMethodList" border>
|
|
|
<el-table-column label="序号" align="center" prop="sortOrder" width="80" />
|
|
|
<el-table-column label="付款条款" align="center" prop="paymentDescription" min-width="200" show-overflow-tooltip />
|
|
|
<el-table-column label="支付金额" align="center" prop="paymentAmount" width="100" />
|
|
|
<el-table-column label="支付比例(%)" align="center" prop="paymentPercentage" width="100" />
|
|
|
<el-table-column label="操作" align="center" fixed="right" width="150" v-if="!isFormDisabled">
|
|
|
<template #default="scope">
|
|
|
<el-button link type="primary" icon="Edit" @click="handleEditPaymentMethod(scope.row, scope.$index)">编辑</el-button>
|
|
|
<el-button link type="danger" icon="Delete" @click="handleDeletePaymentMethod(scope.$index)">删除</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 变更后合同物料 -->
|
|
|
<el-card shadow="never" style="margin-top: 0">
|
|
|
<template #header>
|
|
|
<div style="text-align: left; font-weight: bold; font-size: 18px">合同物料</div>
|
|
|
</template>
|
|
|
<div style="margin-bottom: 16px">
|
|
|
<el-button type="primary" icon="Plus" v-if="!isFormDisabled" @click="handleAddMaterial">新增合同物料</el-button>
|
|
|
</div>
|
|
|
<el-table :data="changeMaterialList" border>
|
|
|
<el-table-column label="物料编号" align="center" prop="materialCode" width="120" />
|
|
|
<el-table-column label="物料名称" align="center" prop="materialName" width="120" />
|
|
|
<el-table-column label="销售物料名称" align="center" prop="saleMaterialName" width="120" />
|
|
|
<el-table-column label="产品名称(合同显示)" align="center" prop="productName" min-width="140" />
|
|
|
<el-table-column label="规格描述" align="center" prop="specificationDescription" min-width="100" />
|
|
|
<el-table-column label="数量" align="center" prop="amount" width="80">
|
|
|
<template #default="scope">{{ scope.row.amount != null ? Number(scope.row.amount).toFixed(2) : '0.00' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="单位" align="center" prop="unitName" width="60" />
|
|
|
<el-table-column label="未税单价" align="center" prop="beforePrice" width="100">
|
|
|
<template #default="scope">{{ scope.row.beforePrice != null ? Number(scope.row.beforePrice).toFixed(2) : '0.00' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="税率(%)" align="center" prop="taxRate" width="80">
|
|
|
<template #default="scope">{{ scope.row.taxRate != null ? Number(scope.row.taxRate).toFixed(2) : '0.00' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="含税单价" align="center" prop="includingPrice" width="100">
|
|
|
<template #default="scope">{{ scope.row.includingPrice != null ? Number(scope.row.includingPrice).toFixed(2) : '0.00' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="小计" align="center" prop="subtotal" width="90">
|
|
|
<template #default="scope">{{ scope.row.subtotal != null ? Number(scope.row.subtotal).toFixed(2) : '0.00' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="备注" align="center" prop="remark" min-width="80" />
|
|
|
<el-table-column label="操作" align="center" fixed="right" width="150" v-if="!isFormDisabled">
|
|
|
<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-form-item label="合同物料备注" style="margin-bottom: 16px; margin-top: 16px">
|
|
|
<el-input v-model="form.changeInfo.materialRemark" type="textarea" :rows="3" placeholder="合同物料备注" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-card>
|
|
|
</template>
|
|
|
<el-form-item label="备注" prop="remark">
|
|
|
<el-input v-model="form.remark" type="textarea" placeholder="选填" :disabled="isFormDisabled" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 合同物料编辑对话框 -->
|
|
|
<el-dialog v-model="materialDialog.visible" :title="materialDialog.title" 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="materialFlag">
|
|
|
<el-radio-group v-model="materialForm.materialFlag">
|
|
|
<el-radio v-for="dict in material_flag" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
|
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12" v-if="materialForm.materialFlag === '1'">
|
|
|
<el-form-item label="物料名称" prop="materialName">
|
|
|
<el-input v-model="materialForm.materialName" placeholder="点击右侧图标检索">
|
|
|
<template #suffix>
|
|
|
<el-icon style="cursor: pointer" @click="openSaleMaterialSelect"><Search /></el-icon>
|
|
|
</template>
|
|
|
</el-input>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12" v-if="materialForm.materialFlag === '1'">
|
|
|
<el-form-item label="物料编号"><el-input v-model="materialForm.materialCode" disabled /></el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12" v-if="materialForm.materialFlag === '1'">
|
|
|
<el-form-item label="客户名称"><el-input v-model="materialForm.customerName" disabled /></el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="产品名称" prop="productName">
|
|
|
<el-input v-model="materialForm.productName" placeholder="产品名称" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<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="数量" prop="amount">
|
|
|
<el-input-number v-model="materialForm.amount" placeholder="数量" style="width: 100%" :precision="2" @change="calculateSubtotal" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="物料单位" prop="unitId">
|
|
|
<el-select v-model="materialForm.unitId" placeholder="请选择单位" style="width: 100%">
|
|
|
<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="taxRate">
|
|
|
<el-select v-model="materialForm.taxRate" placeholder="税率" style="width: 100%" filterable allow-create @change="calculateBeforePrice">
|
|
|
<el-option label="0" :value="0" /><el-option label="6" :value="6" /><el-option label="9" :value="9" /><el-option label="13" :value="13" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="未税单价"><el-input-number v-model="materialForm.beforePrice" style="width: 100%" :precision="2" disabled :controls="false" /></el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="含税单价" prop="includingPrice">
|
|
|
<el-input-number v-model="materialForm.includingPrice" placeholder="含税单价" style="width: 100%" :precision="2" @change="calculateBeforePrice" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="小计"><el-input-number v-model="materialForm.subtotal" style="width: 100%" :precision="2" readonly disabled :controls="false" /></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>
|
|
|
<el-button @click="cancelMaterial">取 消</el-button>
|
|
|
<el-button type="primary" @click="submitMaterialForm">确 定</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<!-- 合同付款方式编辑对话框 -->
|
|
|
<el-dialog v-model="paymentMethodDialog.visible" :title="paymentMethodDialog.title" width="500px" append-to-body>
|
|
|
<el-form ref="paymentMethodFormRef" :model="paymentMethodForm" :rules="paymentMethodRules" label-width="120px">
|
|
|
<el-form-item label="序号" prop="sortOrder">
|
|
|
<el-input-number v-model="paymentMethodForm.sortOrder" :min="1" readonly :controls="false" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="付款节点" prop="paymentStageId">
|
|
|
<el-select v-model="paymentMethodForm.paymentStageId" placeholder="请选择付款节点" style="width: 100%" filterable @change="onPaymentStageChange">
|
|
|
<el-option v-for="item in paymentStageList" :key="item.paymentStageId" :label="item.paymentMethod" :value="item.paymentStageId" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="支付期限(天)" prop="paymentDeadline">
|
|
|
<el-input-number v-model="paymentMethodForm.paymentDeadline" :min="0" style="width: 100%" @change="refreshPaymentDescription" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="支付比例(%)" prop="paymentPercentage">
|
|
|
<el-input-number v-model="paymentMethodForm.paymentPercentage" :min="0" :max="100" style="width: 100%" @change="refreshPaymentDescription" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="发票比例(%)" prop="invoicePercentage">
|
|
|
<el-input-number v-model="paymentMethodForm.invoicePercentage" :min="0" :max="100" style="width: 100%" @change="refreshPaymentDescription" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="付款条款" prop="paymentDescription">
|
|
|
<el-input v-model="paymentMethodForm.paymentDescription" type="textarea" :rows="5" placeholder="选择付款节点后自动带出,可修改" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="cancelPaymentMethod">取 消</el-button>
|
|
|
<el-button type="primary" @click="submitPaymentMethodForm">确 定</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<SaleMaterialSelect ref="saleMaterialSelectRef" :multiple="false" @confirm-call-back="saleMaterialSelectCallBack" />
|
|
|
|
|
|
<!-- 选择合同弹框:列表 + 合同详情 -->
|
|
|
<el-dialog v-model="contractDialog.visible" title="选择合同" width="960px" append-to-body>
|
|
|
<el-form :model="contractQueryParams" :inline="true" label-width="90px">
|
|
|
<el-form-item label="合同编号">
|
|
|
<el-input
|
|
|
v-model="contractQueryParams.contractCode"
|
|
|
placeholder="请输入合同编号"
|
|
|
clearable
|
|
|
style="width: 160px"
|
|
|
@keyup.enter="getContractList"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="合同名称">
|
|
|
<el-input
|
|
|
v-model="contractQueryParams.contractName"
|
|
|
placeholder="请输入合同名称"
|
|
|
clearable
|
|
|
style="width: 180px"
|
|
|
@keyup.enter="getContractList"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" icon="Search" @click="getContractList">搜索</el-button>
|
|
|
<el-button icon="Refresh" @click="resetContractQuery">重置</el-button>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<el-row :gutter="16">
|
|
|
<el-col :span="14">
|
|
|
<el-table
|
|
|
v-loading="contractLoading"
|
|
|
:data="contractList"
|
|
|
border
|
|
|
max-height="360"
|
|
|
highlight-current-row
|
|
|
@current-change="handleContractRowChange"
|
|
|
>
|
|
|
<el-table-column type="index" label="序号" width="55" />
|
|
|
<el-table-column label="合同编号" align="center" prop="contractCode" min-width="120" />
|
|
|
<el-table-column label="合同名称" align="center" prop="contractName" min-width="160" show-overflow-tooltip />
|
|
|
<el-table-column label="合同总价" align="center" prop="totalPrice" width="100" />
|
|
|
</el-table>
|
|
|
<pagination
|
|
|
v-show="contractTotal > 0"
|
|
|
:total="contractTotal"
|
|
|
v-model:page="contractQueryParams.pageNum"
|
|
|
v-model:limit="contractQueryParams.pageSize"
|
|
|
@pagination="getContractList"
|
|
|
/>
|
|
|
</el-col>
|
|
|
<el-col :span="10">
|
|
|
<div class="contract-detail-panel">
|
|
|
<div class="panel-title">合同详情</div>
|
|
|
<template v-if="contractDetailPreview">
|
|
|
<el-descriptions :column="1" border size="small">
|
|
|
<el-descriptions-item label="合同编号">{{ contractDetailPreview.contractCode }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="合同名称">{{ contractDetailPreview.contractName }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="合同总价">{{ contractDetailPreview.totalPrice }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="客户">{{ (contractDetailPreview as any).oneCustomerName || (contractDetailPreview as any).twoCustomerName || '-' }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="签订日期">{{ contractDetailPreview.contractDate || '-' }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
</template>
|
|
|
<div v-else class="panel-placeholder">点击左侧列表行可查看合同详情</div>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<template #footer>
|
|
|
<el-button @click="contractDialog.visible = false">取 消</el-button>
|
|
|
<el-button type="primary" @click="submitContractSelect">确 定</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<!-- 审批记录 -->
|
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
|
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="ContractChangeApply" lang="ts">
|
|
|
import { Search } from '@element-plus/icons-vue';
|
|
|
import { listContractInfo, getContractInfo } from '@/api/oa/erp/contractInfo';
|
|
|
import type { ContractInfoVO, ContractInfoQuery } from '@/api/oa/erp/contractInfo/types';
|
|
|
import { saveContractChange, getContractChangeDetail } from '@/api/oa/erp/contractChange';
|
|
|
import type { ContractChangeSaveForm } from '@/api/oa/erp/contractChange/types';
|
|
|
import type { ContractMaterialForm } from '@/api/oa/erp/contractMaterial/types';
|
|
|
import type { ContractPaymentMethodForm } from '@/api/oa/erp/contractPaymentMethod/types';
|
|
|
import { getBaseUnitInfoList } from '@/api/oa/base/unitInfo';
|
|
|
import { listPaymentStage } from '@/api/oa/base/paymentStage';
|
|
|
import type { PaymentStageVO } from '@/api/oa/base/paymentStage/types';
|
|
|
import { getCrmCustomerInfoList } from '@/api/oa/crm/customerInfo';
|
|
|
import { getCrmPaymentAccountList } from '@/api/oa/crm/paymentAccount';
|
|
|
import { getUserList } from '@/api/system/user';
|
|
|
import { getBasePrintTemplateList } from '@/api/oa/base/printTemplate';
|
|
|
import { startWorkFlow } from '@/api/workflow/task';
|
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
|
import FileUpload from '@/components/FileUpload/index.vue';
|
|
|
import SaleMaterialSelect from '@/components/SaleMaterialSelect/index.vue';
|
|
|
import { FlowCodeEnum } from '@/enums/OAEnum';
|
|
|
|
|
|
const route = useRoute();
|
|
|
const router = useRouter();
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const {
|
|
|
contract_change_type,
|
|
|
contract_category,
|
|
|
business_direction,
|
|
|
contract_flag,
|
|
|
contract_template_flag,
|
|
|
material_flag
|
|
|
} = toRefs<any>(
|
|
|
proxy?.useDict('contract_change_type', 'contract_category', 'business_direction', 'contract_flag', 'contract_template_flag', 'material_flag')
|
|
|
);
|
|
|
|
|
|
const paymentMethodOptions = ['电汇', '银行承兑(6个月内)', '电汇/银行承兑(6个月内)', '商业承兑'];
|
|
|
|
|
|
const formRef = ref<ElFormInstance>();
|
|
|
const buttonLoading = ref(false);
|
|
|
const routeParams = ref<Record<string, any>>({ ...(route.query as Record<string, any>) });
|
|
|
|
|
|
// 审批相关
|
|
|
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
|
|
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
|
|
const taskVariables = ref<Record<string, any>>({});
|
|
|
|
|
|
// 下拉与选项
|
|
|
const unitInfoList = ref<any[]>([]);
|
|
|
const paymentStageList = ref<PaymentStageVO[]>([]);
|
|
|
const customerInfoList = ref<any[]>([]);
|
|
|
const userInfoList = ref<any[]>([]);
|
|
|
const paymentAccountList = ref<any[]>([]);
|
|
|
const printTemplateList = ref<any[]>([]);
|
|
|
|
|
|
// 变更后物料/付款方式列表(仅内容变更时使用)
|
|
|
const changeMaterialList = computed(() => form.value.changeMaterialList || []);
|
|
|
const changePaymentMethodList = computed(() => form.value.changePaymentMethodList || []);
|
|
|
|
|
|
// 变更后合同附件(绑定到 changeInfo.ossId)
|
|
|
const changeInfoOssId = computed({
|
|
|
get() {
|
|
|
const v = form.value.changeInfo?.ossId;
|
|
|
return v === undefined || v === null ? '' : String(v);
|
|
|
},
|
|
|
set(val: string) {
|
|
|
if (form.value.changeInfo) form.value.changeInfo.ossId = val || undefined;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 合同物料弹框
|
|
|
const materialDialog = reactive({ visible: false, title: '' });
|
|
|
const materialFormRef = ref<ElFormInstance>();
|
|
|
const initMaterialFormData: ContractMaterialForm & { materialCode?: string; customerName?: string; saleMaterialName?: string } = {
|
|
|
contractMaterialId: undefined,
|
|
|
materialFlag: '2',
|
|
|
contractId: undefined,
|
|
|
productName: undefined,
|
|
|
specificationDescription: undefined,
|
|
|
materialId: undefined,
|
|
|
relationMaterialId: undefined,
|
|
|
materialCode: undefined,
|
|
|
materialName: undefined,
|
|
|
saleMaterialName: undefined,
|
|
|
customerName: undefined,
|
|
|
amount: undefined,
|
|
|
unitId: undefined,
|
|
|
beforePrice: undefined,
|
|
|
taxRate: undefined,
|
|
|
includingPrice: undefined,
|
|
|
subtotal: undefined,
|
|
|
remark: undefined,
|
|
|
activeFlag: '1'
|
|
|
};
|
|
|
const materialForm = ref<ContractMaterialForm & { materialCode?: string; customerName?: string; saleMaterialName?: string }>({ ...initMaterialFormData });
|
|
|
const materialRules = {
|
|
|
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
|
|
|
amount: [{ required: true, message: '数量不能为空', trigger: 'blur' }],
|
|
|
taxRate: [{ required: true, message: '税率不能为空', trigger: 'blur' }]
|
|
|
};
|
|
|
|
|
|
// 合同付款方式弹框
|
|
|
const paymentMethodDialog = reactive({ visible: false, title: '' });
|
|
|
const editingPaymentMethodIndex = ref<number | null>(null);
|
|
|
const paymentMethodFormRef = ref<ElFormInstance>();
|
|
|
const initPaymentMethodFormData: ContractPaymentMethodForm = {
|
|
|
paymentMethodId: undefined,
|
|
|
contractId: undefined,
|
|
|
sortOrder: undefined,
|
|
|
paymentStageId: undefined,
|
|
|
paymentDeadline: undefined,
|
|
|
paymentPercentage: undefined,
|
|
|
invoicePercentage: 0,
|
|
|
paymentAmount: undefined,
|
|
|
paymentDescription: undefined,
|
|
|
remark: undefined,
|
|
|
activeFlag: '1'
|
|
|
};
|
|
|
const paymentMethodForm = ref<ContractPaymentMethodForm>({ ...initPaymentMethodFormData });
|
|
|
const paymentMethodRules = {
|
|
|
paymentStageId: [{ required: true, message: '请选择付款节点', trigger: 'change' }],
|
|
|
paymentDeadline: [{ required: true, message: '支付期限(天)不能为空', trigger: 'blur' }],
|
|
|
paymentPercentage: [{ required: true, message: '支付比例不能为空', trigger: 'blur' }],
|
|
|
invoicePercentage: [{ required: true, message: '发票比例不能为空', trigger: 'blur' }]
|
|
|
};
|
|
|
const PAYMENT_TEMPLATE_PLACEHOLDERS: Record<string, string> = {
|
|
|
'{序号}': 'sortOrder',
|
|
|
'{支付期限}': 'paymentDeadline',
|
|
|
'{支付比例}': 'paymentPercentage',
|
|
|
'{发票比例}': 'invoicePercentage'
|
|
|
};
|
|
|
const replacePaymentTemplate = (template: string | undefined, data: Record<string, any>): string => {
|
|
|
if (template == null || template === '') return '';
|
|
|
let result = template;
|
|
|
for (const [placeholder, fieldKey] of Object.entries(PAYMENT_TEMPLATE_PLACEHOLDERS)) {
|
|
|
const value = data[fieldKey];
|
|
|
result = result.replaceAll(placeholder, value !== undefined && value !== null ? String(value) : '');
|
|
|
}
|
|
|
return result;
|
|
|
};
|
|
|
const removeSegmentWhenInvoiceZero = (text: string, invoicePercentage: any): string => {
|
|
|
if (text == null || text === '') return text;
|
|
|
const isZero = invoicePercentage === 0 || invoicePercentage === '0' || Number(invoicePercentage) === 0;
|
|
|
if (!isZero) return text;
|
|
|
const startChar = '且';
|
|
|
const endChar = '后';
|
|
|
const idxStart = text.indexOf(startChar);
|
|
|
if (idxStart === -1) return text;
|
|
|
const idxEnd = text.indexOf(endChar, idxStart);
|
|
|
if (idxEnd === -1) return text;
|
|
|
return text.slice(0, idxStart) + text.slice(idxEnd + endChar.length);
|
|
|
};
|
|
|
const applyPaymentDescription = (template: string | undefined, data: Record<string, any>): string => {
|
|
|
const replaced = replacePaymentTemplate(template, data);
|
|
|
return removeSegmentWhenInvoiceZero(replaced, data.invoicePercentage);
|
|
|
};
|
|
|
const paymentMethodTemplateRaw = ref<string | null>(null);
|
|
|
const refreshPaymentDescription = () => {
|
|
|
if (paymentMethodTemplateRaw.value) {
|
|
|
paymentMethodForm.value.paymentDescription = applyPaymentDescription(
|
|
|
paymentMethodTemplateRaw.value,
|
|
|
paymentMethodForm.value as Record<string, any>
|
|
|
);
|
|
|
}
|
|
|
};
|
|
|
const onPaymentStageChange = (paymentStageId: string | number | undefined) => {
|
|
|
if (paymentStageId == null) return;
|
|
|
const stage = paymentStageList.value.find((s) => s.paymentStageId === paymentStageId || String(s.paymentStageId) === String(paymentStageId));
|
|
|
if (stage?.paymentTemplate) {
|
|
|
paymentMethodTemplateRaw.value = stage.paymentTemplate;
|
|
|
paymentMethodForm.value.paymentDescription = applyPaymentDescription(
|
|
|
stage.paymentTemplate,
|
|
|
paymentMethodForm.value as Record<string, any>
|
|
|
);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const saleMaterialSelectRef = ref<InstanceType<typeof SaleMaterialSelect>>();
|
|
|
|
|
|
// 合同选择弹框
|
|
|
const contractDialog = reactive({ visible: false });
|
|
|
const contractLoading = ref(false);
|
|
|
const contractList = ref<ContractInfoVO[]>([]);
|
|
|
const contractTotal = ref(0);
|
|
|
const contractQueryParams = ref<ContractInfoQuery>({
|
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
|
contractStatus: '3',
|
|
|
activeFlag: '1'
|
|
|
});
|
|
|
const selectedContract = ref<ContractInfoVO | null>(null);
|
|
|
const contractDetailPreview = ref<ContractInfoVO | null>(null);
|
|
|
const selectedContractName = ref<string>('');
|
|
|
|
|
|
const isEdit = computed(() => !!(routeParams.value.id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')));
|
|
|
const isFormDisabled = computed(() => routeParams.value.type === 'view' || routeParams.value.type === 'approval');
|
|
|
|
|
|
const form = ref<
|
|
|
ContractChangeSaveForm & {
|
|
|
changeInfo?: any;
|
|
|
changeMaterialList?: any[];
|
|
|
changePaymentMethodList?: any[];
|
|
|
flowStatus?: string;
|
|
|
changeCode?: string;
|
|
|
}
|
|
|
>({
|
|
|
contractChangeId: undefined,
|
|
|
contractId: undefined as any,
|
|
|
changeType: '1',
|
|
|
changeReason: '',
|
|
|
applyTime: undefined,
|
|
|
remark: undefined,
|
|
|
changeStatus: '1',
|
|
|
flowStatus: 'draft',
|
|
|
activeFlag: '1',
|
|
|
contractCode: undefined,
|
|
|
contractName: undefined,
|
|
|
originalCustomerName: undefined,
|
|
|
originalContractAmount: undefined,
|
|
|
changeContractCode: undefined,
|
|
|
changeContractName: undefined,
|
|
|
customerName: undefined,
|
|
|
changeContractAmount: undefined,
|
|
|
changeInfo: undefined,
|
|
|
changeMaterialList: undefined,
|
|
|
changePaymentMethodList: undefined,
|
|
|
changeCode: undefined
|
|
|
});
|
|
|
|
|
|
const rules = {
|
|
|
changeType: [{ required: true, message: '请选择变更类型', trigger: 'change' }],
|
|
|
contractId: [{ required: true, message: '请选择合同', trigger: 'change' }],
|
|
|
changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }]
|
|
|
};
|
|
|
|
|
|
function goBack() {
|
|
|
proxy?.$tab.closePage(route);
|
|
|
router.push({ path: '/contract/contractChange' });
|
|
|
}
|
|
|
|
|
|
/** 打开合同选择弹框 */
|
|
|
function openContractSelect() {
|
|
|
if (isFormDisabled.value) return;
|
|
|
contractDialog.visible = true;
|
|
|
selectedContract.value = null;
|
|
|
contractDetailPreview.value = null;
|
|
|
contractQueryParams.value.pageNum = 1;
|
|
|
getContractList();
|
|
|
}
|
|
|
|
|
|
/** 查询合同列表(仅已激活) */
|
|
|
async function getContractList() {
|
|
|
contractLoading.value = true;
|
|
|
try {
|
|
|
const res = await listContractInfo(contractQueryParams.value);
|
|
|
contractList.value = res.rows || [];
|
|
|
contractTotal.value = res.total || 0;
|
|
|
} finally {
|
|
|
contractLoading.value = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function resetContractQuery() {
|
|
|
contractQueryParams.value.contractCode = undefined;
|
|
|
contractQueryParams.value.contractName = undefined;
|
|
|
contractQueryParams.value.pageNum = 1;
|
|
|
getContractList();
|
|
|
}
|
|
|
|
|
|
/** 表格当前行变化:选中合同并加载详情 */
|
|
|
async function handleContractRowChange(row: ContractInfoVO | null) {
|
|
|
selectedContract.value = row || null;
|
|
|
contractDetailPreview.value = null;
|
|
|
if (row?.contractId) {
|
|
|
try {
|
|
|
const res = await getContractInfo(row.contractId);
|
|
|
contractDetailPreview.value = res.data || null;
|
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** 确定选择合同 */
|
|
|
function submitContractSelect() {
|
|
|
if (selectedContract.value) {
|
|
|
const c = selectedContract.value;
|
|
|
form.value.contractId = c.contractId as any;
|
|
|
selectedContractName.value = c.contractName || '';
|
|
|
contractDialog.visible = false;
|
|
|
onContractChange(c.contractId);
|
|
|
} else {
|
|
|
proxy?.$modal.msgWarning('请先选择一条合同');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function onContractChange(contractId: string | number) {
|
|
|
if (!contractId || form.value.changeType !== '1') return;
|
|
|
const res = await getContractInfo(contractId);
|
|
|
const c = res.data;
|
|
|
if (!c) return;
|
|
|
form.value.contractCode = c.contractCode;
|
|
|
form.value.contractName = c.contractName;
|
|
|
const cAny = c as any;
|
|
|
form.value.originalCustomerName = cAny.oneCustomerName || cAny.twoCustomerName;
|
|
|
form.value.originalContractAmount = c.totalPrice as any;
|
|
|
form.value.changeContractCode = c.contractCode;
|
|
|
form.value.changeContractName = c.contractName;
|
|
|
form.value.customerName = cAny.oneCustomerName || cAny.twoCustomerName;
|
|
|
form.value.changeContractAmount = c.totalPrice as any;
|
|
|
form.value.changeInfo = c ? mapContractToChangeInfo(c) : undefined;
|
|
|
form.value.changeMaterialList = (cAny.contractMaterialList || []).map((m: any) => ({
|
|
|
...m,
|
|
|
changeMaterialId: undefined,
|
|
|
contractChangeId: undefined
|
|
|
}));
|
|
|
form.value.changePaymentMethodList = (cAny.contractPaymentMethodList || []).map((p: any) => ({
|
|
|
...p,
|
|
|
changePaymentId: undefined,
|
|
|
contractChangeId: undefined
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
function mapContractToChangeInfo(c: any) {
|
|
|
const info: any = {};
|
|
|
const keys = [
|
|
|
'contractFlag',
|
|
|
'customerContractCode',
|
|
|
'contractCode',
|
|
|
'contractName',
|
|
|
'contractCategory',
|
|
|
'contractType',
|
|
|
'businessDirection',
|
|
|
'contractDeptId',
|
|
|
'contractDate',
|
|
|
'totalPrice',
|
|
|
'oneCustomerId',
|
|
|
'oneRepresent',
|
|
|
'oneDate',
|
|
|
'twoCustomerId',
|
|
|
'twoRepresent',
|
|
|
'twoDate',
|
|
|
'contractManagerId',
|
|
|
'templateId',
|
|
|
'ossId',
|
|
|
'paymentAccountId',
|
|
|
'paymentMethod',
|
|
|
'signatureAppendix',
|
|
|
'warrantyPeriod',
|
|
|
'internalContractCode',
|
|
|
'externalContractCode',
|
|
|
'orderContractCode',
|
|
|
'projectContractCode',
|
|
|
'deliveryStart',
|
|
|
'warrantyPeriodDescription',
|
|
|
'deliveryLocation',
|
|
|
'shipMethod',
|
|
|
'taxRate',
|
|
|
'signingPlace',
|
|
|
'materialRemark',
|
|
|
'contractTemplateFlag',
|
|
|
'capitalizedAmount',
|
|
|
'remark'
|
|
|
];
|
|
|
keys.forEach((k) => {
|
|
|
if (c[k] !== undefined) info[k] = c[k];
|
|
|
});
|
|
|
info.changeInfoId = undefined;
|
|
|
info.contractChangeId = undefined;
|
|
|
if (info.contractTemplateFlag == null) info.contractTemplateFlag = '2';
|
|
|
return info;
|
|
|
}
|
|
|
|
|
|
async function loadDetail() {
|
|
|
const id = routeParams.value.id;
|
|
|
if (!id) return;
|
|
|
const res = await getContractChangeDetail(id);
|
|
|
const d = res.data;
|
|
|
if (!d?.main) return;
|
|
|
const m = d.main;
|
|
|
form.value.contractChangeId = m.contractChangeId;
|
|
|
form.value.contractId = m.contractId;
|
|
|
form.value.changeType = m.changeType;
|
|
|
form.value.changeReason = m.changeReason;
|
|
|
form.value.changeCode = m.changeCode;
|
|
|
form.value.applyTime = m.applyTime;
|
|
|
form.value.remark = m.remark;
|
|
|
form.value.changeStatus = m.changeStatus;
|
|
|
form.value.flowStatus = m.flowStatus ?? (m.changeStatus === '1' ? 'draft' : 'waiting');
|
|
|
form.value.contractCode = m.contractCode;
|
|
|
form.value.contractName = m.contractName;
|
|
|
form.value.originalCustomerName = m.originalCustomerName;
|
|
|
form.value.originalContractAmount = m.originalContractAmount;
|
|
|
form.value.changeContractCode = m.changeContractCode;
|
|
|
form.value.changeContractName = m.changeContractName;
|
|
|
form.value.customerName = m.customerName;
|
|
|
form.value.changeContractAmount = m.changeContractAmount;
|
|
|
form.value.changeInfo = d.changeInfo ? { ...d.changeInfo } : undefined;
|
|
|
form.value.changeMaterialList = (d.changeMaterialList || []).map((x: any) => ({ ...x }));
|
|
|
form.value.changePaymentMethodList = (d.changePaymentMethodList || []).map((x: any) => ({ ...x }));
|
|
|
selectedContractName.value = m.contractName || '';
|
|
|
}
|
|
|
|
|
|
function loadSelectOptions() {
|
|
|
getBaseUnitInfoList(null).then((res) => (unitInfoList.value = res.data || []));
|
|
|
listPaymentStage({ pageNum: 1, pageSize: 500 }).then((res) => (paymentStageList.value = res.rows || []));
|
|
|
getCrmCustomerInfoList(null).then((res) => (customerInfoList.value = res.data || []));
|
|
|
getUserList({ pageNum: 1, pageSize: 1000 }).then((res) => (userInfoList.value = res.data || []));
|
|
|
getCrmPaymentAccountList({}).then((res) => (paymentAccountList.value = res.data || []));
|
|
|
getBasePrintTemplateList({ templateType: '1' }).then((res) => (printTemplateList.value = res.data || []));
|
|
|
}
|
|
|
|
|
|
// ----- 变更后物料:增删改 -----
|
|
|
function handleAddMaterial() {
|
|
|
if (!form.value.changeInfo) return;
|
|
|
resetMaterialForm();
|
|
|
materialForm.value.contractId = form.value.contractId as any;
|
|
|
materialDialog.visible = true;
|
|
|
materialDialog.title = '新增合同物料';
|
|
|
}
|
|
|
function handleEditMaterial(row: any) {
|
|
|
resetMaterialForm();
|
|
|
materialForm.value = { ...row };
|
|
|
materialDialog.visible = true;
|
|
|
materialDialog.title = '编辑合同物料';
|
|
|
}
|
|
|
async function handleDeleteMaterial(row: any) {
|
|
|
await proxy?.$modal.confirm('是否确认删除该合同物料?');
|
|
|
const list = form.value.changeMaterialList || [];
|
|
|
const idx = list.findIndex((item: any) => (item.changeMaterialId != null && item.changeMaterialId === row.changeMaterialId) || (item.contractMaterialId != null && item.contractMaterialId === row.contractMaterialId) || item === row);
|
|
|
if (idx !== -1) {
|
|
|
list.splice(idx, 1);
|
|
|
calculateTotalPrice();
|
|
|
}
|
|
|
proxy?.$modal.msgSuccess('删除成功');
|
|
|
}
|
|
|
function resetMaterialForm() {
|
|
|
materialForm.value = { ...initMaterialFormData };
|
|
|
materialFormRef.value?.resetFields();
|
|
|
}
|
|
|
function cancelMaterial() {
|
|
|
resetMaterialForm();
|
|
|
materialDialog.visible = false;
|
|
|
}
|
|
|
function calculateBeforePrice() {
|
|
|
const tax = Number(materialForm.value.taxRate);
|
|
|
const including = Number(materialForm.value.includingPrice);
|
|
|
if (!isNaN(tax) && !isNaN(including)) {
|
|
|
const divisor = 1 + tax / 100;
|
|
|
if (divisor > 0) materialForm.value.beforePrice = Number((including / divisor).toFixed(2));
|
|
|
}
|
|
|
calculateSubtotal();
|
|
|
}
|
|
|
function calculateSubtotal() {
|
|
|
const amount = Number(materialForm.value.amount);
|
|
|
const including = Number(materialForm.value.includingPrice);
|
|
|
if (!isNaN(amount) && !isNaN(including)) materialForm.value.subtotal = Number((amount * including).toFixed(2));
|
|
|
calculateTotalPrice();
|
|
|
}
|
|
|
function calculateTotalPrice() {
|
|
|
const list = form.value.changeMaterialList || [];
|
|
|
const total = list.reduce((sum: number, m: any) => sum + (Number(m.subtotal) || 0), 0);
|
|
|
if (form.value.changeInfo) form.value.changeInfo.totalPrice = Number(total.toFixed(2));
|
|
|
}
|
|
|
function submitMaterialForm() {
|
|
|
materialFormRef.value?.validate((valid: boolean) => {
|
|
|
if (!valid) return;
|
|
|
if (!form.value.changeMaterialList) form.value.changeMaterialList = [];
|
|
|
const list = form.value.changeMaterialList;
|
|
|
const unitInfo = unitInfoList.value.find((item: any) => item.unitId === materialForm.value.unitId);
|
|
|
const unitName = unitInfo ? unitInfo.unitName : '';
|
|
|
const data = { ...materialForm.value, unitName };
|
|
|
const id = materialForm.value.contractMaterialId ?? (materialForm.value as any).changeMaterialId;
|
|
|
const idx = id != null ? list.findIndex((item: any) => item.contractMaterialId === id || item.changeMaterialId === id) : -1;
|
|
|
if (idx !== -1) {
|
|
|
list[idx] = data;
|
|
|
} else {
|
|
|
list.push({ ...data, contractMaterialId: undefined, changeMaterialId: Date.now() });
|
|
|
}
|
|
|
calculateTotalPrice();
|
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
|
materialDialog.visible = false;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// ----- 变更后付款方式:增删改 -----
|
|
|
function handleAddPaymentMethod() {
|
|
|
resetPaymentMethodForm();
|
|
|
editingPaymentMethodIndex.value = null;
|
|
|
const list = form.value.changePaymentMethodList || [];
|
|
|
paymentMethodForm.value.sortOrder = list.length + 1;
|
|
|
paymentMethodDialog.visible = true;
|
|
|
paymentMethodDialog.title = '新增付款方式';
|
|
|
}
|
|
|
function handleEditPaymentMethod(row: any, rowIndex: number) {
|
|
|
resetPaymentMethodForm();
|
|
|
editingPaymentMethodIndex.value = rowIndex;
|
|
|
paymentMethodForm.value = { ...row };
|
|
|
const stage = paymentStageList.value.find((s) => s.paymentStageId === row.paymentStageId || String(s.paymentStageId) === String(row.paymentStageId));
|
|
|
if (stage?.paymentTemplate) paymentMethodTemplateRaw.value = stage.paymentTemplate;
|
|
|
paymentMethodDialog.visible = true;
|
|
|
paymentMethodDialog.title = '编辑付款方式';
|
|
|
}
|
|
|
async function handleDeletePaymentMethod(rowIndex: number) {
|
|
|
await proxy?.$modal.confirm('是否确认删除该付款方式?');
|
|
|
const list = form.value.changePaymentMethodList || [];
|
|
|
if (rowIndex >= 0 && rowIndex < list.length) {
|
|
|
list.splice(rowIndex, 1);
|
|
|
list.forEach((item: any, i: number) => (item.sortOrder = i + 1));
|
|
|
}
|
|
|
proxy?.$modal.msgSuccess('删除成功');
|
|
|
}
|
|
|
function resetPaymentMethodForm() {
|
|
|
paymentMethodForm.value = { ...initPaymentMethodFormData };
|
|
|
paymentMethodTemplateRaw.value = null;
|
|
|
paymentMethodFormRef.value?.resetFields();
|
|
|
}
|
|
|
function cancelPaymentMethod() {
|
|
|
resetPaymentMethodForm();
|
|
|
editingPaymentMethodIndex.value = null;
|
|
|
paymentMethodDialog.visible = false;
|
|
|
}
|
|
|
function submitPaymentMethodForm() {
|
|
|
paymentMethodFormRef.value?.validate((valid: boolean) => {
|
|
|
if (!valid) return;
|
|
|
if (!form.value.changePaymentMethodList) form.value.changePaymentMethodList = [];
|
|
|
const list = form.value.changePaymentMethodList;
|
|
|
const data = { ...paymentMethodForm.value };
|
|
|
data.paymentDescription = applyPaymentDescription(data.paymentDescription || '', data as Record<string, any>);
|
|
|
const editIndex = editingPaymentMethodIndex.value;
|
|
|
if (editIndex !== null && editIndex >= 0 && editIndex < list.length) {
|
|
|
list[editIndex] = data;
|
|
|
} else {
|
|
|
list.push({ ...data, paymentMethodId: undefined, changePaymentId: undefined });
|
|
|
}
|
|
|
editingPaymentMethodIndex.value = null;
|
|
|
list.forEach((item: any, i: number) => {
|
|
|
item.sortOrder = i + 1;
|
|
|
item.paymentDescription = applyPaymentDescription(item.paymentDescription || '', item);
|
|
|
});
|
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
|
paymentMethodDialog.visible = false;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function openSaleMaterialSelect() {
|
|
|
saleMaterialSelectRef.value?.open();
|
|
|
}
|
|
|
function saleMaterialSelectCallBack(data: any) {
|
|
|
const arr = Array.isArray(data) ? data : [data];
|
|
|
if (arr.length > 0) {
|
|
|
const m = arr[0];
|
|
|
materialForm.value.materialId = m.materialId;
|
|
|
materialForm.value.materialCode = m.materialCode;
|
|
|
materialForm.value.materialName = m.materialName;
|
|
|
materialForm.value.productName = m.saleMaterialName;
|
|
|
materialForm.value.saleMaterialName = m.saleMaterialName;
|
|
|
materialForm.value.relationMaterialId = m.relationMaterialId;
|
|
|
materialForm.value.customerName = m.customerName;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function buildPayload(changeStatus: '1' | '2'): ContractChangeSaveForm {
|
|
|
const f = form.value;
|
|
|
const payload: ContractChangeSaveForm = {
|
|
|
contractChangeId: f.contractChangeId,
|
|
|
contractId: f.contractId,
|
|
|
changeType: f.changeType,
|
|
|
changeReason: f.changeReason,
|
|
|
applyTime: f.applyTime || new Date().toISOString().slice(0, 19).replace('T', ' '),
|
|
|
remark: f.remark,
|
|
|
changeStatus,
|
|
|
activeFlag: '1',
|
|
|
contractCode: f.contractCode,
|
|
|
contractName: f.contractName,
|
|
|
originalCustomerName: f.originalCustomerName,
|
|
|
originalContractAmount: f.originalContractAmount,
|
|
|
changeContractCode: f.changeContractCode,
|
|
|
changeContractName: f.changeContractName,
|
|
|
customerName: f.customerName,
|
|
|
changeContractAmount: f.changeContractAmount
|
|
|
};
|
|
|
if (f.changeType === '1') {
|
|
|
payload.changeInfo = f.changeInfo;
|
|
|
payload.changeMaterialList = f.changeMaterialList;
|
|
|
payload.changePaymentMethodList = f.changePaymentMethodList;
|
|
|
}
|
|
|
return payload;
|
|
|
}
|
|
|
|
|
|
/** 审批栏:提交(暂存 / 提交审批) */
|
|
|
function submitForm(status: string, _mode: boolean) {
|
|
|
formRef.value?.validate((valid: boolean) => {
|
|
|
if (!valid) return;
|
|
|
if (form.value.changeType === '1' && !form.value.changeInfo) {
|
|
|
proxy?.$modal.msgError('内容变更请先选择合同,以加载变更后合同信息');
|
|
|
return;
|
|
|
}
|
|
|
buttonLoading.value = true;
|
|
|
if (status !== 'draft') {
|
|
|
// 提交审批:先保存再发起流程
|
|
|
const payload = buildPayload('2');
|
|
|
saveContractChange(payload)
|
|
|
.then((res) => {
|
|
|
const id = res.data;
|
|
|
if (id) form.value.contractChangeId = id;
|
|
|
form.value.changeStatus = '2';
|
|
|
form.value.flowStatus = 'waiting';
|
|
|
const variables = {
|
|
|
contractChangeId: id,
|
|
|
changeCode: form.value.changeCode || '',
|
|
|
contractName: form.value.contractName,
|
|
|
changeReason: form.value.changeReason
|
|
|
};
|
|
|
taskVariables.value = variables;
|
|
|
return startWorkFlow({
|
|
|
businessId: String(id),
|
|
|
flowCode: FlowCodeEnum.CONTRACT_CHANGE_CODE,
|
|
|
variables,
|
|
|
bizExt: { businessTitle: '合同变更', businessCode: form.value.changeCode || '合同变更' }
|
|
|
});
|
|
|
})
|
|
|
.then((resp) => {
|
|
|
if (resp?.data?.taskId && submitVerifyRef.value) {
|
|
|
return submitVerifyRef.value.openDialog(resp.data.taskId);
|
|
|
}
|
|
|
})
|
|
|
.then(() => {
|
|
|
proxy?.$modal.msgSuccess('提交成功');
|
|
|
goBack();
|
|
|
})
|
|
|
.finally(() => {
|
|
|
buttonLoading.value = false;
|
|
|
});
|
|
|
} else {
|
|
|
// 暂存
|
|
|
const payload = buildPayload('1');
|
|
|
saveContractChange(payload)
|
|
|
.then((res) => {
|
|
|
const id = res.data;
|
|
|
if (id) form.value.contractChangeId = id;
|
|
|
form.value.changeStatus = '1';
|
|
|
form.value.flowStatus = 'draft';
|
|
|
proxy?.$modal.msgSuccess('暂存成功');
|
|
|
goBack();
|
|
|
})
|
|
|
.finally(() => {
|
|
|
buttonLoading.value = false;
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function submitCallback() {
|
|
|
goBack();
|
|
|
}
|
|
|
|
|
|
function handleApprovalRecord() {
|
|
|
approvalRecordRef.value?.init(form.value.contractChangeId);
|
|
|
}
|
|
|
|
|
|
async function approvalVerifyOpen() {
|
|
|
if (routeParams.value.taskId) await submitVerifyRef.value?.openDialog(routeParams.value.taskId);
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
|
routeParams.value = { ...(route.query as Record<string, any>) };
|
|
|
loadSelectOptions();
|
|
|
if (isEdit.value) {
|
|
|
await loadDetail();
|
|
|
} else {
|
|
|
form.value.changeType = '1';
|
|
|
form.value.flowStatus = 'draft';
|
|
|
}
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.contract-detail-panel {
|
|
|
border: 1px solid var(--el-border-color);
|
|
|
border-radius: 4px;
|
|
|
padding: 12px;
|
|
|
min-height: 200px;
|
|
|
}
|
|
|
.panel-title {
|
|
|
font-weight: 600;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
.panel-placeholder {
|
|
|
color: var(--el-text-color-secondary);
|
|
|
font-size: 12px;
|
|
|
padding: 16px 0;
|
|
|
}
|
|
|
</style>
|