合同变更

pull/781/head
zy 2025-05-12 15:06:45 +08:00
parent 56f3fbf833
commit 4c9618bc29
4 changed files with 571 additions and 7 deletions

View File

@ -125,6 +125,12 @@ export const changeContract = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/contract/changeContract', data })
}
// 合同变更列表
export const changeContractList = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/contract-change-record/page', data })
}
// 获得待审核合同数量
export const getAuditContractCount = async () => {
return await request.get({ url: '/crm/contract/audit-count' })

View File

@ -614,7 +614,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
component: () => import('@/views/crm/business/BusinessForm.vue')
},
{
path: 'contract/add',
path: 'contract/ContractForm',
name: 'CrmContractAdd',
meta: {
title: '合同新增',

View File

@ -28,12 +28,13 @@
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
@change="changeCustomer"
:disabled="formType"
placeholder="请选择客户"
class="w-1/1"
>
<el-option
v-for="item in contactList"
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
@ -41,14 +42,32 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="8" v-if="!formType">
<el-form-item label="合同名称" prop="customerId">
<el-select
v-model="formData.processInstance"
:disabled="formType"
@change="getContractName"
placeholder="请选择合同名称"
class="w-1/1"
>
<el-option
v-for="item in contractList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formType">
<el-form-item label="协议编码" prop="name">
<el-input v-model="formData.processInstanceId" disabled placeholder="协议编码" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同状态" prop="quotationId">
<el-select v-model="formData.auditStatus" disabled clearable placeholder="请选择合同状态">
<el-select v-model="formData.auditStatus" disabled clearable placeholder="">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_AUDIT_STATUS)"
:key="dict.value"
@ -65,7 +84,7 @@
disabled
type="date"
value-format="x"
placeholder="请选择协议截止日期"
placeholder=""
style="width: 100%"
/>
</el-form-item>
@ -194,6 +213,7 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const customerList = ref([]) //
const quotationList = ref<QuotationVO[]>([])
const contactList = ref<ContactApi.ContactVO[]>([])
const contractList = ref<ContractApi.ContractVO[]>([])
/** 子表的表单 */
const subTabsName = ref('limit')
@ -224,6 +244,9 @@ const open = async (type: string) => {
formLoading.value = true
try {
let data = await ContractApi.getContract(type)
if(!route.query.id) delete data.customerId
formData.value = Object.assign(formData.value, data)
} finally {
formLoading.value = false
@ -466,6 +489,17 @@ const getContactOptions = computed(() =>
contactList.value.filter((item) => item.customerId == formData.value.customerId)
)
const changeCustomer = async (val) => {
contractList.value = await ContractApi.getContractSimpleList(val)
}
const getContractName = (val) => {
open(val)
const arr = contractList.value.filter(v => v.id == val)
formData.value.processInstanceId = (arr.length ? arr[0]['no'] : '')
}
const route = useRoute();
onMounted(async () => {
formType.value = route.query.id;
@ -473,7 +507,7 @@ onMounted(async () => {
if (formType.value) open(formType.value)
//
customerList.value = await CustomerApi.getCustomerSimpleList()
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
@ -485,5 +519,6 @@ onMounted(async () => {
// }
//
contactList.value = await CustomerApi.getCustomerSimpleList()
});
</script>

View File

@ -0,0 +1,523 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="合同编号" prop="no">
<el-input
v-model="queryParams.no"
class="!w-240px"
clearable
placeholder="请输入合同编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="合同名称" prop="name">
<el-input
v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入合同名称"
@keyup.enter="handleQuery"
/>
<el-form-item label="客户" prop="customerId">
<el-select
v-model="queryParams.customerId"
class="!w-240px"
clearable
lable-key="name"
placeholder="请选择客户"
value-key="id"
@keyup.enter="handleQuery"
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['crm:contract:create']" type="primary" @click="handleChange">
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button
v-hasPermi="['crm:contract:export']"
:loading="exportLoading"
plain
type="success"
@click="handleExport"
>
<Icon class="mr-5px" icon="ep:download" />
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
<template #default="scope">
<el-link :underline="false" type="primary" @click="openDetail(scope.row)">
{{ scope.row.name }}
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="合同编号" prop="no" width="180" />
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
<!-- <template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openCustomerDetail(scope.row.customerId)"
>
{{ scope.row.customerName }}
</el-link>
</template> -->
</el-table-column>
<!-- <el-table-column align="center" label="商机名称" prop="businessName" width="130">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openBusinessDetail(scope.row.businessId)"
>
{{ scope.row.businessName }}
</el-link>
</template>
</el-table-column> -->
<el-table-column
align="center"
label="合同金额(元)"
prop="totalPrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<!-- <el-table-column
align="center"
label="下单时间"
prop="orderDate"
width="120"
:formatter="dateFormatter2"
/>-->
<el-table-column
align="center"
label="合同开始时间"
prop="startTime"
width="120"
:formatter="dateFormatter2"
/>
<el-table-column
align="center"
label="合同结束时间"
prop="endTime"
width="120"
:formatter="dateFormatter2"
/>
<!-- <el-table-column align="center" label="客户签约人" prop="signContactName" width="130">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContactDetail(scope.row.signContactId)"
>
{{ scope.row.signContactName }}
</el-link>
</template>
</el-table-column>-->
<!-- <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />-->
<!-- <el-table-column align="center" label="备注" prop="remark" width="200" />
<el-table-column
align="center"
label="已回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
align="center"
label="未回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
>
<template #default="scope">
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
</template>
</el-table-column>-->
<!-- <el-table-column
:formatter="dateFormatter"
align="center"
label="最后跟进时间"
prop="contactLastTime"
width="180px"
/>-->
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
<el-table-column
:formatter="dateFormatter"
align="center"
label="更新时间"
prop="updateTime"
width="180px"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180px"
/>
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="180">
<template #default="scope">
<div style="display:flex">
<el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="openFormEdit(scope.row)"
>
编辑
</el-button>
<!-- <el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="handleSubmit(scope.row)"
>
提交审核
</el-button>
<el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="handleChange(scope.row)"
>
合同变更
</el-button> -->
<el-button
v-hasPermi="['crm:contract:query']"
link
type="primary"
@click="openDetail(scope.row)"
>
详情
</el-button>
<el-button
v-if="scope.row.auditStatus !== 0"
link
v-hasPermi="['crm:customer-suggestion:query']"
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
v-if="scope.row.auditStatus !== 0 && scope.row.auditStatus !== 2 && scope.row.auditStatus !== 10"
v-hasPermi="['crm:contract:delete']"
link
type="danger"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
<el-dropdown
v-if="scope.row.auditStatus === 0 || scope.row.auditStatus === 2"
@command="(command) => handleCommand(command, scope.row)"
v-hasPermi="[
'crm:contract:update',
'crm:contract:delete',
'crm:contract:print',
'crm:contract:change'
]"
>
<el-button type="primary" link>更多<Icon icon="ep:d-arrow-right" /> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
command="handleSubmit"
v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0"
>
提交审核
</el-dropdown-item>
<el-dropdown-item
command="handleChange"
v-if="checkPermi(['crm:contract:change']) && scope.row.auditStatus === 2"
>
合同变更
</el-dropdown-item>
<el-dropdown-item
command="handlePrint"
v-if="checkPermi(['crm:contract:print']) && scope.row.auditStatus === 2"
>
合同下载
</el-dropdown-item>
<el-dropdown-item
command="handleDelete"
v-if="checkPermi(['crm:contract:delete']) && (scope.row.auditStatus === 0 || scope.row.auditStatus === 2)"
>
删除
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
<FileTemplateForm ref="formRef" @success="onPrintContract" />
</ContentWrap>
</template>
<script lang="ts" setup>
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download'
import FileTemplateForm from './templateForm.vue'
import * as ContractApi from '@/api/crm/contract'
import { DICT_TYPE } from '@/utils/dict'
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
import * as CustomerApi from '@/api/crm/customer'
import { checkPermi } from '@/utils/permission'
import { TabsPaneContext } from 'element-plus'
defineOptions({ name: 'ContractChangeRecord' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
sceneType: '1', // activeName
name: null,
customerId: null,
orderDate: [],
no: null
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
const activeName = ref('1') // tab
const customerList = ref<CustomerApi.CustomerVO[]>([]) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ContractApi.changeContractList(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const router = useRouter() //
const openFormEdit = (row: Object) => {
router.push({ name: 'CrmContractEdit', query: { id: row.id} })
}
const openFormAdd = () => {
push({ name: 'CrmContractAdd' })
}
const openFormDetail = (row: Object) => {
push({ name: 'CrmContractDetail', query: { id: row.id } })
}
const handleChange = (row) => {
router.push({ name: 'CrmContractChange', query: { id: row.id } })
}
/** 操作分发 */
const handleCommand = (command: string, row) => {
switch (command) {
case 'handleSubmit':
handleSubmit(row)
break
case 'handleChange':
handleChange(row)
break
case 'handlePrint':
onPrintContract(row)
break
case 'handleDelete':
handleDelete(row.id)
break
default:
break
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await ContractApi.deleteContract(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await ContractApi.exportContract(queryParams)
download.excel(data, '合同.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 提交审核 **/
const handleSubmit = async (row: ContractApi.ContractVO) => {
await message.confirm(`您确定提交【${row.name}】审核吗?`)
await ContractApi.submitContract(row.id)
message.success('提交审核成功!')
await getList()
}
const getPrintTemplate = (row) => {
formRef.value.open(row.id)
}
const onPrintContract = async (row: ContractApi.ContractVO) => {
try {
const res = await ContractApi.printContract(row.id)
if (!res) {
throw new Error('返回的合同文件为空')
}
// const contentType = res.type.toLowerCase()
// let fileType = 'application/msword' // 使 .doc MIME
// let extension = '.doc'
// if (contentType.includes('wordprocessingml')) {
// fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // .docx MIME
// extension = '.docx'
// }
// // blob
// const blob = new Blob([res], { type: fileType })
// const url = window.URL.createObjectURL(blob)
// const link = document.createElement('a')
// link.href = url
// const disposition = res.headers['content-disposition']
// let fileName = '.docx'
// if (disposition && disposition.includes('filename=')) {
// const match = disposition.match(/filename="?([^"]+)"?/)
// if (match?.[1]) {
// fileName = decodeURIComponent(match[1])
// }
// }
// link.click()
// console.log('%csrc/views/crm/contract/index.vue:489 res.data', 'color: #007acc;', res);
window.open(res)
// message.success('')
await getList()
} catch (error) {
console.error('合同下载失败', error)
}
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 打开合同详情 */
const { push } = useRouter()
const openDetail = (row: Object) => {
router.push({ name: 'CrmContractDetail', query: { id: row.id } })
}
/** 打开客户详情 */
const openCustomerDetail = (id: number) => {
router.push({ name: 'CrmCustomerDetail', query: { id } })
}
/** 打开联系人详情 */
const openContactDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } })
}
/** 打开商机详情 */
const openBusinessDetail = (id: number) => {
push({ name: 'CrmBusinessDetail', params: { id } })
}
/** 初始化 **/
onMounted(async () => {
await getList()
customerList.value = await CustomerApi.getCustomerSimpleList()
})
onActivated(()=>{
getList()
})
</script>