admin-vue3/src/views/crm/contract/ContractForm.vue

428 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1280">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="120px"
>
<el-row>
<!-- <el-col :span="8">
<el-form-item label="合同编号" prop="no">
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item label="合同名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="主客户签约方 " prop="customerId">
<el-select
v-model="formData.customerId"
placeholder="请选择客户签约方 "
class="w-1/1"
@change="handleCustomerChange"
:multiple-limit="3"
filterable
clearable
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="其他客户签约方 " prop="otherCustomerId">
<el-select
v-model="formData.otherCustomerId"
placeholder="请选择客户签约方 "
class="w-1/1"
@change="handleCustomerChange"
:multiple-limit="2"
filterable
clearable
multiple
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="公司签约方" prop="deptId">
<el-select
v-model="formData.deptId"
class="w-1/1"
>
<el-option
v-for="item in deptList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="合同状态" prop="contractStatus">
<el-select v-model="formData.contractStatus" placeholder="请选择合同状态" class="w-1/1">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CONTRACT_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同类型" prop="contractType">
<el-select v-model="formData.contractType" placeholder="请选择合同类型" class="w-1/1">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CONTRACT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同属性" prop="contractAttribute">
<el-select v-model="formData.contractAttribute" placeholder="请选择合同属性" class="w-1/1">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CONTRACT_ATTRIBUTE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="签订日期" prop="orderDate">
<el-date-picker
v-model="formData.orderDate"
placeholder="选择签订日期"
type="date"
value-format="x"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
v-model="formData.startTime"
placeholder="选择开始时间"
type="date"
value-format="x"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker
v-model="formData.endTime"
placeholder="选择结束时间"
type="date"
value-format="x"
class="!w-1/1"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
</el-form-item>
</el-col>
</el-row>
<!-- 子表的表单 -->
<ContentWrap>
<el-tabs v-model="subTabsName" class="-mt-15px -mb-10px">
<el-tab-pane label="产品清单" name="product">
<ContractProductForm
ref="productFormRef"
:products="formData.products"
:disabled="disabled"
/>
</el-tab-pane>
</el-tabs>
</ContentWrap>
<el-row>
<el-col :span="8">
<el-form-item label="合同总金额" prop="totalProductPrice">
<el-input
disabled
v-model="formData.totalProductPrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="整单折扣(%" prop="discountPercent">
<el-input-number
v-model="formData.discountPercent"
placeholder="请输入整单折扣"
controls-position="right"
:min="0"
:precision="2"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="折扣后金额" prop="totalPrice">
<el-input
disabled
v-model="formData.totalPrice"
placeholder="请输入商机金额"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col> -->
</el-row>
<el-form-item label="附加图片" prop="picUrls">
<UploadImgs v-model="formData.picUrls" :fileType="['image/jpeg', 'image/png']" />
</el-form-item>
<el-form-item label="附件" prop="fileUrls">
<UploadFile v-model="formData.fileUrls" :fileType="['pdf']"/>
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">保存</el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as CustomerApi from '@/api/crm/customer'
import * as ContractApi from '@/api/crm/contract'
import * as UserApi from '@/api/system/user'
import * as ContactApi from '@/api/crm/contact'
import * as BusinessApi from '@/api/crm/business'
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils'
import { useUserStore } from '@/store/modules/user'
import ContractProductForm from '@/views/crm/contract/components/ContractProductForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as DeptApi from '@/api/system/dept'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const formData = ref({
id: undefined,
no: undefined,
name: undefined,
customerId: undefined,
otherCustomerId: [],
contractStatus: undefined,
contractType: undefined,
contractAttribute: undefined,
businessId: undefined,
orderDate: undefined,
startTime: undefined,
endTime: undefined,
signUserId: undefined,
signContactId: undefined,
ownerUserId: undefined,
deptId: undefined,
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
products: [],
picUrls: [],
fileUrls: [],
})
const validateEndTime = (rule: any, value: any, callback: any) => {
if (!value || !formData.value.startTime) {
callback()
return
}
if (value < formData.value.startTime) {
callback(new Error('结束时间不能早于开始时间'))
} else {
callback()
}
}
const formRules = reactive({
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '主签约方不能为空', trigger: 'blur' }],
orderDate: [{ required: true, message: '下单日期不能为空', trigger: 'blur' }],
contractStatus: [{ required: true, message: '合同状态不能为空', trigger: 'blur' }],
contractType: [{ required: true, message: '合同类型不能为空', trigger: 'blur' }],
contractAttribute: [{ required: true, message: '合同属性不能为空', trigger: 'blur' }],
deptId: [{ required: true, message: '公司签约方不能为空', trigger: 'blur' }],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
endTime: [
{ required: true, message: '结束时间不能为空', trigger: 'blur' },
{ validator: validateEndTime, trigger: 'blur' }
],
})
const formRef = ref() // 表单 Ref
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const customerList = ref([]) // 客户列表的数据
const businessList = ref<BusinessApi.BusinessVO[]>([])
const contactList = ref<ContactApi.ContactVO[]>([])
const deptList = ref<DeptApi.DeptVO[]>([])
/** 子表的表单 */
const subTabsName = ref('product')
const productFormRef = ref()
/** 计算 discountPrice、totalPrice 价格 */
watch(
() => formData.value,
(val) => {
if (!val) {
return
}
const totalProductPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
const discountPrice =
val.discountPercent != null
? erpPriceMultiply(totalProductPrice, val.discountPercent / 100.0)
: 0
const totalPrice = totalProductPrice - discountPrice
// 赋值
formData.value.totalProductPrice = totalProductPrice
formData.value.totalPrice = totalPrice
},
{ deep: true }
)
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ContractApi.getContract(id)
} finally {
formLoading.value = false
}
}
// 获得客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
// 默认新建时选中自己
// if (formType.value === 'create') {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// 获取联系人
contactList.value = await ContactApi.getSimpleContactList()
// 加载部门列表
deptList.value = await DeptApi.querySimpleDeptList(2)
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 校验产品清单不能为空
if (!formData.value.products || formData.value.products.length === 0) {
message.error('产品清单不能为空')
return
}
// 校验图片或附件至少有一个不为空
const hasPic = formData.value.picUrls && formData.value.picUrls.length > 0
const hasFile = formData.value.fileUrls && formData.value.fileUrls.length > 0
if (!hasPic && !hasFile) {
message.error('附加图片和附件不能同时为空')
return
}
// 提交请求
formLoading.value = true
productFormRef.value.validate()
try {
const data = unref(formData.value) as unknown as ContractApi.ContractVO
if (formType.value === 'create') {
await ContractApi.createContract(data)
message.success(t('common.createSuccess'))
} else {
await ContractApi.updateContract(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
no: undefined,
name: undefined,
customerId: undefined,
otherCustomerId: [],
contractStatus: undefined,
contractType: undefined,
contractAttribute: undefined,
businessId: undefined,
orderDate: undefined,
startTime: undefined,
endTime: undefined,
signUserId: undefined,
signContactId: undefined,
ownerUserId: undefined,
deptId: undefined,
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
products: [],
picUrls: [],
fileUrls: [],
}
formRef.value?.resetFields()
}
/** 处理切换客户 */
const handleCustomerChange = () => {
formData.value.businessId = undefined
formData.value.signContactId = undefined
formData.value.products = []
}
/** 动态获取客户联系人 */
const getContactOptions = computed(() =>
contactList.value.filter((item) => item.customerId == formData.value.customerId)
)
</script>