Merge remote-tracking branch 'yudao/dev' into dev
commit
783f6283ca
|
@ -1,11 +1,6 @@
|
|||
import request from '@/config/axios'
|
||||
// TODO 芋艿:融合下
|
||||
|
||||
// 3. 获得分配给我的客户数量
|
||||
export const getFollowCustomerCount = async () => {
|
||||
return await request.get({ url: '/crm/customer/follow-customer-count' })
|
||||
}
|
||||
|
||||
// 5. 获得待审核合同数量
|
||||
export const getCheckContractCount = async () => {
|
||||
return await request.get({ url: '/crm/contract/check-contract-count' })
|
||||
|
|
|
@ -4,22 +4,42 @@ import { TransferReqVO } from '@/api/crm/customer'
|
|||
export interface BusinessVO {
|
||||
id: number
|
||||
name: string
|
||||
statusTypeId: number
|
||||
statusId: number
|
||||
contactNextTime: Date
|
||||
customerId: number
|
||||
dealTime: Date
|
||||
price: number
|
||||
discountPercent: number
|
||||
productPrice: number
|
||||
remark: string
|
||||
customerName?: string
|
||||
followUpStatus: boolean
|
||||
contactLastTime: Date
|
||||
contactNextTime: Date
|
||||
ownerUserId: number
|
||||
roUserIds: string
|
||||
rwUserIds: string
|
||||
ownerUserName?: string // 负责人的用户名称
|
||||
ownerUserDept?: string // 负责人的部门名称
|
||||
statusTypeId: number
|
||||
statusTypeName?: string
|
||||
statusId: number
|
||||
statusName?: string
|
||||
endStatus: number
|
||||
endRemark: string
|
||||
contactLastTime: Date
|
||||
followUpStatus: number
|
||||
dealTime: Date
|
||||
totalProductPrice: number
|
||||
totalPrice: number
|
||||
discountPercent: number
|
||||
remark: string
|
||||
creator: string // 创建人
|
||||
creatorName?: string // 创建人名称
|
||||
createTime: Date // 创建时间
|
||||
updateTime: Date // 更新时间
|
||||
products?: [
|
||||
{
|
||||
id: number
|
||||
productId: number
|
||||
productName: string
|
||||
productNo: string
|
||||
productUnit: number
|
||||
productPrice: number
|
||||
businessPrice: number
|
||||
count: number
|
||||
totalPrice: number
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 查询 CRM 商机列表
|
||||
|
@ -52,6 +72,11 @@ export const updateBusiness = async (data: BusinessVO) => {
|
|||
return await request.put({ url: `/crm/business/update`, data })
|
||||
}
|
||||
|
||||
// 修改 CRM 商机状态
|
||||
export const updateBusinessStatus = async (data: BusinessVO) => {
|
||||
return await request.put({ url: `/crm/business/update-status`, data })
|
||||
}
|
||||
|
||||
// 删除 CRM 商机
|
||||
export const deleteBusiness = async (id: number) => {
|
||||
return await request.delete({ url: `/crm/business/delete?id=` + id })
|
||||
|
@ -67,11 +92,6 @@ export const getBusinessPageByContact = async (params) => {
|
|||
return await request.get({ url: `/crm/business/page-by-contact`, params })
|
||||
}
|
||||
|
||||
// 获得 CRM 商机列表
|
||||
export const getBusinessListByIds = async (val: number[]) => {
|
||||
return await request.get({ url: '/crm/business/list-by-ids', params: { ids: val.join(',') } })
|
||||
}
|
||||
|
||||
// 商机转移
|
||||
export const transferBusiness = async (data: TransferReqVO) => {
|
||||
return await request.put({ url: '/crm/business/transfer', data })
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface BusinessStatusTypeVO {
|
||||
id: number
|
||||
name: string
|
||||
deptIds: number[]
|
||||
statuses?: {
|
||||
id: number
|
||||
name: string
|
||||
percent: number
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_STATUSES = [
|
||||
{
|
||||
endStatus: 1,
|
||||
key: '结束',
|
||||
name: '赢单',
|
||||
percent: 100
|
||||
},
|
||||
{
|
||||
endStatus: 2,
|
||||
key: '结束',
|
||||
name: '输单',
|
||||
percent: 0
|
||||
},
|
||||
{
|
||||
endStatus: 3,
|
||||
key: '结束',
|
||||
name: '无效',
|
||||
percent: 0
|
||||
}
|
||||
]
|
||||
|
||||
// 查询商机状态组列表
|
||||
export const getBusinessStatusPage = async (params: any) => {
|
||||
return await request.get({ url: `/crm/business-status/page`, params })
|
||||
}
|
||||
|
||||
// 新增商机状态组
|
||||
export const createBusinessStatus = async (data: BusinessStatusTypeVO) => {
|
||||
return await request.post({ url: `/crm/business-status/create`, data })
|
||||
}
|
||||
|
||||
// 修改商机状态组
|
||||
export const updateBusinessStatus = async (data: BusinessStatusTypeVO) => {
|
||||
return await request.put({ url: `/crm/business-status/update`, data })
|
||||
}
|
||||
|
||||
// 查询商机状态类型详情
|
||||
export const getBusinessStatus = async (id: number) => {
|
||||
return await request.get({ url: `/crm/business-status/get?id=` + id })
|
||||
}
|
||||
|
||||
// 删除商机状态
|
||||
export const deleteBusinessStatus = async (id: number) => {
|
||||
return await request.delete({ url: `/crm/business-status/delete?id=` + id })
|
||||
}
|
||||
|
||||
// 获得商机状态组列表
|
||||
export const getBusinessStatusTypeSimpleList = async () => {
|
||||
return await request.get({ url: `/crm/business-status/type-simple-list` })
|
||||
}
|
||||
|
||||
// 获得商机阶段列表
|
||||
export const getBusinessStatusSimpleList = async (typeId: number) => {
|
||||
return await request.get({ url: `/crm/business-status/status-simple-list`, params: { typeId } })
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface BusinessStatusTypeVO {
|
||||
id: number
|
||||
name: string
|
||||
deptIds: number[]
|
||||
status: boolean
|
||||
}
|
||||
|
||||
// 查询商机状态类型列表
|
||||
export const getBusinessStatusTypePage = async (params) => {
|
||||
return await request.get({ url: `/crm/business-status-type/page`, params })
|
||||
}
|
||||
|
||||
// 查询商机状态类型详情
|
||||
export const getBusinessStatusType = async (id: number) => {
|
||||
return await request.get({ url: `/crm/business-status-type/get?id=` + id })
|
||||
}
|
||||
|
||||
// 新增商机状态类型
|
||||
export const createBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||
return await request.post({ url: `/crm/business-status-type/create`, data })
|
||||
}
|
||||
|
||||
// 修改商机状态类型
|
||||
export const updateBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||
return await request.put({ url: `/crm/business-status-type/update`, data })
|
||||
}
|
||||
|
||||
// 删除商机状态类型
|
||||
export const deleteBusinessStatusType = async (id: number) => {
|
||||
return await request.delete({ url: `/crm/business-status-type/delete?id=` + id })
|
||||
}
|
||||
|
||||
// 导出商机状态类型 Excel
|
||||
export const exportBusinessStatusType = async (params) => {
|
||||
return await request.download({ url: `/crm/business-status-type/export-excel`, params })
|
||||
}
|
||||
|
||||
// 获取商机状态类型信息列表
|
||||
export const getBusinessStatusTypeList = async () => {
|
||||
return await request.get({ url: `/crm/business-status-type/get-simple-list` })
|
||||
}
|
||||
|
||||
// 根据类型ID获取商机状态信息列表
|
||||
export const getBusinessStatusListByTypeId = async (typeId: number) => {
|
||||
return await request.get({ url: `/crm/business-status-type/get-status-list?typeId=` + typeId })
|
||||
}
|
|
@ -2,29 +2,34 @@ import request from '@/config/axios'
|
|||
import { TransferReqVO } from '@/api/crm/customer'
|
||||
|
||||
export interface ContactVO {
|
||||
name: string
|
||||
nextTime: Date
|
||||
mobile: string
|
||||
telephone: string
|
||||
email: string
|
||||
post: string
|
||||
customerId: number
|
||||
detailAddress: string
|
||||
remark: string
|
||||
ownerUserId: string
|
||||
lastTime: Date
|
||||
id: number
|
||||
parentId: number
|
||||
qq: number
|
||||
wechat: string
|
||||
sex: number
|
||||
master: boolean
|
||||
creatorName: string
|
||||
updateTime?: Date
|
||||
createTime?: Date
|
||||
customerName: string
|
||||
areaName: string
|
||||
ownerUserName: string
|
||||
id: number // 编号
|
||||
name: string // 联系人名称
|
||||
customerId: number // 客户编号
|
||||
customerName?: string // 客户名称
|
||||
contactLastTime: Date // 最后跟进时间
|
||||
contactLastContent: string // 最后跟进内容
|
||||
contactNextTime: Date // 下次联系时间
|
||||
ownerUserId: number // 负责人的用户编号
|
||||
ownerUserName?: string // 负责人的用户名称
|
||||
ownerUserDept?: string // 负责人的部门名称
|
||||
mobile: string // 手机号
|
||||
telephone: string // 电话
|
||||
qq: string // QQ
|
||||
wechat: string // wechat
|
||||
email: string // email
|
||||
areaId: number // 所在地
|
||||
areaName?: string // 所在地名称
|
||||
detailAddress: string // 详细地址
|
||||
sex: number // 性别
|
||||
master: boolean // 是否主联系人
|
||||
post: string // 职务
|
||||
parentId: number // 上级联系人编号
|
||||
parentName?: string // 上级联系人名称
|
||||
remark: string // 备注
|
||||
creator: string // 创建人
|
||||
creatorName?: string // 创建人名称
|
||||
createTime: Date // 创建时间
|
||||
updateTime: Date // 更新时间
|
||||
}
|
||||
|
||||
export interface ContactBusinessReqVO {
|
||||
|
@ -32,6 +37,11 @@ export interface ContactBusinessReqVO {
|
|||
businessIds: number[]
|
||||
}
|
||||
|
||||
export interface ContactBusiness2ReqVO {
|
||||
businessId: number
|
||||
contactIds: number[]
|
||||
}
|
||||
|
||||
// 查询 CRM 联系人列表
|
||||
export const getContactPage = async (params) => {
|
||||
return await request.get({ url: `/crm/contact/page`, params })
|
||||
|
@ -42,6 +52,11 @@ export const getContactPageByCustomer = async (params: any) => {
|
|||
return await request.get({ url: `/crm/contact/page-by-customer`, params })
|
||||
}
|
||||
|
||||
// 查询 CRM 联系人列表,基于指定商机
|
||||
export const getContactPageByBusiness = async (params: any) => {
|
||||
return await request.get({ url: `/crm/contact/page-by-business`, params })
|
||||
}
|
||||
|
||||
// 查询 CRM 联系人详情
|
||||
export const getContact = async (id: number) => {
|
||||
return await request.get({ url: `/crm/contact/get?id=` + id })
|
||||
|
@ -72,21 +87,26 @@ export const getSimpleContactList = async () => {
|
|||
return await request.get({ url: `/crm/contact/simple-all-list` })
|
||||
}
|
||||
|
||||
// 获得 CRM 联系人列表
|
||||
export const getContactListByIds = async (val: number[]) => {
|
||||
return await request.get({ url: '/crm/contact/list-by-ids', params: { ids: val.join(',') } })
|
||||
}
|
||||
|
||||
// 批量新增联系人商机关联
|
||||
export const createContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||
return await request.post({ url: `/crm/contact/create-business-list`, data })
|
||||
}
|
||||
|
||||
// 批量新增联系人商机关联
|
||||
export const createContactBusinessList2 = async (data: ContactBusiness2ReqVO) => {
|
||||
return await request.post({ url: `/crm/contact/create-business-list2`, data })
|
||||
}
|
||||
|
||||
// 解除联系人商机关联
|
||||
export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||
return await request.delete({ url: `/crm/contact/delete-business-list`, data })
|
||||
}
|
||||
|
||||
// 解除联系人商机关联
|
||||
export const deleteContactBusinessList2 = async (data: ContactBusiness2ReqVO) => {
|
||||
return await request.delete({ url: `/crm/contact/delete-business-list2`, data })
|
||||
}
|
||||
|
||||
// 联系人转移
|
||||
export const transferContact = async (data: TransferReqVO) => {
|
||||
return await request.put({ url: '/crm/contact/transfer', data })
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface ContractConfigVO {
|
||||
notifyEnabled?: boolean
|
||||
notifyDays?: number
|
||||
}
|
||||
|
||||
// 获取合同配置
|
||||
export const getContractConfig = async () => {
|
||||
return await request.get({ url: `/crm/contract-config/get` })
|
||||
}
|
||||
|
||||
// 更新合同配置
|
||||
export const saveContractConfig = async (data: ContractConfigVO) => {
|
||||
return await request.put({ url: `/crm/contract-config/save`, data })
|
||||
}
|
|
@ -1,35 +1,49 @@
|
|||
import request from '@/config/axios'
|
||||
import { ProductExpandVO } from '@/api/crm/product'
|
||||
import { TransferReqVO } from '@/api/crm/customer'
|
||||
|
||||
export interface ContractVO {
|
||||
id: number
|
||||
name: string
|
||||
no: string
|
||||
customerId: number
|
||||
customerName?: string
|
||||
businessId: number
|
||||
businessName: string
|
||||
processInstanceId: number
|
||||
orderDate: Date
|
||||
contactLastTime: Date
|
||||
ownerUserId: number
|
||||
no: string
|
||||
ownerUserName?: string
|
||||
ownerUserDeptName?: string
|
||||
processInstanceId: number
|
||||
auditStatus: number
|
||||
orderDate: Date
|
||||
startTime: Date
|
||||
endTime: Date
|
||||
price: number
|
||||
totalProductPrice: number
|
||||
discountPercent: number
|
||||
productPrice: number
|
||||
contactId: number
|
||||
totalPrice: number
|
||||
totalReceivablePrice: number
|
||||
signContactId: number
|
||||
signContactName?: string
|
||||
signUserId: number
|
||||
signUserName: string
|
||||
contactLastTime: Date
|
||||
auditStatus: number
|
||||
remark: string
|
||||
productItems: ProductExpandVO[]
|
||||
createTime?: Date
|
||||
creator: string
|
||||
creatorName: string
|
||||
updateTime?: Date
|
||||
createTime?: Date
|
||||
customerName: string
|
||||
contactName: string
|
||||
ownerUserName: string
|
||||
products?: [
|
||||
{
|
||||
id: number
|
||||
productId: number
|
||||
productName: string
|
||||
productNo: string
|
||||
productUnit: number
|
||||
productPrice: number
|
||||
contractPrice: number
|
||||
count: number
|
||||
totalPrice: number
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 查询 CRM 合同列表
|
||||
|
@ -42,11 +56,23 @@ export const getContractPageByCustomer = async (params: any) => {
|
|||
return await request.get({ url: `/crm/contract/page-by-customer`, params })
|
||||
}
|
||||
|
||||
// 查询 CRM 联系人列表,基于指定商机
|
||||
export const getContractPageByBusiness = async (params: any) => {
|
||||
return await request.get({ url: `/crm/contract/page-by-business`, params })
|
||||
}
|
||||
|
||||
// 查询 CRM 合同详情
|
||||
export const getContract = async (id: number) => {
|
||||
return await request.get({ url: `/crm/contract/get?id=` + id })
|
||||
}
|
||||
|
||||
// 查询 CRM 合同下拉列表
|
||||
export const getContractSimpleList = async (customerId: number) => {
|
||||
return await request.get({
|
||||
url: `/crm/contract/simple-list?customerId=${customerId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 新增 CRM 合同
|
||||
export const createContract = async (data: ContractVO) => {
|
||||
return await request.post({ url: `/crm/contract/create`, data })
|
||||
|
@ -76,3 +102,13 @@ export const submitContract = async (id: number) => {
|
|||
export const transferContract = async (data: TransferReqVO) => {
|
||||
return await request.put({ url: '/crm/contract/transfer', data })
|
||||
}
|
||||
|
||||
// 获得待审核合同数量
|
||||
export const getAuditContractCount = async () => {
|
||||
return await request.get({ url: '/crm/contract/audit-count' })
|
||||
}
|
||||
|
||||
// 获得即将到期(提醒)的合同数量
|
||||
export const getRemindContractCount = async () => {
|
||||
return await request.get({ url: '/crm/contract/remind-count' })
|
||||
}
|
||||
|
|
|
@ -90,6 +90,11 @@ export const importCustomerTemplate = () => {
|
|||
return request.download({ url: '/crm/customer/get-import-template' })
|
||||
}
|
||||
|
||||
// 导入客户
|
||||
export const handleImport = async (formData) => {
|
||||
return await request.upload({ url: `/crm/customer/import`, data: formData })
|
||||
}
|
||||
|
||||
// 客户列表
|
||||
export const getCustomerSimpleList = async () => {
|
||||
return await request.get({ url: `/crm/customer/simple-list` })
|
||||
|
|
|
@ -11,7 +11,17 @@ export interface FollowUpRecordVO {
|
|||
fileUrls: string[] // 附件
|
||||
nextTime: Date // 下次联系时间
|
||||
businessIds: number[] // 关联的商机编号数组
|
||||
businesses: {
|
||||
id: number
|
||||
name: string
|
||||
}[] // 关联的商机数组
|
||||
contactIds: number[] // 关联的联系人编号数组
|
||||
contacts: {
|
||||
id: number
|
||||
name: string
|
||||
}[] // 关联的联系人数组
|
||||
creator: string
|
||||
creatorName?: string
|
||||
}
|
||||
|
||||
// 跟进记录 API
|
||||
|
@ -21,28 +31,13 @@ export const FollowUpRecordApi = {
|
|||
return await request.get({ url: `/crm/follow-up-record/page`, params })
|
||||
},
|
||||
|
||||
// 查询跟进记录详情
|
||||
getFollowUpRecord: async (id: number) => {
|
||||
return await request.get({ url: `/crm/follow-up-record/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增跟进记录
|
||||
createFollowUpRecord: async (data: FollowUpRecordVO) => {
|
||||
return await request.post({ url: `/crm/follow-up-record/create`, data })
|
||||
},
|
||||
|
||||
// 修改跟进记录
|
||||
updateFollowUpRecord: async (data: FollowUpRecordVO) => {
|
||||
return await request.put({ url: `/crm/follow-up-record/update`, data })
|
||||
},
|
||||
|
||||
// 删除跟进记录
|
||||
deleteFollowUpRecord: async (id: number) => {
|
||||
return await request.delete({ url: `/crm/follow-up-record/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出跟进记录 Excel
|
||||
exportFollowUpRecord: async (params) => {
|
||||
return await request.download({ url: `/crm/follow-up-record/export-excel`, params })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,21 +8,21 @@ export interface ProductVO {
|
|||
price: number
|
||||
status: number
|
||||
categoryId: number
|
||||
categoryName?: string
|
||||
description: string
|
||||
ownerUserId: number
|
||||
}
|
||||
|
||||
export interface ProductExpandVO extends ProductVO {
|
||||
count: number
|
||||
discountPercent: number
|
||||
totalPrice: number
|
||||
}
|
||||
|
||||
// 查询产品列表
|
||||
export const getProductPage = async (params) => {
|
||||
return await request.get({ url: `/crm/product/page`, params })
|
||||
}
|
||||
|
||||
// 获得产品精简列表
|
||||
export const getProductSimpleList = async () => {
|
||||
return await request.get({ url: `/crm/product/simple-list` })
|
||||
}
|
||||
|
||||
// 查询产品详情
|
||||
export const getProduct = async (id: number) => {
|
||||
return await request.get({ url: `/crm/product/get?id=` + id })
|
||||
|
|
|
@ -5,15 +5,24 @@ export interface ReceivableVO {
|
|||
no: string
|
||||
planId: number
|
||||
customerId: number
|
||||
customerName?: string
|
||||
contractId: number
|
||||
contract?: {
|
||||
no: string
|
||||
totalPrice: number
|
||||
}
|
||||
auditStatus: number
|
||||
processInstanceId: number
|
||||
returnTime: Date
|
||||
returnType: string
|
||||
price: number
|
||||
ownerUserId: number
|
||||
sort: number
|
||||
ownerUserName?: string
|
||||
remark: string
|
||||
creator: string // 创建人
|
||||
creatorName?: string // 创建人名称
|
||||
createTime: Date // 创建时间
|
||||
updateTime: Date // 更新时间
|
||||
}
|
||||
|
||||
// 查询回款列表
|
||||
|
@ -50,3 +59,8 @@ export const deleteReceivable = async (id: number) => {
|
|||
export const exportReceivable = async (params) => {
|
||||
return await request.download({ url: `/crm/receivable/export-excel`, params })
|
||||
}
|
||||
|
||||
// 提交审核
|
||||
export const submitReceivable = async (id: number) => {
|
||||
return await request.put({ url: `/crm/receivable/submit?id=${id}` })
|
||||
}
|
||||
|
|
|
@ -4,18 +4,26 @@ export interface ReceivablePlanVO {
|
|||
id: number
|
||||
period: number
|
||||
receivableId: number
|
||||
status: number
|
||||
checkStatus: string
|
||||
processInstanceId: number
|
||||
price: number
|
||||
returnTime: Date
|
||||
remindDays: number
|
||||
returnType: number
|
||||
remindTime: Date
|
||||
customerId: number
|
||||
customerName?: string
|
||||
contractId: number
|
||||
contractNo?: string
|
||||
ownerUserId: number
|
||||
sort: number
|
||||
ownerUserName?: string
|
||||
remark: string
|
||||
creator: string // 创建人
|
||||
creatorName?: string // 创建人名称
|
||||
createTime: Date // 创建时间
|
||||
updateTime: Date // 更新时间
|
||||
receivable?: {
|
||||
price: number
|
||||
returnTime: Date
|
||||
}
|
||||
}
|
||||
|
||||
// 查询回款计划列表
|
||||
|
@ -33,6 +41,13 @@ export const getReceivablePlan = async (id: number) => {
|
|||
return await request.get({ url: `/crm/receivable-plan/get?id=` + id })
|
||||
}
|
||||
|
||||
// 查询回款计划下拉数据
|
||||
export const getReceivablePlanSimpleList = async (customerId: number, contractId: number) => {
|
||||
return await request.get({
|
||||
url: `/crm/receivable-plan/simple-list?customerId=${customerId}&contractId=${contractId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 新增回款计划
|
||||
export const createReceivablePlan = async (data: ReceivablePlanVO) => {
|
||||
return await request.post({ url: `/crm/receivable-plan/create`, data })
|
||||
|
|
|
@ -1,66 +1,66 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface BiRankRespVO {
|
||||
export interface StatisticsRankRespVO {
|
||||
count: number
|
||||
nickname: string
|
||||
deptName: string
|
||||
}
|
||||
|
||||
// 排行 API
|
||||
export const RankApi = {
|
||||
export const StatisticsRankApi = {
|
||||
// 获得合同排行榜
|
||||
getContractPriceRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-contract-price-rank',
|
||||
url: '/crm/statistics-rank/get-contract-price-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 获得回款排行榜
|
||||
getReceivablePriceRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-receivable-price-rank',
|
||||
url: '/crm/statistics-rank/get-receivable-price-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 签约合同排行
|
||||
getContractCountRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-contract-count-rank',
|
||||
url: '/crm/statistics-rank/get-contract-count-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 产品销量排行
|
||||
getProductSalesRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-product-sales-rank',
|
||||
url: '/crm/statistics-rank/get-product-sales-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 新增客户数排行
|
||||
getCustomerCountRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-customer-count-rank',
|
||||
url: '/crm/statistics-rank/get-customer-count-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 新增联系人数排行
|
||||
getContactsCountRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-contacts-count-rank',
|
||||
url: '/crm/statistics-rank/get-contacts-count-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 跟进次数排行
|
||||
getFollowCountRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-follow-count-rank',
|
||||
url: '/crm/statistics-rank/get-follow-count-rank',
|
||||
params
|
||||
})
|
||||
},
|
||||
// 跟进客户数排行
|
||||
getFollowCustomerCountRank: (params: any) => {
|
||||
return request.get({
|
||||
url: '/crm/bi-rank/get-follow-customer-count-rank',
|
||||
url: '/crm/statistics-rank/get-follow-customer-count-rank',
|
||||
params
|
||||
})
|
||||
}
|
|
@ -104,7 +104,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/dict',
|
||||
component: Layout,
|
||||
|
@ -518,6 +517,17 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
},
|
||||
component: () => import('@/views/crm/customer/detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'business/detail/:id',
|
||||
name: 'CrmBusinessDetail',
|
||||
meta: {
|
||||
title: '商机详情',
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
activeMenu: '/crm/business'
|
||||
},
|
||||
component: () => import('@/views/crm/business/detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'contract/detail/:id',
|
||||
name: 'CrmContractDetail',
|
||||
|
@ -529,6 +539,28 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
},
|
||||
component: () => import('@/views/crm/contract/detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'receivable-plan/detail/:id',
|
||||
name: 'CrmReceivablePlanDetail',
|
||||
meta: {
|
||||
title: '回款计划详情',
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
activeMenu: '/crm/receivable-plan'
|
||||
},
|
||||
component: () => import('@/views/crm/receivable/plan/detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'receivable/detail/:id',
|
||||
name: 'CrmReceivableDetail',
|
||||
meta: {
|
||||
title: '回款详情',
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
activeMenu: '/crm/receivable'
|
||||
},
|
||||
component: () => import('@/views/crm/receivable/detail/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'contact/detail/:id',
|
||||
name: 'CrmContactDetail',
|
||||
|
|
|
@ -234,6 +234,7 @@ const getProcessInstance = async () => {
|
|||
fApi.value?.fapi?.disabled(true)
|
||||
})
|
||||
} else {
|
||||
// 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
|
||||
BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,14 @@
|
|||
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="130" />
|
||||
<el-table-column align="center" label="合同名称" prop="name" width="130" />
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
||||
<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.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
|
@ -43,8 +49,24 @@
|
|||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @puhui999:做了商机详情后,可以把这个超链接加上 -->
|
||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130" />
|
||||
<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="下单时间"
|
||||
|
@ -52,13 +74,6 @@
|
|||
width="120"
|
||||
:formatter="dateFormatter2"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同金额"
|
||||
prop="price"
|
||||
width="130"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同开始时间"
|
||||
|
@ -78,17 +93,41 @@
|
|||
<el-link
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openContactDetail(scope.row.contactId)"
|
||||
@click="openContactDetail(scope.row.signContactId)"
|
||||
>
|
||||
{{ scope.row.contactName }}
|
||||
{{ 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="130" />
|
||||
<!-- TODO @puhui999:后续可加 【已收款金额】、【未收款金额】 -->
|
||||
<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="creatorName" width="120" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
@ -103,11 +142,24 @@
|
|||
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 fixed="right" label="操作" width="90">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
v-hasPermi="['crm:contract:update']"
|
||||
type="primary"
|
||||
@click="handleProcessDetail(scope.row)"
|
||||
>
|
||||
查看审批
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
|
@ -122,9 +174,9 @@
|
|||
<script setup lang="ts" name="CheckContract">
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { AUDIT_STATUS } from './common'
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
@ -132,7 +184,8 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
auditStatus: 20
|
||||
sceneType: 1, // 我负责的
|
||||
auditStatus: 10
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
|
@ -154,8 +207,18 @@ const handleQuery = () => {
|
|||
getList()
|
||||
}
|
||||
|
||||
/** 查看审批 */
|
||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContractDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const { push } = useRouter() // 路由
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
@ -165,6 +228,16 @@ const openContactDetail = (id: number) => {
|
|||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
const openBusinessDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
onActivated(async () => {
|
||||
await getList()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
|
@ -30,8 +30,14 @@
|
|||
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="130" />
|
||||
<el-table-column align="center" label="合同名称" prop="name" width="130" />
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
||||
<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.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
|
@ -43,8 +49,24 @@
|
|||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @puhui999:做了商机详情后,可以把这个超链接加上 -->
|
||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130" />
|
||||
<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="下单时间"
|
||||
|
@ -52,13 +74,6 @@
|
|||
width="120"
|
||||
:formatter="dateFormatter2"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同金额"
|
||||
prop="price"
|
||||
width="130"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同开始时间"
|
||||
|
@ -78,17 +93,41 @@
|
|||
<el-link
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openContactDetail(scope.row.contactId)"
|
||||
@click="openContactDetail(scope.row.signContactId)"
|
||||
>
|
||||
{{ scope.row.contactName }}
|
||||
{{ 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="130" />
|
||||
<!-- TODO @puhui999:后续可加 【已收款金额】、【未收款金额】 -->
|
||||
<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="creatorName" width="120" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
@ -103,11 +142,24 @@
|
|||
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 fixed="right" label="操作" width="90">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
v-hasPermi="['crm:contract:update']"
|
||||
type="primary"
|
||||
@click="handleProcessDetail(scope.row)"
|
||||
>
|
||||
查看审批
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
|
@ -125,8 +177,7 @@ import * as ContractApi from '@/api/crm/contract'
|
|||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { CONTRACT_EXPIRY_TYPE } from './common'
|
||||
|
||||
const { push } = useRouter() // 路由
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
@ -134,6 +185,7 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
sceneType: '1', // 自己负责的
|
||||
expiryType: 1
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
@ -156,6 +208,17 @@ const handleQuery = () => {
|
|||
getList()
|
||||
}
|
||||
|
||||
/** 查看审批 */
|
||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContractDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
|
@ -166,10 +229,18 @@ const openContactDetail = (id: number) => {
|
|||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
const openBusinessDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
onActivated(async () => {
|
||||
await getList()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -130,8 +130,8 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
followUpStatus: false,
|
||||
sceneType: 1
|
||||
sceneType: 1,
|
||||
followUpStatus: false
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
|
@ -158,10 +158,13 @@ const openDetail = (id: number) => {
|
|||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
onActivated(async () => {
|
||||
await getList()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
@ -29,32 +29,31 @@
|
|||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="客户名称" prop="name" width="160">
|
||||
<el-table-column align="center" label="客户名称" fixed="left" prop="name" width="160">
|
||||
<template #default="scope">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="电话" prop="telephone" width="120" />
|
||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="所属行业" prop="industryId" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="客户级别" prop="level" width="120">
|
||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
||||
<el-table-column label="电话" align="center" prop="telephone" width="130" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" width="180" />
|
||||
<el-table-column align="center" label="客户级别" prop="level" width="135">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="网址" prop="website" width="200" />
|
||||
<el-table-column align="center" label="客户行业" prop="industryId" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
@ -63,12 +62,16 @@
|
|||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="距进入公海天数" prop="poolDay" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
@ -76,10 +79,17 @@
|
|||
prop="contactLastTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="最后跟进记录" prop="contactLastContent" width="200" />
|
||||
<el-table-column label="地址" align="center" prop="detailAddress" width="180" />
|
||||
<el-table-column align="center" label="距离进入公海天数" prop="poolDay" width="140">
|
||||
<template #default="scope"> {{ scope.row.poolDay }} 天</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
label="更新时间"
|
||||
prop="updateTime"
|
||||
width="180px"
|
||||
/>
|
||||
|
@ -90,8 +100,6 @@
|
|||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
|
|
|
@ -20,8 +20,9 @@ export const CONTACT_STATUS = [
|
|||
|
||||
/** 审批状态 */
|
||||
export const AUDIT_STATUS = [
|
||||
{ label: '已审批', value: 20 },
|
||||
{ label: '待审批', value: 10 }
|
||||
{ label: '待审批', value: 10 },
|
||||
{ label: '审核通过', value: 20 },
|
||||
{ label: '审核不通过', value: 30 }
|
||||
]
|
||||
|
||||
/** 回款提醒类型 */
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<el-col :span="20" :xs="24">
|
||||
<CustomerTodayContactList v-if="leftMenu === 'customerTodayContact'" />
|
||||
<ClueFollowList v-if="leftMenu === 'clueFollow'" />
|
||||
<CheckContract v-if="leftMenu === 'checkContract'" />
|
||||
<ContractAuditList v-if="leftMenu === 'contractAudit'" />
|
||||
<CheckReceivables v-if="leftMenu === 'checkReceivables'" />
|
||||
<EndContract v-if="leftMenu === 'endContract'" />
|
||||
<ContractRemindList v-if="leftMenu === 'contractRemind'" />
|
||||
<CustomerFollowList v-if="leftMenu === 'customerFollow'" />
|
||||
<CustomerPutPoolRemindList v-if="leftMenu === 'customerPutPoolRemind'" />
|
||||
<RemindReceivables v-if="leftMenu === 'remindReceivables'" />
|
||||
|
@ -33,25 +33,26 @@ import CustomerFollowList from './components/CustomerFollowList.vue'
|
|||
import CustomerTodayContactList from './components/CustomerTodayContactList.vue'
|
||||
import CustomerPutPoolRemindList from './components/CustomerPutPoolRemindList.vue'
|
||||
import ClueFollowList from './components/ClueFollowList.vue'
|
||||
import CheckContract from './tables/CheckContract.vue'
|
||||
import CheckReceivables from './tables/CheckReceivables.vue'
|
||||
import EndContract from './tables/EndContract.vue'
|
||||
import ContractAuditList from './components/ContractAuditList.vue'
|
||||
import ContractRemindList from './components/ContractRemindList.vue'
|
||||
import RemindReceivables from './tables/RemindReceivables.vue'
|
||||
import CheckReceivables from './tables/CheckReceivables.vue'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ClueApi from '@/api/crm/clue'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
|
||||
defineOptions({ name: 'CrmBacklog' })
|
||||
|
||||
const leftMenu = ref('customerTodayContact')
|
||||
|
||||
const customerTodayContactCount = ref(0)
|
||||
const clueFollowCount = ref(0)
|
||||
const customerFollowCount = ref(0)
|
||||
const customerPutPoolRemindCount = ref(0)
|
||||
const checkContractCount = ref(0)
|
||||
const customerTodayContactCount = ref(0)
|
||||
const contractAuditCount = ref(0)
|
||||
const contractRemindCount = ref(0)
|
||||
const checkReceivablesCount = ref(0)
|
||||
const remindReceivablesCount = ref(0)
|
||||
const endContractCount = ref(0)
|
||||
|
||||
const leftSides = ref([
|
||||
{
|
||||
|
@ -76,8 +77,8 @@ const leftSides = ref([
|
|||
},
|
||||
{
|
||||
name: '待审核合同',
|
||||
menu: 'checkContract',
|
||||
count: checkContractCount
|
||||
menu: 'contractAudit',
|
||||
count: contractAuditCount
|
||||
},
|
||||
{
|
||||
name: '待审核回款',
|
||||
|
@ -91,8 +92,8 @@ const leftSides = ref([
|
|||
},
|
||||
{
|
||||
name: '即将到期的合同',
|
||||
menu: 'endContract',
|
||||
count: endContractCount
|
||||
menu: 'contractRemind',
|
||||
count: contractRemindCount
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -110,10 +111,10 @@ const getCount = () => {
|
|||
)
|
||||
CustomerApi.getFollowCustomerCount().then((count) => (customerFollowCount.value = count))
|
||||
ClueApi.getFollowClueCount().then((count) => (clueFollowCount.value = count))
|
||||
BacklogApi.getCheckContractCount().then((count) => (checkContractCount.value = count))
|
||||
ContractApi.getAuditContractCount().then((count) => (contractAuditCount.value = count))
|
||||
ContractApi.getRemindContractCount().then((count) => (contractRemindCount.value = count))
|
||||
BacklogApi.getCheckReceivablesCount().then((count) => (checkReceivablesCount.value = count))
|
||||
BacklogApi.getRemindReceivablePlanCount().then((count) => (remindReceivablesCount.value = count))
|
||||
BacklogApi.getEndContractCount().then((count) => (endContractCount.value = count))
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
|
|
|
@ -1,113 +1,133 @@
|
|||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1280">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
label-width="120px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="商机名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入商机名称" />
|
||||
</el-form-item>
|
||||
<!-- TODO 芋艿:客户列表的组件 -->
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
:teleported="false"
|
||||
:visible="showCustomer"
|
||||
:offset="10"
|
||||
>
|
||||
<template #reference>
|
||||
<el-input
|
||||
placeholder="请选择客户"
|
||||
@click="openCustomerSelect"
|
||||
v-model="formData.customerName"
|
||||
/>
|
||||
</template>
|
||||
<el-table :data="customerList" ref="multipleTableRef" @select="handleSelectionChange">
|
||||
<el-table-column width="55" label="选择" type="selection" />
|
||||
<el-table-column width="100" label="编号" property="id" />
|
||||
<el-table-column width="150" label="客户名称" property="name" />
|
||||
<el-table-column width="100" label="客户来源" prop="source" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户级别" align="center" prop="level" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getCustomerList"
|
||||
layout="sizes, prev, pager, next"
|
||||
<el-row>
|
||||
<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="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10" :offset="13">
|
||||
<el-button @click="selectCustomer">确认</el-button>
|
||||
<el-button @click="showCustomer = false">取消</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<!-- TODO @ljlleo:idea 红色的报错,可以解决下 -->
|
||||
<el-form-item label="商机状态类型" prop="statusTypeId">
|
||||
<el-select
|
||||
v-model="formData.statusTypeId"
|
||||
placeholder="请选择商机状态类型"
|
||||
clearable
|
||||
@change="changeBusinessStatusType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in businessStatusTypeList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商机状态" prop="statusId">
|
||||
<el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable>
|
||||
<el-option
|
||||
v-for="item in businessStatusList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="预计成交日期" prop="dealTime">
|
||||
<el-date-picker
|
||||
v-model="formData.dealTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择预计成交日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商机金额" prop="price">
|
||||
<el-input v-model="formData.price" placeholder="请输入商机金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="整单折扣" prop="discountPercent">
|
||||
<el-input v-model="formData.discountPercent" placeholder="请输入整单折扣" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品总金额" prop="productPrice">
|
||||
<el-input v-model="formData.productPrice" placeholder="请输入产品总金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select
|
||||
:disabled="formData.customerDefault"
|
||||
v-model="formData.customerId"
|
||||
placeholder="请选择客户"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
: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="statusTypeId">
|
||||
<el-select
|
||||
v-model="formData.statusTypeId"
|
||||
placeholder="请选择商机状态组"
|
||||
clearable
|
||||
class="w-1/1"
|
||||
:disabled="formType !== 'create'"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusTypeList"
|
||||
: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="dealTime">
|
||||
<el-date-picker
|
||||
v-model="formData.dealTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择预计成交日期"
|
||||
class="!w-1/1"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 子表的表单 -->
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="subTabsName" class="-mt-15px -mb-10px">
|
||||
<el-tab-pane label="产品清单" name="product">
|
||||
<BusinessProductForm
|
||||
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="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
</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="price">
|
||||
<el-input
|
||||
disabled
|
||||
v-model="formData.totalPrice"
|
||||
placeholder="请输入商机金额"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
|
@ -117,10 +137,12 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { ElTable } from 'element-plus'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import BusinessProductForm from './components/BusinessProductForm.vue'
|
||||
import { erpPriceMultiply, erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -132,35 +154,56 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
|||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
statusTypeId: undefined,
|
||||
statusId: undefined,
|
||||
contactNextTime: undefined,
|
||||
customerId: undefined,
|
||||
dealTime: undefined,
|
||||
price: undefined,
|
||||
discountPercent: undefined,
|
||||
productPrice: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
roUserIds: undefined,
|
||||
rwUserIds: undefined,
|
||||
endStatus: undefined,
|
||||
endRemark: undefined,
|
||||
contactLastTime: undefined,
|
||||
followUpStatus: undefined
|
||||
statusTypeId: undefined,
|
||||
dealTime: undefined,
|
||||
discountPercent: 0,
|
||||
totalProductPrice: undefined,
|
||||
totalPrice: undefined,
|
||||
remark: undefined,
|
||||
products: [],
|
||||
contactId: undefined,
|
||||
customerDefault: false
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }]
|
||||
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }],
|
||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
|
||||
statusTypeId: [{ required: true, message: '商机状态组不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const businessStatusList = ref([]) // 商机状态列表
|
||||
const businessStatusTypeList = ref([]) //商机状态类型列表
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const statusTypeList = ref([]) // 商机状态类型列表
|
||||
// TODO 芋艿:统一的客户选择面板
|
||||
const customerList = ref([]) // 客户列表的数据
|
||||
|
||||
/** 子表的表单 */
|
||||
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) => {
|
||||
const open = async (type: string, id?: number, customerId?: number, contactId?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
|
@ -173,9 +216,26 @@ const open = async (type: string, id?: number) => {
|
|||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
if (customerId) {
|
||||
formData.value.customerId = customerId
|
||||
formData.value.customerDefault = true // 默认客户的选择,不允许变
|
||||
}
|
||||
// 自动关联 contactId 联系人编号
|
||||
if (contactId) {
|
||||
formData.value.contactId = contactId
|
||||
}
|
||||
}
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
// 加载商机状态类型列表
|
||||
businessStatusTypeList.value = await BusinessStatusTypeApi.getBusinessStatusTypeList()
|
||||
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 默认新建时选中自己
|
||||
if (formType.value === 'create') {
|
||||
formData.value.ownerUserId = useUserStore().getUser.id
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -186,6 +246,7 @@ const submitForm = async () => {
|
|||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
await productFormRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
|
@ -210,71 +271,18 @@ const resetForm = () => {
|
|||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
statusTypeId: undefined,
|
||||
statusId: undefined,
|
||||
contactNextTime: undefined,
|
||||
customerId: undefined,
|
||||
dealTime: undefined,
|
||||
price: undefined,
|
||||
discountPercent: undefined,
|
||||
productPrice: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
roUserIds: undefined,
|
||||
rwUserIds: undefined,
|
||||
endStatus: undefined,
|
||||
endRemark: undefined,
|
||||
contactLastTime: undefined,
|
||||
followUpStatus: undefined
|
||||
statusTypeId: undefined,
|
||||
dealTime: undefined,
|
||||
discountPercent: 0,
|
||||
totalProductPrice: undefined,
|
||||
totalPrice: undefined,
|
||||
remark: undefined,
|
||||
products: [],
|
||||
contactId: undefined,
|
||||
customerDefault: false
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 加载商机状态列表 */
|
||||
const changeBusinessStatusType = async (typeId: number) => {
|
||||
businessStatusList.value = await BusinessStatusTypeApi.getBusinessStatusListByTypeId(typeId)
|
||||
}
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
mobile: null,
|
||||
industryId: null,
|
||||
level: null,
|
||||
source: null,
|
||||
pool: false
|
||||
})
|
||||
// 选择客户
|
||||
const showCustomer = ref(false)
|
||||
const openCustomerSelect = () => {
|
||||
showCustomer.value = !showCustomer.value
|
||||
queryParams.pageNo = 1
|
||||
getCustomerList()
|
||||
}
|
||||
|
||||
/** 查询客户列表 */
|
||||
const getCustomerList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerApi.getCustomerPage(queryParams)
|
||||
customerList.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref()
|
||||
const handleSelectionChange = ({}, row) => {
|
||||
multipleSelection.value = row
|
||||
multipleTableRef.value!.clearSelection()
|
||||
multipleTableRef.value!.toggleRowSelection(row, undefined)
|
||||
}
|
||||
|
||||
const selectCustomer = () => {
|
||||
formData.value.customerId = multipleSelection.value.id
|
||||
formData.value.customerName = multipleSelection.value.name
|
||||
showCustomer.value = !showCustomer.value
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<template>
|
||||
<Dialog title="变更商机状态" v-model="dialogVisible" width="400">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="商机阶段" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择商机阶段" class="w-1/1">
|
||||
<el-option
|
||||
v-for="item in statusList"
|
||||
:key="item.id"
|
||||
:label="item.name + '(赢单率:' + item.percent + '%)'"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option
|
||||
v-for="item in BusinessStatusApi.DEFAULT_STATUSES"
|
||||
:key="item.endStatus"
|
||||
:label="item.name + '(赢单率:' + item.percent + '%)'"
|
||||
:value="-item.endStatus"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
statusId: undefined,
|
||||
endStatus: undefined,
|
||||
status: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
status: [{ required: true, message: '商机阶段不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const statusList = ref([]) // 商机状态列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (business: BusinessApi.BusinessVO) => {
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
formData.value = {
|
||||
id: business.id,
|
||||
statusId: business.statusId,
|
||||
endStatus: business.endStatus,
|
||||
status: business.endStatus != null ? -business.endStatus : business.statusId
|
||||
}
|
||||
// 加载状态列表
|
||||
formLoading.value = true
|
||||
try {
|
||||
statusList.value = await BusinessStatusApi.getBusinessStatusSimpleList(business.statusTypeId)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await BusinessApi.updateBusinessStatus({
|
||||
id: formData.value.id,
|
||||
statusId: formData.value.status > 0 ? formData.value.status : undefined,
|
||||
endStatus: formData.value.status < 0 ? -formData.value.status : undefined
|
||||
})
|
||||
message.success('更新商机状态成功')
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
statusId: undefined,
|
||||
endStatus: undefined,
|
||||
status: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
|
@ -38,7 +38,12 @@
|
|||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商机金额" align="center" prop="price" :formatter="fenToYuanFormat" />
|
||||
<el-table-column
|
||||
label="商机金额"
|
||||
align="center"
|
||||
prop="price"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
||||
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
||||
|
@ -66,8 +71,8 @@ import * as BusinessApi from '@/api/crm/business'
|
|||
import * as ContactApi from '@/api/crm/contact'
|
||||
import BusinessForm from './../BusinessForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import BusinessListModal from './BusinessListModal.vue'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const message = useMessage() // 消息
|
||||
|
||||
|
@ -76,6 +81,7 @@ const props = defineProps<{
|
|||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId?: number // 关联联系人与商机时,需要传入 customerId 进行筛选
|
||||
contactId?: number // 特殊:联系人编号;在【联系人】详情中,可以传递联系人编号,默认新建的商机关联到该联系人
|
||||
}>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
|
@ -125,7 +131,7 @@ const handleQuery = () => {
|
|||
/** 添加操作 */
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open('create')
|
||||
formRef.value.open('create', null, props.customerId, props.contactId)
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
|
|
|
@ -48,8 +48,8 @@
|
|||
<el-table-column
|
||||
label="商机金额"
|
||||
align="center"
|
||||
prop="price"
|
||||
:formatter="fenToYuanFormat"
|
||||
prop="totalPrice"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
||||
|
@ -75,7 +75,7 @@
|
|||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import BusinessForm from '../BusinessForm.vue'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
|
@ -99,6 +99,7 @@ const queryParams = reactive({
|
|||
/** 打开弹窗 */
|
||||
const open = async () => {
|
||||
dialogVisible.value = true
|
||||
queryParams.customerId = props.customerId // 解决 props.customerId 没更新到 queryParams 上的问题
|
||||
await getList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
@ -144,10 +145,10 @@ const submitForm = async () => {
|
|||
return message.error('未选择商机')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
emit('success', businessIds)
|
||||
emit('success', businessIds, businessRef.value.getSelectionRows())
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
/** 打开商机详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
v-loading="formLoading"
|
||||
label-width="0px"
|
||||
:inline-message="true"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<el-table :data="formData" class="-mt-10px">
|
||||
<el-table-column label="序号" type="index" align="center" width="60" />
|
||||
<el-table-column label="产品名称" min-width="180">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
|
||||
<el-select
|
||||
v-model="row.productId"
|
||||
clearable
|
||||
filterable
|
||||
@change="onChangeProduct($event, row)"
|
||||
placeholder="请选择产品"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in productList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-form-item class="mb-0px!">
|
||||
<el-input disabled v-model="row.productNo" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="价格(元)" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-form-item class="mb-0px!">
|
||||
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售价(元)" fixed="right" min-width="140">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.businessPrice`" class="mb-0px!">
|
||||
<el-input-number
|
||||
v-model="row.businessPrice"
|
||||
controls-position="right"
|
||||
:min="0.001"
|
||||
:precision="2"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
|
||||
<el-input-number
|
||||
v-model="row.count"
|
||||
controls-position="right"
|
||||
:min="0.001"
|
||||
:precision="3"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合计" prop="totalPrice" fixed="right" min-width="140">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
|
||||
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="{ $index }">
|
||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row justify="center" class="mt-3" v-if="!disabled">
|
||||
<el-button @click="handleAdd" round>+ 添加产品</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const props = defineProps<{
|
||||
products: undefined
|
||||
disabled: false
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
|
||||
businessPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
|
||||
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref([]) // 表单 Ref
|
||||
const productList = ref<ProductApi.ProductVO[]>([]) // 产品列表
|
||||
|
||||
/** 初始化设置产品项 */
|
||||
watch(
|
||||
() => props.products,
|
||||
async (val) => {
|
||||
formData.value = val
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 监听合同产品变化,计算合同产品总价 */
|
||||
watch(
|
||||
() => formData.value,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
// 循环处理
|
||||
val.forEach((item) => {
|
||||
if (item.businessPrice != null && item.count != null) {
|
||||
item.totalPrice = erpPriceMultiply(item.businessPrice, item.count)
|
||||
} else {
|
||||
item.totalPrice = undefined
|
||||
}
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const row = {
|
||||
id: undefined,
|
||||
productId: undefined,
|
||||
productUnit: undefined, // 产品单位
|
||||
productNo: undefined, // 产品条码
|
||||
productPrice: undefined, // 产品价格
|
||||
businessPrice: undefined,
|
||||
count: 1
|
||||
}
|
||||
formData.value.push(row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index: number) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 处理产品变更 */
|
||||
const onChangeProduct = (productId, row) => {
|
||||
const product = productList.value.find((item) => item.id === productId)
|
||||
if (product) {
|
||||
row.productUnit = product.unit
|
||||
row.productNo = product.no
|
||||
row.productPrice = product.price
|
||||
row.businessPrice = product.price
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
defineExpose({ validate })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
productList.value = await ProductApi.getProductSimpleList()
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<el-col>
|
||||
<el-row>
|
||||
<span class="text-xl font-bold">{{ business.name }}</span>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商机金额(元)">
|
||||
{{ erpPriceInputFormatter(business.totalPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商机组">{{ business.statusTypeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(business.createTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { business } = defineProps<{ business: BusinessApi.BusinessVO }>()
|
||||
</script>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="商机姓名">{{ business.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ business.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商机金额(元)">
|
||||
{{ erpPriceInputFormatter(business.totalPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="预计成交日期">
|
||||
{{ formatDate(business.dealTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{ formatDate(business.contactNextTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商机状态组">
|
||||
{{ business.statusTypeName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商机阶段">{{ business.statusName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ business.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="负责人">{{ business.ownerUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后跟进时间">
|
||||
{{ formatDate(business.contactLastTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label=""> </el-descriptions-item>
|
||||
<el-descriptions-item label=""> </el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ business.creatorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(business.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ formatDate(business.updateTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { business } = defineProps<{
|
||||
business: BusinessApi.BusinessVO
|
||||
}>()
|
||||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||
</script>
|
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-table :data="business.products" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="产品名称"
|
||||
fixed="left"
|
||||
prop="productName"
|
||||
min-width="160"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.productName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品条码" align="center" prop="productNo" min-width="120" />
|
||||
<el-table-column align="center" label="产品单位" prop="productUnit" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="产品价格(元)"
|
||||
align="center"
|
||||
prop="productPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="商机价格(元)"
|
||||
align="center"
|
||||
prop="businessPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="数量"
|
||||
prop="count"
|
||||
min-width="100px"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="合计金额(元)"
|
||||
align="center"
|
||||
prop="totalPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
</el-table>
|
||||
<el-row class="mt-10px" justify="end">
|
||||
<el-col :span="3"> 整单折扣:{{ erpPriceInputFormatter(business.discountPercent) }}% </el-col>
|
||||
<el-col :span="4">
|
||||
产品总金额:{{ erpPriceInputFormatter(business.totalProductPrice) }} 元
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const { business } = defineProps<{
|
||||
business: BusinessApi.BusinessVO
|
||||
}>()
|
||||
</script>
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<BusinessDetailsHeader v-loading="loading" :business="business">
|
||||
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', business.id)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="business.endStatus"
|
||||
v-if="permissionListRef?.validateWrite"
|
||||
type="success"
|
||||
@click="openStatusForm()"
|
||||
>
|
||||
变更商机状态
|
||||
</el-button>
|
||||
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
|
||||
转移
|
||||
</el-button>
|
||||
</BusinessDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="跟进记录">
|
||||
<FollowUpList :biz-id="businessId" :biz-type="BizTypeEnum.CRM_BUSINESS" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="详细资料">
|
||||
<BusinessDetailsInfo :business="business" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="联系人" lazy>
|
||||
<ContactList
|
||||
:biz-id="business.id!"
|
||||
:biz-type="BizTypeEnum.CRM_BUSINESS"
|
||||
:business-id="business.id"
|
||||
:customer-id="business.customerId"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="产品">
|
||||
<BusinessProductList :business="business" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="合同" lazy>
|
||||
<ContractList :biz-id="business.id!" :biz-type="BizTypeEnum.CRM_BUSINESS" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志">
|
||||
<OperateLogV2 :log-list="logList" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="团队成员">
|
||||
<PermissionList
|
||||
ref="permissionListRef"
|
||||
:biz-id="business.id!"
|
||||
:biz-type="BizTypeEnum.CRM_BUSINESS"
|
||||
:show-action="true"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<BusinessForm ref="formRef" @success="getBusiness(business.id)" />
|
||||
<BusinessUpdateStatusForm ref="statusFormRef" @success="getBusiness(business.id)" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import BusinessDetailsHeader from './BusinessDetailsHeader.vue'
|
||||
import BusinessDetailsInfo from './BusinessDetailsInfo.vue'
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
||||
import BusinessForm from '@/views/crm/business/BusinessForm.vue'
|
||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
||||
import ContactList from '@/views/crm/contact/components/ContactList.vue'
|
||||
import BusinessUpdateStatusForm from '@/views/crm/business/BusinessUpdateStatusForm.vue'
|
||||
import ContractList from '@/views/crm/contract/components/ContractList.vue'
|
||||
|
||||
defineOptions({ name: 'CrmBusinessDetail' })
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const businessId = ref(0) // 线索编号
|
||||
const loading = ref(true) // 加载中
|
||||
const business = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getBusiness = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
business.value = await BusinessApi.getBusiness(id)
|
||||
await getOperateLog(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 编辑 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 变更商机状态 */
|
||||
const statusFormRef = ref()
|
||||
const openStatusForm = () => {
|
||||
statusFormRef.value.open(business.value)
|
||||
}
|
||||
|
||||
/** 联系人转移 */
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
||||
const transfer = () => {
|
||||
transferFormRef.value?.open('商机转移', business.value.id, BusinessApi.transferBusiness)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
|
||||
const getOperateLog = async (contactId: number) => {
|
||||
if (!contactId) {
|
||||
return
|
||||
}
|
||||
const data = await getOperateLogPage({
|
||||
bizType: BizTypeEnum.CRM_BUSINESS,
|
||||
bizId: contactId
|
||||
})
|
||||
logList.value = data.list
|
||||
}
|
||||
|
||||
/** 关闭窗口 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const { params } = useRoute()
|
||||
onMounted(async () => {
|
||||
if (!params.id) {
|
||||
message.warning('参数错误,商机不能为空!')
|
||||
close()
|
||||
return
|
||||
}
|
||||
businessId.value = params.id as unknown as number
|
||||
await getBusiness(businessId.value)
|
||||
})
|
||||
</script>
|
|
@ -38,10 +38,37 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="我负责的" name="1" />
|
||||
<el-tab-pane label="我参与的" name="2" />
|
||||
<el-tab-pane label="下属负责的" name="3" />
|
||||
</el-tabs>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="商机名称" align="center" prop="name" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="商机金额" align="center" prop="price" />
|
||||
<el-table-column align="center" label="商机名称" fixed="left" prop="name" width="160">
|
||||
<template #default="scope">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="left" 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
|
||||
label="商机金额(元)"
|
||||
align="center"
|
||||
prop="totalPrice"
|
||||
width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="预计成交日期"
|
||||
align="center"
|
||||
|
@ -49,9 +76,23 @@
|
|||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="商机状态类型" align="center" prop="statusTypeName" />
|
||||
<el-table-column label="商机状态" align="center" prop="statusName" />
|
||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="下次联系时间"
|
||||
prop="contactNextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="最后跟进时间"
|
||||
prop="contactLastTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
|
@ -66,9 +107,21 @@
|
|||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId" />
|
||||
<el-table-column label="创建人" align="center" prop="creator" />
|
||||
<el-table-column label="跟进状态" align="center" prop="followUpStatus" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
||||
<el-table-column
|
||||
label="商机状态组"
|
||||
align="center"
|
||||
prop="statusTypeName"
|
||||
fixed="right"
|
||||
width="140"
|
||||
/>
|
||||
<el-table-column
|
||||
label="商机阶段"
|
||||
align="center"
|
||||
prop="statusName"
|
||||
fixed="right"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="130px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
|
@ -108,6 +161,8 @@ import { dateFormatter } from '@/utils/formatTime'
|
|||
import download from '@/utils/download'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import BusinessForm from './BusinessForm.vue'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'CrmBusiness' })
|
||||
|
||||
|
@ -120,27 +175,12 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
statusTypeId: null,
|
||||
statusId: null,
|
||||
contactNextTime: [],
|
||||
customerId: null,
|
||||
dealTime: [],
|
||||
price: null,
|
||||
discountPercent: null,
|
||||
productPrice: null,
|
||||
remark: null,
|
||||
ownerUserId: null,
|
||||
createTime: [],
|
||||
roUserIds: null,
|
||||
rwUserIds: null,
|
||||
endStatus: null,
|
||||
endRemark: null,
|
||||
contactLastTime: [],
|
||||
followUpStatus: null
|
||||
sceneType: '1', // 默认和 activeName 相等
|
||||
name: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const activeName = ref('1') // 列表 tab
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -166,6 +206,23 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
/** tab 切换 */
|
||||
const handleTabClick = (tab: TabsPaneContext) => {
|
||||
queryParams.sceneType = tab.paneName
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const { currentRoute, push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="状态类型名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入状态类型名" />
|
||||
<el-form-item label="状态组名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入状态组名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="应用部门" prop="deptIds">
|
||||
<template #label>
|
||||
<Tooltip message="不选择部门时,默认全公司生效" title="应用部门" />
|
||||
</template>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="deptList"
|
||||
|
@ -21,31 +24,55 @@
|
|||
show-checkbox
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态设置" prop="statusList">
|
||||
<el-table border style="width: 100%" :data="formData.statusList">
|
||||
<el-table-column align="center" label="状态" width="120" prop="star">
|
||||
<el-form-item label="阶段设置" prop="statuses">
|
||||
<el-table
|
||||
border
|
||||
style="width: 100%"
|
||||
:data="formData.statuses.concat(BusinessStatusApi.DEFAULT_STATUSES)"
|
||||
>
|
||||
<el-table-column align="center" label="阶段" width="70">
|
||||
<template #default="scope">
|
||||
<el-text>状态{{ scope.$index + 1 }}</el-text>
|
||||
<el-text v-if="!scope.row.defaultStatus">阶段 {{ scope.$index + 1 }}</el-text>
|
||||
<el-text v-else>结束</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="状态名称" width="120" prop="name">
|
||||
<el-table-column align="center" label="阶段名称" width="160" prop="name">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.name" placeholder="请输入状态名称" />
|
||||
<el-input v-if="!row.endStatus" v-model="row.name" placeholder="请输入状态名称" />
|
||||
<el-text v-else>{{ row.name }}</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="120" align="center" label="赢单率" prop="percent">
|
||||
<el-table-column width="140" align="center" label="赢单率(%)" prop="percent">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.percent" placeholder="请输入赢单率" />
|
||||
<el-input-number
|
||||
v-if="!row.endStatus"
|
||||
v-model="row.percent"
|
||||
placeholder="请输入赢单率"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="2"
|
||||
class="!w-1/1"
|
||||
/>
|
||||
<el-text v-else>{{ row.percent }}</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column label="操作" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="addStatusArea(scope.$index)"> 添加 </el-button>
|
||||
<el-button
|
||||
v-if="!scope.row.endStatus"
|
||||
link
|
||||
type="primary"
|
||||
@click="addStatus(scope.$index)"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!scope.row.endStatus"
|
||||
link
|
||||
type="danger"
|
||||
@click="deleteStatusArea(scope.$index)"
|
||||
v-show="scope.$index > 0"
|
||||
:disabled="formData.statuses.length <= 1"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
|
@ -61,7 +88,7 @@
|
|||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
|
||||
|
@ -71,15 +98,15 @@ const message = useMessage() // 消息弹窗
|
|||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formType = ref('') // 表单的组:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
deptIds: [],
|
||||
statusList: []
|
||||
statuses: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '状态类型名不能为空', trigger: 'blur' }]
|
||||
name: [{ required: true, message: '状态组名不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const deptList = ref<Tree[]>([]) // 树形结构
|
||||
|
@ -96,16 +123,16 @@ const open = async (type: string, id?: number) => {
|
|||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await BusinessStatusTypeApi.getBusinessStatusType(id)
|
||||
formData.value = await BusinessStatusApi.getBusinessStatus(id)
|
||||
treeRef.value.setCheckedKeys(formData.value.deptIds)
|
||||
if (formData.value.statusList.length == 0) {
|
||||
addStatusArea(0)
|
||||
if (formData.value.statuses.length == 0) {
|
||||
addStatus()
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
addStatusArea(0)
|
||||
addStatus()
|
||||
}
|
||||
// 加载部门树
|
||||
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||
|
@ -120,13 +147,13 @@ const submitForm = async () => {
|
|||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as BusinessStatusTypeApi.BusinessStatusTypeVO
|
||||
const data = formData.value as unknown as BusinessStatusApi.BusinessStatusTypeVO
|
||||
data.deptIds = treeRef.value.getCheckedKeys(false)
|
||||
if (formType.value === 'create') {
|
||||
await BusinessStatusTypeApi.createBusinessStatusType(data)
|
||||
await BusinessStatusApi.createBusinessStatus(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await BusinessStatusTypeApi.updateBusinessStatusType(data)
|
||||
await BusinessStatusApi.updateBusinessStatus(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
|
@ -144,24 +171,24 @@ const resetForm = () => {
|
|||
id: 0,
|
||||
name: '',
|
||||
deptIds: [],
|
||||
statusList: []
|
||||
statuses: []
|
||||
}
|
||||
treeRef.value?.setCheckedNodes([])
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 添加状态 */
|
||||
const addStatusArea = () => {
|
||||
const addStatus = () => {
|
||||
const data = formData.value
|
||||
data.statusList.push({
|
||||
data.statuses.push({
|
||||
name: '',
|
||||
percent: ''
|
||||
percent: undefined
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除状态 */
|
||||
const deleteStatusArea = (index: number) => {
|
||||
const data = formData.value
|
||||
data.statusList.splice(index, 1)
|
||||
data.statuses.splice(index, 1)
|
||||
}
|
||||
</script>
|
|
@ -9,25 +9,14 @@
|
|||
label-width="68px"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['crm:business-status-type:create']"
|
||||
v-hasPermi="['crm:business-status:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:business-status-type:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
@ -35,8 +24,15 @@
|
|||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="状态类型名" align="center" prop="name" />
|
||||
<el-table-column label="使用的部门" align="center" prop="deptNames" />
|
||||
<el-table-column label="状态组名" align="center" prop="name" />
|
||||
<el-table-column label="应用部门" align="center" prop="deptNames">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row?.deptNames?.length > 0">
|
||||
{{ scope.row.deptNames.join(' ') }}
|
||||
</span>
|
||||
<span v-else>全公司</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建人" align="center" prop="creator" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
|
@ -51,7 +47,7 @@
|
|||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:business-status-type:update']"
|
||||
v-hasPermi="['crm:business-status:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
|
@ -59,7 +55,7 @@
|
|||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:business-status-type:delete']"
|
||||
v-hasPermi="['crm:business-status:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
|
@ -76,16 +72,17 @@
|
|||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<BusinessStatusTypeForm ref="formRef" @success="getList" />
|
||||
<BusinessStatusForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||
import BusinessStatusTypeForm from './BusinessStatusTypeForm.vue'
|
||||
import * as BusinessStatusApi from '@/api/crm/business/status'
|
||||
import BusinessStatusForm from './BusinessStatusForm.vue'
|
||||
import { deleteBusinessStatus } from '@/api/crm/business/status'
|
||||
|
||||
defineOptions({ name: 'BusinessStatusType' })
|
||||
defineOptions({ name: 'CrmBusinessStatus' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
@ -104,7 +101,7 @@ const exportLoading = ref(false) // 导出的加载中
|
|||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await BusinessStatusTypeApi.getBusinessStatusTypePage(queryParams)
|
||||
const data = await BusinessStatusApi.getBusinessStatusPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
|
@ -130,40 +127,19 @@ const openForm = (type: string, id?: number) => {
|
|||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 选择客户操作 */
|
||||
const formCustomerRef = ref()
|
||||
const openCustomerForm = (id?: number) => {
|
||||
formCustomerRef.value.open(id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await BusinessStatusTypeApi.deleteBusinessStatusType(id)
|
||||
await BusinessStatusApi.deleteBusinessStatus(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await BusinessStatusTypeApi.exportBusinessStatusType(queryParams)
|
||||
download.excel(data, '商机状态类型.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
|
@ -33,7 +33,7 @@
|
|||
ref="permissionListRef"
|
||||
:biz-id="clue.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CLUE"
|
||||
:show-action="!permissionListRef?.isPool || false"
|
||||
:show-action="true"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" :width="820">
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" input-style="width:190px;" placeholder="请输入姓名" />
|
||||
<el-form-item label="联系人姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
lable-key="nickname"
|
||||
placeholder="请选择负责人"
|
||||
value-key="id"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
|
@ -33,12 +32,12 @@
|
|||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select
|
||||
:disabled="formData.customerDefault"
|
||||
v-model="formData.customerId"
|
||||
lable-key="name"
|
||||
placeholder="请选择客户"
|
||||
value-key="id"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
|
@ -50,113 +49,41 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input
|
||||
v-model="formData.mobile"
|
||||
input-style="width:190px;"
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="电话" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入电话" style="width: 215px" />
|
||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="formData.email"
|
||||
input-style="width:190px;"
|
||||
placeholder="请输入邮箱"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="formData.qq" placeholder="请输入QQ" style="width: 215px" />
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input
|
||||
v-model="formData.wechat"
|
||||
input-style="width:190px;"
|
||||
placeholder="请输入微信"
|
||||
/>
|
||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.contactNextTime"
|
||||
placeholder="选择下次联系时间"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
/>
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="formData.qq" placeholder="请输入 QQ" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所在地" prop="areaId">
|
||||
<el-tree-select
|
||||
v-model="formData.areaId"
|
||||
:data="areaList"
|
||||
:props="defaultProps"
|
||||
:render-after-expand="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="地址" prop="detailAddress">
|
||||
<el-input
|
||||
v-model="formData.detailAddress"
|
||||
input-style="width:190px;"
|
||||
placeholder="请输入地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="直属上级" prop="parentId">
|
||||
<el-select v-model="formData.parentId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in allContactList"
|
||||
:key="item.id"
|
||||
:disabled="item.id == formData.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职位" prop="post">
|
||||
<el-input v-model="formData.post" input-style="width:190px;" placeholder="请输入职位" />
|
||||
<el-input v-model="formData.post" placeholder="请输入职位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关键决策人" prop="master" style="width: 400px">
|
||||
<el-radio-group v-model="formData.master">
|
||||
|
@ -172,9 +99,66 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择" class="w-1/1">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="直属上级" prop="parentId">
|
||||
<el-select v-model="formData.parentId" placeholder="请选择直属上级" class="w-1/1">
|
||||
<el-option
|
||||
v-for="item in contactList"
|
||||
:key="item.id"
|
||||
:disabled="item.id == formData.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="地址" prop="areaId">
|
||||
<el-cascader
|
||||
v-model="formData.areaId"
|
||||
:options="areaList"
|
||||
:props="defaultProps"
|
||||
class="w-1/1"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择城市"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="详细地址" prop="detailAddress">
|
||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.contactNextTime"
|
||||
placeholder="选择下次联系时间"
|
||||
type="datetime"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -192,6 +176,7 @@ import * as UserApi from '@/api/system/user'
|
|||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as AreaApi from '@/api/system/area'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -202,25 +187,25 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
|||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const areaList = ref([]) // 地区列表
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
customerId: undefined,
|
||||
contactNextTime: undefined,
|
||||
ownerUserId: 0,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
customerName: undefined,
|
||||
detailAddress: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
post: undefined,
|
||||
qq: undefined,
|
||||
wechat: undefined,
|
||||
email: undefined,
|
||||
areaId: undefined,
|
||||
detailAddress: undefined,
|
||||
sex: undefined,
|
||||
master: false,
|
||||
areaId: undefined
|
||||
post: undefined,
|
||||
parentId: undefined,
|
||||
remark: undefined,
|
||||
businessId: undefined,
|
||||
customerDefault: false
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
||||
|
@ -228,22 +213,17 @@ const formRules = reactive({
|
|||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const ownerUserList = ref<any[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
// TODO 芋艿:统一的客户选择面板
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
const allContactList = ref<ContactApi.ContactVO[]>([]) // 所有联系人列表
|
||||
const contactList = ref<ContactApi.ContactVO[]>([]) // 联系人列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
const open = async (type: string, id?: number, customerId?: number, businessId?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
allContactList.value = await ContactApi.getSimpleContactList()
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
areaList.value = await AreaApi.getAreaTree()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
|
@ -252,6 +232,27 @@ const open = async (type: string, id?: number) => {
|
|||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
if (customerId) {
|
||||
formData.value.customerId = customerId
|
||||
formData.value.customerDefault = true // 默认客户的选择,不允许变
|
||||
}
|
||||
// 自动关联 businessId 商机编号
|
||||
if (businessId) {
|
||||
formData.value.businessId = businessId
|
||||
}
|
||||
}
|
||||
// 获得联系人列表
|
||||
contactList.value = await ContactApi.getSimpleContactList()
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
// 获得地区列表
|
||||
areaList.value = await AreaApi.getAreaTree()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 默认新建时选中自己
|
||||
if (formType.value === 'create') {
|
||||
formData.value.ownerUserId = useUserStore().getUser.id
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
@ -259,7 +260,6 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// owerSelectValue(ownerUserList)
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
|
@ -285,27 +285,27 @@ const submitForm = async () => {
|
|||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
// TODO zyna:ide 告警,看看怎么去掉哈;
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
customerId: undefined,
|
||||
contactNextTime: undefined,
|
||||
ownerUserId: 0,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
detailAddress: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
post: undefined,
|
||||
qq: undefined,
|
||||
wechat: undefined,
|
||||
email: undefined,
|
||||
areaId: undefined,
|
||||
detailAddress: undefined,
|
||||
sex: undefined,
|
||||
master: false
|
||||
master: false,
|
||||
post: undefined,
|
||||
parentId: undefined,
|
||||
remark: undefined,
|
||||
businessId: undefined,
|
||||
customerDefault: false
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
ownerUserList.value = []
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -5,11 +5,32 @@
|
|||
<Icon class="mr-5px" icon="system-uicons:contacts" />
|
||||
创建联系人
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="openBusinessModal"
|
||||
v-hasPermi="['crm:contact:create-business']"
|
||||
v-if="queryParams.businessId"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:circle-plus" />关联
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="deleteContactBusinessList"
|
||||
v-hasPermi="['crm:contact:delete-business']"
|
||||
v-if="queryParams.businessId"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:remove" />解除关联
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table
|
||||
ref="contactRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" v-if="queryParams.businessId" />
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
|
@ -20,12 +41,11 @@
|
|||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master">
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO 芋艿:【操作:设为首要联系人】 -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
|
@ -38,17 +58,26 @@
|
|||
|
||||
<!-- 表单弹窗:添加 -->
|
||||
<ContactForm ref="formRef" @success="getList" />
|
||||
<!-- 关联商机选择弹框 -->
|
||||
<ContactListModal
|
||||
ref="contactModalRef"
|
||||
:customer-id="props.customerId"
|
||||
@success="createContactBusinessList"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from './../ContactForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import ContactListModal from './ContactListModal.vue'
|
||||
|
||||
defineOptions({ name: 'CrmContactList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId: number // 特殊:客户编号;在【商机】详情中,可以传递客户编号,默认新建的联系人关联到该客户
|
||||
businessId: number // 特殊:商机编号;在【商机】详情中,可以传递商机编号,默认新建的联系人关联到该商机
|
||||
}>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
|
@ -57,8 +86,10 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerId: undefined as unknown // 允许 undefined + number
|
||||
customerId: undefined as unknown, // 允许 undefined + number
|
||||
businessId: undefined as unknown // 允许 undefined + number
|
||||
})
|
||||
const message = useMessage()
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -73,6 +104,10 @@ const getList = async () => {
|
|||
queryParams.customerId = props.bizId
|
||||
data = await ContactApi.getContactPageByCustomer(queryParams)
|
||||
break
|
||||
case BizTypeEnum.CRM_BUSINESS:
|
||||
queryParams.businessId = props.bizId
|
||||
data = await ContactApi.getContactPageByBusiness(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
@ -92,7 +127,7 @@ const handleQuery = () => {
|
|||
/** 添加操作 */
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open('create')
|
||||
formRef.value.open('create', undefined, props.customerId, props.businessId)
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
|
@ -101,6 +136,41 @@ const openDetail = (id: number) => {
|
|||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开联系人与商机的关联弹窗 */
|
||||
const contactModalRef = ref()
|
||||
const openBusinessModal = () => {
|
||||
contactModalRef.value.open()
|
||||
}
|
||||
const createContactBusinessList = async (contactIds: number[]) => {
|
||||
const data = {
|
||||
businessId: props.bizId,
|
||||
contactIds: contactIds
|
||||
} as ContactApi.ContactBusiness2ReqVO
|
||||
contactRef.value.getSelectionRows().forEach((row: ContactApi.ContactVO) => {
|
||||
data.businessIds.push(row.id)
|
||||
})
|
||||
await ContactApi.createContactBusinessList2(data)
|
||||
// 刷新列表
|
||||
message.success('关联联系人成功')
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 解除联系人与商机的关联 */
|
||||
const contactRef = ref()
|
||||
const deleteContactBusinessList = async () => {
|
||||
const data = {
|
||||
businessId: props.bizId,
|
||||
contactIds: contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
|
||||
} as ContactApi.ContactBusiness2ReqVO
|
||||
if (data.contactIds.length === 0) {
|
||||
return message.error('未选择联系人')
|
||||
}
|
||||
await ContactApi.deleteContactBusinessList2(data)
|
||||
// 刷新列表
|
||||
message.success('取关联系人成功')
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||
watch(
|
||||
() => [props.bizId, props.bizType],
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
<template>
|
||||
<Dialog title="关联联系人" v-model="dialogVisible">
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="90px"
|
||||
>
|
||||
<el-form-item label="联系人名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入联系人名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" @click="openForm()" v-hasPermi="['crm:business:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
ref="contactRef"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 表单弹窗:添加 -->
|
||||
<ContactForm ref="formRef" @success="getList" />
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from '../ContactForm.vue'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
customerId: number
|
||||
}>()
|
||||
defineOptions({ name: 'ContactListModal' })
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
customerId: props.customerId
|
||||
})
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async () => {
|
||||
dialogVisible.value = true
|
||||
queryParams.customerId = props.customerId // 解决 props.customerId 没更新到 queryParams 上的问题
|
||||
await getList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ContactApi.getContactPageByCustomer(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 openForm = () => {
|
||||
formRef.value.open('create')
|
||||
}
|
||||
|
||||
/** 关联联系人提交 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const contactRef = ref()
|
||||
const submitForm = async () => {
|
||||
const contactIds = contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
|
||||
if (contactIds.length === 0) {
|
||||
return message.error('未选择联系人')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
emit('success', contactIds, contactRef.value.getSelectionRows())
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
</script>
|
|
@ -16,17 +16,11 @@
|
|||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户">
|
||||
{{ contact.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">
|
||||
{{ contact.post }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">
|
||||
{{ contact.mobile }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ contact.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">{{ contact.post }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">{{ contact.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ contact.createTime ? formatDate(contact.createTime) : '空' }}
|
||||
{{ formatDate(contact.createTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
|
|
|
@ -6,60 +6,49 @@
|
|||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="姓名">
|
||||
{{ contact.name }}
|
||||
<el-descriptions-item label="姓名">{{ contact.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ contact.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">{{ contact.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电话">{{ contact.telephone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ contact.email }}</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">{{ contact.qq }}</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">{{ contact.wechat }}</el-descriptions-item>
|
||||
<el-descriptions-item label="地址">
|
||||
{{ contact.areaName }} {{ contact.detailAddress }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户">
|
||||
{{ contact.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">
|
||||
{{ contact.mobile }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="座机">
|
||||
{{ contact.telephone }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">
|
||||
{{ contact.email }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">
|
||||
{{ contact.qq }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">
|
||||
{{ contact.wechat }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{ contact.nextTime ? formatDate(contact.nextTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所在地">
|
||||
{{ contact.areaName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="详细地址">
|
||||
{{ contact.detailAddress }}
|
||||
<el-descriptions-item label="职务">{{ contact.post }}</el-descriptions-item>
|
||||
<el-descriptions-item label="直属上级">{{ contact.parentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关键决策人">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="contact.master" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="contact.sex" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">
|
||||
{{ contact.remark }}
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{ formatDate(contact.contactNextTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ contact.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ contact.ownerUserName }}
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="负责人">{{ contact.ownerUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后跟进记录">
|
||||
{{ contact.contactLastContent }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ contact.creatorName }}
|
||||
<el-descriptions-item label="最后跟进时间">
|
||||
{{ formatDate(contact.contactLastTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label=""> </el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ contact.creatorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ contact.createTime ? formatDate(contact.createTime) : '空' }}
|
||||
{{ formatDate(contact.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ contact.updateTime ? formatDate(contact.updateTime) : '空' }}
|
||||
{{ formatDate(contact.updateTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
</ContactDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="跟进记录">
|
||||
<FollowUpList :biz-id="contactId" :biz-type="BizTypeEnum.CRM_CONTACT" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="详细资料">
|
||||
<ContactDetailsInfo :contact="contact" />
|
||||
</el-tab-pane>
|
||||
|
@ -20,7 +23,7 @@
|
|||
ref="permissionListRef"
|
||||
:biz-id="contact.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
||||
:show-action="!permissionListRef?.isPool || false"
|
||||
:show-action="true"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
@ -29,13 +32,14 @@
|
|||
:biz-id="contact.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CONTACT"
|
||||
:customer-id="contact.customerId"
|
||||
:contact-id="contact.id"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ContactForm ref="formRef" @success="getContactData(contact.id)" />
|
||||
<CrmTransferForm ref="crmTransferFormRef" @success="close" />
|
||||
<ContactForm ref="formRef" @success="getContact(contact.id)" />
|
||||
<CrmTransferForm ref="transferFormRef" @success="close" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
|
@ -49,18 +53,19 @@ import { OperateLogV2VO } from '@/api/system/operatelog'
|
|||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
||||
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
||||
|
||||
defineOptions({ name: 'CrmContactDetail' })
|
||||
|
||||
const route = useRoute()
|
||||
const message = useMessage()
|
||||
const id = Number(route.params.id) // 联系人编号
|
||||
|
||||
const contactId = ref(0) // 线索编号
|
||||
const loading = ref(true) // 加载中
|
||||
const contact = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getContactData = async (id: number) => {
|
||||
const getContact = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
contact.value = await ContactApi.getContact(id)
|
||||
|
@ -77,9 +82,9 @@ const openForm = (type: string, id?: number) => {
|
|||
}
|
||||
|
||||
/** 联系人转移 */
|
||||
const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
|
||||
const transfer = () => {
|
||||
crmTransferFormRef.value?.open('联系人转移', contact.value.id, ContactApi.transferContact)
|
||||
transferFormRef.value?.open('联系人转移', contact.value.id, ContactApi.transferContact)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
|
@ -96,19 +101,21 @@ const getOperateLog = async (contactId: number) => {
|
|||
}
|
||||
|
||||
/** 关闭窗口 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const { params } = useRoute()
|
||||
onMounted(async () => {
|
||||
if (!id) {
|
||||
if (!params.id) {
|
||||
message.warning('参数错误,联系人不能为空!')
|
||||
close()
|
||||
return
|
||||
}
|
||||
await getContactData(id)
|
||||
contactId.value = params.id as unknown as number
|
||||
await getContact(contactId.value)
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -53,15 +53,6 @@
|
|||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input
|
||||
v-model="queryParams.qq"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入QQ"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input
|
||||
v-model="queryParams.wechat"
|
||||
|
@ -109,6 +100,11 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="我负责的" name="1" />
|
||||
<el-tab-pane label="我参与的" name="2" />
|
||||
<el-tab-pane label="下属负责的" name="3" />
|
||||
</el-tabs>
|
||||
<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">
|
||||
|
@ -224,6 +220,7 @@ import * as ContactApi from '@/api/crm/contact'
|
|||
import ContactForm from './ContactForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'CrmContact' })
|
||||
|
||||
|
@ -233,20 +230,21 @@ const { t } = useI18n() // 国际化
|
|||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
sceneType: '1', // 默认和 activeName 相等
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
name: undefined,
|
||||
qq: undefined,
|
||||
wechat: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const activeName = ref('1') // 列表 tab
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -272,6 +270,12 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
/** tab 切换 */
|
||||
const handleTabClick = (tab: TabsPaneContext) => {
|
||||
queryParams.sceneType = tab.paneName
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
|
|
|
@ -1,40 +1,68 @@
|
|||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="820">
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="1280">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="合同编号" prop="no">
|
||||
<el-input v-model="formData.no" placeholder="请输入合同编号" />
|
||||
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<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="12">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select v-model="formData.customerId">
|
||||
<el-select
|
||||
v-model="formData.customerId"
|
||||
placeholder="请选择客户"
|
||||
class="w-1/1"
|
||||
@change="handleCustomerChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customerList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="商机名称" prop="businessId">
|
||||
<el-select v-model="formData.businessId">
|
||||
<el-select
|
||||
@change="handleBusinessChange"
|
||||
:disabled="!formData.customerId"
|
||||
v-model="formData.businessId"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in businessList"
|
||||
v-for="item in getBusinessOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
|
@ -42,46 +70,48 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
</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="12">
|
||||
<el-form-item label="合同金额" prop="price">
|
||||
<el-input v-model="formData.price" placeholder="请输入合同金额" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<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="12">
|
||||
<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-col :span="12">
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="公司签约人" prop="signUserId">
|
||||
<el-select v-model="formData.signUserId">
|
||||
<el-select v-model="formData.signUserId" class="w-1/1">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id!"
|
||||
|
@ -89,61 +119,70 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户签约人" prop="contactId">
|
||||
<el-select v-model="formData.contactId" :disabled="!formData.customerId">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="客户签约人" prop="signContactId">
|
||||
<el-select
|
||||
v-model="formData.signContactId"
|
||||
:disabled="!formData.customerId"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in getContactOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select v-model="formData.ownerUserId">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-col :span="8">
|
||||
<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
|
||||
v-model="formData.remark"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
type="textarea"
|
||||
disabled
|
||||
v-model="formData.totalProductPrice"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- TODO @puhui999:productItems 改成 products 会更好点;因为它不是 item 哈 -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="产品列表" prop="productList">
|
||||
<ProductList v-model="formData.productItems" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="整单折扣(%)" prop="discountPercent">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="整单折扣(%)" prop="discountPercent">
|
||||
<el-input-number
|
||||
v-model="formData.discountPercent"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="0"
|
||||
placeholder="请输入整单折扣"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
class="!w-1/1"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品总金额(元)" prop="productPrice">
|
||||
{{ fenToYuan(formData.productPrice) }}
|
||||
<el-col :span="8">
|
||||
<el-form-item label="折扣后金额" prop="totalPrice">
|
||||
<el-input
|
||||
disabled
|
||||
v-model="formData.totalPrice"
|
||||
placeholder="请输入商机金额"
|
||||
:formatter="erpPriceTableColumnFormattere"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -160,8 +199,9 @@ 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 ProductList from './components/ProductList.vue'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { erpPriceMultiply, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import ContractProductForm from '@/views/crm/contract/components/ContractProductForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -170,30 +210,56 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
|||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref<ContractApi.ContractVO>({} as ContractApi.ContractVO)
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
no: undefined,
|
||||
name: undefined,
|
||||
customerId: undefined,
|
||||
businessId: undefined,
|
||||
orderDate: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
signUserId: undefined,
|
||||
signContactId: undefined,
|
||||
ownerUserId: undefined,
|
||||
discountPercent: 0,
|
||||
totalProductPrice: undefined,
|
||||
remark: undefined,
|
||||
products: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
|
||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
||||
orderDate: [{ required: true, message: '下单日期不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
|
||||
no: [{ required: true, message: '合同编号不能为空', trigger: 'blur' }]
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
// TODO 芋艿:统一的客户选择面板
|
||||
const customerList = ref([]) // 客户列表的数据
|
||||
const businessList = ref<BusinessApi.BusinessVO[]>([])
|
||||
const contactList = ref<ContactApi.ContactVO[]>([])
|
||||
|
||||
/** 监听合同产品变化,计算合同产品总价 */
|
||||
/** 子表的表单 */
|
||||
const subTabsName = ref('product')
|
||||
const productFormRef = ref()
|
||||
|
||||
/** 计算 discountPrice、totalPrice 价格 */
|
||||
watch(
|
||||
() => formData.value.productItems,
|
||||
() => formData.value,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
formData.value.productPrice = 0
|
||||
if (!val) {
|
||||
return
|
||||
}
|
||||
// 使用 reduce 函数进行累加
|
||||
formData.value.productPrice = val.reduce(
|
||||
(accumulator, currentValue) =>
|
||||
isNaN(accumulator + currentValue.totalPrice) ? 0 : accumulator + currentValue.totalPrice,
|
||||
0
|
||||
)
|
||||
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 }
|
||||
)
|
||||
|
@ -213,7 +279,18 @@ const open = async (type: string, id?: number) => {
|
|||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
await getAllApi()
|
||||
// 获得客户列表
|
||||
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()
|
||||
// 获得商机列表
|
||||
businessList.value = await BusinessApi.getSimpleBusinessList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -226,6 +303,7 @@ const submitForm = async () => {
|
|||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
productFormRef.value.validate()
|
||||
try {
|
||||
const data = unref(formData.value) as unknown as ContractApi.ContractVO
|
||||
if (formType.value === 'create') {
|
||||
|
@ -245,39 +323,48 @@ const submitForm = async () => {
|
|||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {} as ContractApi.ContractVO
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
no: undefined,
|
||||
name: undefined,
|
||||
customerId: undefined,
|
||||
businessId: undefined,
|
||||
orderDate: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
signUserId: undefined,
|
||||
signContactId: undefined,
|
||||
ownerUserId: undefined,
|
||||
discountPercent: 0,
|
||||
totalProductPrice: undefined,
|
||||
remark: undefined,
|
||||
products: []
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 获取其它相关数据 */
|
||||
const getAllApi = async () => {
|
||||
await Promise.all([getCustomerList(), getUserList(), getContactListList(), getBusinessList()])
|
||||
/** 处理切换客户 */
|
||||
const handleCustomerChange = () => {
|
||||
formData.value.businessId = undefined
|
||||
formData.value.signContactId = undefined
|
||||
formData.value.products = []
|
||||
}
|
||||
|
||||
/** 获取客户 */
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([])
|
||||
const getCustomerList = async () => {
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
/** 处理商机变化 */
|
||||
const handleBusinessChange = async (businessId: number) => {
|
||||
const business = await BusinessApi.getBusiness(businessId)
|
||||
business.products.forEach((item) => {
|
||||
item.contractPrice = item.businessPrice
|
||||
})
|
||||
formData.value.products = business.products
|
||||
}
|
||||
|
||||
/** 动态获取客户联系人 */
|
||||
const contactList = ref<ContactApi.ContactVO[]>([])
|
||||
const getContactOptions = computed(() =>
|
||||
contactList.value.filter((item) => item.customerId === formData.value.customerId)
|
||||
contactList.value.filter((item) => item.customerId == formData.value.customerId)
|
||||
)
|
||||
/** 动态获取商机 */
|
||||
const getBusinessOptions = computed(() =>
|
||||
businessList.value.filter((item) => item.customerId == formData.value.customerId)
|
||||
)
|
||||
const getContactListList = async () => {
|
||||
contactList.value = await ContactApi.getSimpleContactList()
|
||||
}
|
||||
|
||||
/** 获取用户列表 */
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const getUserList = async () => {
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
|
||||
/** 获取商机 */
|
||||
const businessList = ref<BusinessApi.BusinessVO[]>([])
|
||||
const getBusinessList = async () => {
|
||||
businessList.value = await BusinessApi.getSimpleBusinessList()
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
<el-table-column
|
||||
label="合同金额(元)"
|
||||
align="center"
|
||||
prop="price"
|
||||
:formatter="fenToYuanFormat"
|
||||
prop="totalPrice"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="开始时间"
|
||||
|
@ -61,9 +61,9 @@
|
|||
import * as ContractApi from '@/api/crm/contract'
|
||||
import ContractForm from './../ContractForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'CrmContractList' })
|
||||
const props = defineProps<{
|
||||
|
@ -93,6 +93,10 @@ const getList = async () => {
|
|||
queryParams.customerId = props.bizId
|
||||
data = await ContractApi.getContractPageByCustomer(queryParams)
|
||||
break
|
||||
case BizTypeEnum.CRM_BUSINESS:
|
||||
queryParams.businessId = props.bizId
|
||||
data = await ContractApi.getContractPageByBusiness(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
v-loading="formLoading"
|
||||
label-width="0px"
|
||||
:inline-message="true"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<el-table :data="formData" class="-mt-10px">
|
||||
<el-table-column label="序号" type="index" align="center" width="60" />
|
||||
<el-table-column label="产品名称" min-width="180">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
|
||||
<el-select
|
||||
v-model="row.productId"
|
||||
clearable
|
||||
filterable
|
||||
@change="onChangeProduct($event, row)"
|
||||
placeholder="请选择产品"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in productList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-form-item class="mb-0px!">
|
||||
<el-input disabled v-model="row.productNo" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="价格(元)" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-form-item class="mb-0px!">
|
||||
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售价(元)" fixed="right" min-width="140">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.contractPrice`" class="mb-0px!">
|
||||
<el-input-number
|
||||
v-model="row.contractPrice"
|
||||
controls-position="right"
|
||||
:min="0.001"
|
||||
:precision="2"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
|
||||
<el-input-number
|
||||
v-model="row.count"
|
||||
controls-position="right"
|
||||
:min="0.001"
|
||||
:precision="3"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合计" prop="totalPrice" fixed="right" min-width="140">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
|
||||
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="{ $index }">
|
||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row justify="center" class="mt-3" v-if="!disabled">
|
||||
<el-button @click="handleAdd" round>+ 添加产品</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const props = defineProps<{
|
||||
products: undefined
|
||||
disabled: false
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
|
||||
contractPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
|
||||
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref([]) // 表单 Ref
|
||||
const productList = ref<ProductApi.ProductVO[]>([]) // 产品列表
|
||||
|
||||
/** 初始化设置产品项 */
|
||||
watch(
|
||||
() => props.products,
|
||||
async (val) => {
|
||||
formData.value = val
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 监听合同产品变化,计算合同产品总价 */
|
||||
watch(
|
||||
() => formData.value,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
// 循环处理
|
||||
val.forEach((item) => {
|
||||
if (item.contractPrice != null && item.count != null) {
|
||||
item.totalPrice = erpPriceMultiply(item.contractPrice, item.count)
|
||||
} else {
|
||||
item.totalPrice = undefined
|
||||
}
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const row = {
|
||||
id: undefined,
|
||||
productId: undefined,
|
||||
productUnit: undefined, // 产品单位
|
||||
productNo: undefined, // 产品条码
|
||||
productPrice: undefined, // 产品价格
|
||||
contractPrice: undefined,
|
||||
count: 1
|
||||
}
|
||||
formData.value.push(row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index: number) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 处理产品变更 */
|
||||
const onChangeProduct = (productId, row) => {
|
||||
const product = productList.value.find((item) => item.id === productId)
|
||||
if (product) {
|
||||
row.productUnit = product.unit
|
||||
row.productNo = product.no
|
||||
row.productPrice = product.price
|
||||
row.contractPrice = product.price
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
defineExpose({ validate })
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
productList.value = await ProductApi.getProductSimpleList()
|
||||
})
|
||||
</script>
|
|
@ -1,171 +0,0 @@
|
|||
<!-- 合同 Form 表单下的 Product 列表 -->
|
||||
<template>
|
||||
<el-row justify="end">
|
||||
<el-button plain type="primary" @click="openForm">添加产品</el-button>
|
||||
</el-row>
|
||||
<el-table :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="产品名称" prop="name" width="120" />
|
||||
<el-table-column
|
||||
:formatter="fenToYuanFormat"
|
||||
align="center"
|
||||
label="价格"
|
||||
prop="price"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column align="center" label="产品类型" prop="categoryName" width="100" />
|
||||
<el-table-column align="center" label="产品单位" prop="unit">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="产品编码" prop="no" />
|
||||
<el-table-column align="center" fixed="right" label="数量" prop="count" width="100">
|
||||
<template #default="{ row }: { row: ProductApi.ProductExpandVO }">
|
||||
<el-input-number
|
||||
v-model="row.count"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
fixed="right"
|
||||
label="折扣(%)"
|
||||
prop="discountPercent"
|
||||
width="120"
|
||||
>
|
||||
<template #default="{ row }: { row: ProductApi.ProductExpandVO }">
|
||||
<el-input-number
|
||||
v-model="row.discountPercent"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="0"
|
||||
class="!w-100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="合计" prop="totalPrice" width="100">
|
||||
<template #default="{ row }: { row: ProductApi.ProductExpandVO }">
|
||||
{{ fenToYuan(getTotalPrice(row)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- table 选择表单 -->
|
||||
<TableSelectForm ref="tableSelectFormRef" v-model="multipleSelection" title="选择产品">
|
||||
<el-table-column align="center" label="产品名称" prop="name" width="160" />
|
||||
<el-table-column align="center" label="产品类型" prop="categoryName" width="160" />
|
||||
<el-table-column align="center" label="产品单位" prop="unit">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="产品编码" prop="no" />
|
||||
<el-table-column
|
||||
:formatter="fenToYuanFormat"
|
||||
align="center"
|
||||
label="价格(元)"
|
||||
prop="price"
|
||||
width="100"
|
||||
/>
|
||||
</TableSelectForm>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { TableSelectForm } from '@/components/Table/index'
|
||||
import { fenToYuan, floatToFixed2, yuanToFen } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'ProductList' })
|
||||
const props = withDefaults(defineProps<{ modelValue: ProductApi.ProductExpandVO[] }>(), {
|
||||
modelValue: () => []
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', v: any[]): void
|
||||
}>()
|
||||
|
||||
const list = ref<ProductApi.ProductExpandVO[]>([]) // 已添加的产品列表
|
||||
const multipleSelection = ref<ProductApi.ProductExpandVO[]>([]) // 多选
|
||||
|
||||
/** 处理删除 */
|
||||
const handleDelete = (id: number) => {
|
||||
const index = list.value.findIndex((item) => item.id === id)
|
||||
if (index !== -1) {
|
||||
list.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开 Product 弹窗 */
|
||||
const tableSelectFormRef = ref<InstanceType<typeof TableSelectForm>>()
|
||||
const openForm = () => {
|
||||
tableSelectFormRef.value?.open(ProductApi.getProductPage)
|
||||
}
|
||||
|
||||
/** 计算 totalPrice */
|
||||
const getTotalPrice = computed(() => (row: ProductApi.ProductExpandVO) => {
|
||||
const totalPrice =
|
||||
(Number(row.price) / 100) * Number(row.count) * (1 - Number(row.discountPercent) / 100)
|
||||
row.totalPrice = isNaN(totalPrice) ? 0 : yuanToFen(totalPrice)
|
||||
return isNaN(totalPrice) ? 0 : totalPrice.toFixed(2)
|
||||
})
|
||||
|
||||
/** 编辑时合同产品回显 */
|
||||
const isSetListValue = ref(false) // 判断是否已经给 list 赋值过,用于编辑表单产品回显
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (!val || val.length === 0 || isSetListValue.value) {
|
||||
return
|
||||
}
|
||||
list.value = [
|
||||
...props.modelValue.map((item) => {
|
||||
item.totalPrice = floatToFixed2(item.totalPrice) as unknown as number
|
||||
return item
|
||||
})
|
||||
]
|
||||
isSetListValue.value = true
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 监听列表变化,动态更新合同产品列表 */
|
||||
watch(
|
||||
list,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
emits('update:modelValue', list.value)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 监听产品选择结果动态添加产品到列表中,如果产品存在则不放入列表中
|
||||
watch(
|
||||
multipleSelection,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
// 过滤出不在列表中的产品
|
||||
const ids = list.value.map((item) => item.id)
|
||||
const productList = multipleSelection.value.filter((item) => ids.indexOf(item.id) === -1)
|
||||
if (!productList || productList.length === 0) {
|
||||
return
|
||||
}
|
||||
list.value.push(...productList)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
</script>
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="160px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-card shadow="never">
|
||||
<!-- 操作 -->
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<CardTitle title="合同配置设置" />
|
||||
<el-button type="primary" @click="onSubmit" v-hasPermi="['crm:contract-config:update']">
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 表单 -->
|
||||
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
||||
<el-radio-group
|
||||
v-model="formData.notifyEnabled"
|
||||
@change="changeNotifyEnable"
|
||||
class="ml-4"
|
||||
>
|
||||
<el-radio :label="false" size="large">不提醒</el-radio>
|
||||
<el-radio :label="true" size="large">提醒</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="formData.notifyEnabled">
|
||||
<el-form-item>
|
||||
提前 <el-input-number class="mx-2" v-model="formData.notifyDays" /> 天提醒
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ContractConfigApi from '@/api/crm/contract/config'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
defineOptions({ name: 'CrmContractConfig' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const formLoading = ref(false)
|
||||
const formData = ref({
|
||||
notifyEnabled: false,
|
||||
notifyDays: undefined
|
||||
})
|
||||
const formRules = reactive({})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 获取配置 */
|
||||
const getConfig = async () => {
|
||||
try {
|
||||
formLoading.value = true
|
||||
const data = await ContractConfigApi.getContractConfig()
|
||||
if (data === null) {
|
||||
return
|
||||
}
|
||||
formData.value = data
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交配置 */
|
||||
const onSubmit = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as ContractConfigApi.ContractConfigVO
|
||||
await ContractConfigApi.saveContractConfig(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
await getConfig()
|
||||
formLoading.value = false
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 更改提前提醒设置 */
|
||||
const changeNotifyEnable = () => {
|
||||
if (!formData.value.notifyEnabled) {
|
||||
formData.value.notifyDays = undefined
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getConfig()
|
||||
})
|
||||
</script>
|
|
@ -21,13 +21,13 @@
|
|||
{{ contract.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同金额(元)">
|
||||
{{ floatToFixed2(contract.price) }}
|
||||
{{ erpPriceInputFormatter(contract.totalPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下单时间">
|
||||
{{ contract.orderDate ? formatDate(contract.orderDate) : '空' }}
|
||||
{{ formatDate(contract.orderDate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款金额(元)">
|
||||
{{ floatToFixed2(contract.price) }}
|
||||
{{ erpPriceInputFormatter(contract.totalReceivablePrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ contract.ownerUserName }}
|
||||
|
@ -38,7 +38,7 @@
|
|||
<script lang="ts" setup>
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'ContractDetailsHeader' })
|
||||
defineProps<{ contract: ContractApi.ContractVO }>()
|
||||
|
|
|
@ -6,33 +6,25 @@
|
|||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="3">
|
||||
<el-descriptions-item label="合同编号">
|
||||
{{ contract.no }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同名称">
|
||||
{{ contract.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ contract.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="商机名称">
|
||||
{{ contract.businessName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="合同编号">{{ contract.no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合同名称">{{ contract.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ contract.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商机名称">{{ contract.businessName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合同金额(元)">
|
||||
{{ contract.price }}
|
||||
{{ erpPriceInputFormatter(contract.totalPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下单时间">
|
||||
{{ formatDate(contract.orderDate) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">
|
||||
<el-descriptions-item label="合同开始时间">
|
||||
{{ formatDate(contract.startTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">
|
||||
<el-descriptions-item label="合同结束时间">
|
||||
{{ formatDate(contract.endTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户签约人">
|
||||
{{ contract.contactName }}
|
||||
{{ contract.signContactName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="公司签约人">
|
||||
{{ contract.signUserName }}
|
||||
|
@ -41,7 +33,7 @@
|
|||
{{ contract.remark }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同状态">
|
||||
{{ contract.auditStatus }}
|
||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="contract.auditStatus" />
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
|
@ -49,18 +41,19 @@
|
|||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ contract.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ contract.creatorName }}
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="负责人">{{ contract.ownerUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最后跟进时间">
|
||||
{{ formatDate(contract.contactLastTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label=""> </el-descriptions-item>
|
||||
<el-descriptions-item label=""> </el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ contract.creatorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ contract.createTime ? formatDate(contract.createTime) : '空' }}
|
||||
{{ formatDate(contract.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ contract.updateTime ? formatDate(contract.updateTime) : '空' }}
|
||||
{{ formatDate(contract.updateTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
|
@ -70,6 +63,8 @@
|
|||
<script lang="ts" setup>
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'ContractDetailsInfo' })
|
||||
defineProps<{
|
||||
|
|
|
@ -1,67 +1,66 @@
|
|||
<!-- 合同详情:产品列表 -->
|
||||
<template>
|
||||
<el-table :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="产品名称" prop="name" width="160" />
|
||||
<el-table-column align="center" label="产品类型" prop="categoryName" width="160" />
|
||||
<el-table-column align="center" label="产品单位" prop="unit">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="产品编码" prop="no" />
|
||||
<el-table-column
|
||||
:formatter="fenToYuanFormat"
|
||||
align="center"
|
||||
label="价格(元)"
|
||||
prop="price"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column align="center" label="数量" prop="count" width="200" />
|
||||
<el-table-column align="center" label="折扣(%)" prop="discountPercent" width="200" />
|
||||
<el-table-column align="center" label="合计" prop="totalPrice" width="100">
|
||||
<template #default="{ row }: { row: ProductApi.ProductExpandVO }">
|
||||
{{ getTotalPrice(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ContentWrap>
|
||||
<el-table :data="contract.products" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="产品名称"
|
||||
fixed="left"
|
||||
prop="productName"
|
||||
min-width="160"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.productName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品条码" align="center" prop="productNo" min-width="120" />
|
||||
<el-table-column align="center" label="产品单位" prop="productUnit" min-width="160">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="产品价格(元)"
|
||||
align="center"
|
||||
prop="productPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="合同价格(元)"
|
||||
align="center"
|
||||
prop="contractPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="数量"
|
||||
prop="count"
|
||||
min-width="100px"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="合计金额(元)"
|
||||
align="center"
|
||||
prop="totalPrice"
|
||||
min-width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
</el-table>
|
||||
<el-row class="mt-10px" justify="end">
|
||||
<el-col :span="3"> 整单折扣:{{ erpPriceInputFormatter(contract.discountPercent) }}% </el-col>
|
||||
<el-col :span="4">
|
||||
产品总金额:{{ erpPriceInputFormatter(contract.totalProductPrice) }} 元
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script setup lang="ts">
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { floatToFixed2, yuanToFen } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'ContractProductList' })
|
||||
const props = withDefaults(defineProps<{ modelValue: ProductApi.ProductExpandVO[] }>(), {
|
||||
modelValue: () => []
|
||||
})
|
||||
const list = ref<ProductApi.ProductExpandVO[]>([]) // 产品列表
|
||||
|
||||
/** 计算 totalPrice */
|
||||
const getTotalPrice = computed(() => (row: ProductApi.ProductExpandVO) => {
|
||||
const totalPrice =
|
||||
(Number(row.price) / 100) * Number(row.count) * (1 - Number(row.discountPercent) / 100)
|
||||
row.totalPrice = isNaN(totalPrice) ? 0 : yuanToFen(totalPrice)
|
||||
return isNaN(totalPrice) ? 0 : totalPrice.toFixed(2)
|
||||
})
|
||||
|
||||
/** 编辑时合同产品回显 */
|
||||
const isSetListValue = ref(false) // 判断是否已经给 list 赋值过,用于编辑表单产品回显
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (!val || val.length === 0 || isSetListValue.value) {
|
||||
return
|
||||
}
|
||||
list.value = [
|
||||
...props.modelValue.map((item) => {
|
||||
item.totalPrice = floatToFixed2(item.totalPrice) as unknown as number
|
||||
return item
|
||||
})
|
||||
]
|
||||
isSetListValue.value = true
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
const { contract } = defineProps<{
|
||||
contract: ContractApi.ContractVO
|
||||
}>()
|
||||
</script>
|
||||
|
|
|
@ -10,22 +10,33 @@
|
|||
</ContractDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<!-- TODO @puhui999:跟进记录 -->
|
||||
<el-tab-pane label="跟进记录">
|
||||
<FollowUpList :biz-id="contract.id" :biz-type="BizTypeEnum.CRM_CONTRACT" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="基本信息">
|
||||
<ContractDetailsInfo :contract="contract" />
|
||||
</el-tab-pane>
|
||||
<!-- TODO @puhui999:products 更合适哈 -->
|
||||
<el-tab-pane label="产品">
|
||||
<ContractProductList v-model="contract.productItems" />
|
||||
<ContractProductList :contract="contract" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回款">
|
||||
<ReceivablePlanList
|
||||
:contract-id="contract.id!"
|
||||
:customer-id="contract.customerId"
|
||||
@create-receivable="createReceivable"
|
||||
/>
|
||||
<ReceivableList
|
||||
ref="receivableListRef"
|
||||
:contract-id="contract.id!"
|
||||
:customer-id="contract.customerId"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<!-- TODO @puhui999:回款信息 -->
|
||||
<!-- TODO @puhui999:这里是不是不用 isPool 哈 -->
|
||||
<el-tab-pane label="团队成员">
|
||||
<PermissionList
|
||||
ref="permissionListRef"
|
||||
:biz-id="contract.id!"
|
||||
:biz-type="BizTypeEnum.CRM_CONTRACT"
|
||||
:show-action="!permissionListRef?.isPool || false"
|
||||
:show-action="false"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
@ -43,16 +54,20 @@
|
|||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import ContractDetailsHeader from './ContractDetailsHeader.vue'
|
||||
import ContractDetailsInfo from './ContractDetailsInfo.vue'
|
||||
import ContractDetailsHeader from './ContractDetailsHeader.vue'
|
||||
import ContractProductList from './ContractProductList.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
||||
import ContractForm from '@/views/crm/contract/ContractForm.vue'
|
||||
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue'
|
||||
import FollowUpList from '@/views/crm/followup/index.vue'
|
||||
import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue'
|
||||
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue'
|
||||
|
||||
defineOptions({ name: 'CrmContractDetail' })
|
||||
const props = defineProps<{ id?: number }>()
|
||||
|
||||
const route = useRoute()
|
||||
const message = useMessage()
|
||||
|
@ -71,8 +86,8 @@ const openForm = (type: string, id?: number) => {
|
|||
const getContractData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
await getOperateLog(contractId.value)
|
||||
contract.value = await ContractApi.getContract(contractId.value)
|
||||
await getOperateLog(contractId.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
@ -91,8 +106,14 @@ const getOperateLog = async (contractId: number) => {
|
|||
logList.value = data.list
|
||||
}
|
||||
|
||||
/** 从回款计划创建回款 */
|
||||
const receivableListRef = ref<InstanceType<typeof ReceivableList>>() // 回款列表 Ref
|
||||
const createReceivable = (planData: any) => {
|
||||
receivableListRef.value?.createReceivable(planData)
|
||||
}
|
||||
|
||||
/** 转移 */
|
||||
// TODO @puhui999:这个组件,要不传递业务类型,然后组件里判断 title 和 api 能调用哪个;整体治理掉;
|
||||
// TODO @puhui999:这个组件,要不传递业务类型,然后组件里判断 title 和 api 能调用哪个;整体治理掉;好呢
|
||||
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
|
||||
const transferContract = () => {
|
||||
transferFormRef.value?.open('合同转移', contract.value.id, ContractApi.transferContract)
|
||||
|
@ -107,7 +128,7 @@ const close = () => {
|
|||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
const id = route.params.id
|
||||
const id = props.id || route.params.id
|
||||
if (!id) {
|
||||
message.warning('参数错误,合同不能为空!')
|
||||
close()
|
||||
|
|
|
@ -25,6 +25,24 @@
|
|||
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">
|
||||
|
@ -55,9 +73,20 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="我负责的" name="1" />
|
||||
<el-tab-pane label="我参与的" name="2" />
|
||||
<el-tab-pane label="下属负责的" name="3" />
|
||||
</el-tabs>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="130" />
|
||||
<el-table-column align="center" label="合同名称" prop="name" width="130" />
|
||||
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
|
||||
<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.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
|
@ -69,8 +98,24 @@
|
|||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @puhui999:做了商机详情后,可以把这个超链接加上 -->
|
||||
<el-table-column align="center" label="商机名称" prop="businessName" width="130" />
|
||||
<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="下单时间"
|
||||
|
@ -78,13 +123,6 @@
|
|||
width="120"
|
||||
:formatter="dateFormatter2"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同金额"
|
||||
prop="price"
|
||||
width="130"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="合同开始时间"
|
||||
|
@ -104,17 +142,41 @@
|
|||
<el-link
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openContactDetail(scope.row.contactId)"
|
||||
@click="openContactDetail(scope.row.signContactId)"
|
||||
>
|
||||
{{ scope.row.contactName }}
|
||||
{{ 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="130" />
|
||||
<!-- TODO @puhui999:后续可加 【已收款金额】、【未收款金额】 -->
|
||||
<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="creatorName" width="120" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
|
@ -129,6 +191,7 @@
|
|||
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" />
|
||||
|
@ -137,6 +200,7 @@
|
|||
<el-table-column fixed="right" label="操作" width="250">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.auditStatus === 0"
|
||||
v-hasPermi="['crm:contract:update']"
|
||||
link
|
||||
type="primary"
|
||||
|
@ -144,8 +208,8 @@
|
|||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<!-- TODO @puhui999:可以加下判断,什么情况下,可以审批;然后加个【查看审批】按钮 -->
|
||||
<el-button
|
||||
v-if="scope.row.auditStatus === 0"
|
||||
v-hasPermi="['crm:contract:update']"
|
||||
link
|
||||
type="primary"
|
||||
|
@ -153,6 +217,15 @@
|
|||
>
|
||||
提交审核
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
link
|
||||
v-hasPermi="['crm:contract:update']"
|
||||
type="primary"
|
||||
@click="handleProcessDetail(scope.row)"
|
||||
>
|
||||
查看审批
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:contract:query']"
|
||||
link
|
||||
|
@ -189,8 +262,10 @@ import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
|||
import download from '@/utils/download'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import ContractForm from './ContractForm.vue'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'CrmContract' })
|
||||
|
||||
|
@ -203,16 +278,22 @@ const list = ref([]) // 列表的数据
|
|||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
sceneType: '1', // 默认和 activeName 相等
|
||||
name: null,
|
||||
customerId: null,
|
||||
businessId: null,
|
||||
orderDate: [],
|
||||
no: null,
|
||||
discountPercent: null,
|
||||
productPrice: null
|
||||
no: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const activeName = ref('1') // 列表 tab
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
|
||||
/** tab 切换 */
|
||||
const handleTabClick = (tab: TabsPaneContext) => {
|
||||
queryParams.sceneType = tab.paneName
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -280,6 +361,11 @@ const handleSubmit = async (row: ContractApi.ContractVO) => {
|
|||
await getList()
|
||||
}
|
||||
|
||||
/** 查看审批 */
|
||||
const handleProcessDetail = (row: ContractApi.ContractVO) => {
|
||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
|
@ -296,8 +382,14 @@ const openContactDetail = (id: number) => {
|
|||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
const openBusinessDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
<!-- TODO @puhui999:这个好像和 detail 重复了???能不能复用 detail 哈? -->
|
||||
<template>
|
||||
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="110px">
|
||||
<el-row>
|
||||
<el-col :span="24" class="mb-10px">
|
||||
<CardTitle title="基本信息" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合同名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入合同名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合同编号" prop="no">
|
||||
<el-input v-model="formData.no" placeholder="请输入合同编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-select v-model="formData.customerId">
|
||||
<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="12">
|
||||
<el-form-item label="客户签约人" prop="contactId">
|
||||
<el-select v-model="formData.contactId" :disabled="!formData.customerId">
|
||||
<el-option
|
||||
v-for="item in getContactOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="公司签约人" prop="signUserId">
|
||||
<el-select v-model="formData.signUserId">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select v-model="formData.ownerUserId">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商机名称" prop="businessId">
|
||||
<el-select v-model="formData.businessId">
|
||||
<el-option
|
||||
v-for="item in businessList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合同金额(元)" prop="price">
|
||||
<el-input v-model="formData.price" placeholder="请输入合同金额" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下单日期" prop="orderDate">
|
||||
<el-date-picker
|
||||
v-model="formData.orderDate"
|
||||
placeholder="选择下单日期"
|
||||
type="date"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="formData.startTime"
|
||||
placeholder="选择开始时间"
|
||||
type="date"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="formData.endTime"
|
||||
placeholder="选择结束时间"
|
||||
type="date"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" :rows="3" placeholder="请输入备注" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="产品列表" prop="productList">
|
||||
<ProductList v-model="formData.productItems" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="整单折扣(%)" prop="discountPercent">
|
||||
<el-input v-model="formData.discountPercent" placeholder="请输入整单折扣" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品总金额(元)" prop="productPrice">
|
||||
<el-input v-model="formData.productPrice" placeholder="请输入产品总金额" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<CardTitle class="mb-10px" title="审批信息" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button
|
||||
class="m-20px"
|
||||
link
|
||||
type="primary"
|
||||
@click="BPMLModelRef?.handleBpmnDetail('contract-approve')"
|
||||
>
|
||||
查看工作流
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<BPMLModel ref="BPMLModelRef" />
|
||||
</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 ProductList from '@/views/crm/contract/components/ProductList.vue'
|
||||
// import BPMLModel from '@/views/crm/contract/components/BPMLModel.vue'
|
||||
|
||||
defineOptions({ name: 'ContractDetailOA' })
|
||||
const props = defineProps<{ id?: number }>()
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref<ContractApi.ContractVO>({} as ContractApi.ContractVO)
|
||||
const formRef = ref() // 表单 Ref
|
||||
const BPMLModelRef = ref<InstanceType<typeof BPMLModel>>()
|
||||
watch(
|
||||
() => formData.value.productItems,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
formData.value.productPrice = 0
|
||||
return
|
||||
}
|
||||
// 使用reduce函数进行累加
|
||||
formData.value.productPrice = val.reduce(
|
||||
(accumulator, currentValue) =>
|
||||
isNaN(accumulator + currentValue.totalPrice) ? 0 : accumulator + currentValue.totalPrice,
|
||||
0
|
||||
)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
/** 打开弹窗 */
|
||||
const getFormData = async () => {
|
||||
await getAllApi()
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ContractApi.getContract(props.id!)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
const getAllApi = async () => {
|
||||
await Promise.all([getCustomerList(), getUserList(), getContactListList(), getBusinessList()])
|
||||
}
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([])
|
||||
/** 获取客户 */
|
||||
const getCustomerList = async () => {
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
}
|
||||
const contactList = ref<ContactApi.ContactVO[]>([])
|
||||
/** 动态获取客户联系人 */
|
||||
const getContactOptions = computed(() =>
|
||||
contactList.value.filter((item) => item.customerId === formData.value.customerId)
|
||||
)
|
||||
const getContactListList = async () => {
|
||||
contactList.value = await ContactApi.getSimpleContactList()
|
||||
}
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
/** 获取用户列表 */
|
||||
const getUserList = async () => {
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
const businessList = ref<BusinessApi.BusinessVO[]>([])
|
||||
/** 获取商机 */
|
||||
const getBusinessList = async () => {
|
||||
businessList.value = await BusinessApi.getSimpleBusinessList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getFormData()
|
||||
})
|
||||
</script>
|
|
@ -4,7 +4,6 @@
|
|||
<el-upload
|
||||
ref="uploadRef"
|
||||
v-model:file-list="fileList"
|
||||
:action="importUrl + '?updateSupport=' + updateSupport"
|
||||
:auto-upload="false"
|
||||
:disabled="formLoading"
|
||||
:headers="uploadHeaders"
|
||||
|
@ -13,6 +12,7 @@
|
|||
:on-exceed="handleExceed"
|
||||
:on-success="submitFormSuccess"
|
||||
accept=".xlsx, .xls"
|
||||
action="none"
|
||||
drag
|
||||
>
|
||||
<Icon icon="ep:upload" />
|
||||
|
@ -45,6 +45,7 @@
|
|||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||
import download from '@/utils/download'
|
||||
import type { UploadUserFile } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'SystemUserImportForm' })
|
||||
|
||||
|
@ -53,11 +54,9 @@ const message = useMessage() // 消息弹窗
|
|||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const uploadRef = ref()
|
||||
const importUrl =
|
||||
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/crm/customer/import'
|
||||
const uploadHeaders = ref() // 上传 Header 头
|
||||
const fileList = ref([]) // 文件列表
|
||||
const updateSupport = ref(0) // 是否更新已经存在的客户数据
|
||||
const fileList = ref<UploadUserFile[]>([]) // 文件列表
|
||||
const updateSupport = ref(false) // 是否更新已经存在的客户数据
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = () => {
|
||||
|
@ -79,7 +78,11 @@ const submitForm = async () => {
|
|||
'tenant-id': getTenantId()
|
||||
}
|
||||
formLoading.value = true
|
||||
uploadRef.value!.submit()
|
||||
const formData = new FormData()
|
||||
formData.append('updateSupport', updateSupport.value)
|
||||
formData.append('file', fileList.value[0].raw)
|
||||
// TODO @芋艿:后面是不是可以采用这种形式,去掉 uploadHeaders
|
||||
await CustomerApi.handleImport(formData)
|
||||
}
|
||||
|
||||
/** 文件上传成功 */
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
>
|
||||
锁定
|
||||
</el-button>
|
||||
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleReceive"> 领取 </el-button>
|
||||
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleReceive"> 领取</el-button>
|
||||
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleDistributeForm">
|
||||
分配
|
||||
</el-button>
|
||||
|
@ -64,8 +64,8 @@
|
|||
<ContractList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回款" lazy>
|
||||
<ReceivablePlanList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
||||
<ReceivableList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
|
||||
<ReceivablePlanList :customer-id="customer.id!" @create-receivable="createReceivable" />
|
||||
<ReceivableList ref="receivableListRef" :customer-id="customer.id!" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志">
|
||||
<OperateLogV2 :log-list="logList" />
|
||||
|
@ -103,7 +103,7 @@ const customerId = ref(0) // 客户编号
|
|||
const loading = ref(true) // 加载中
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
|
@ -180,6 +180,7 @@ const handlePutPool = async () => {
|
|||
await message.confirm(`确定将客户【${customer.value.name}】放入公海吗?`)
|
||||
await CustomerApi.putCustomerPool(unref(customerId.value))
|
||||
message.success(`客户【${customer.value.name}】放入公海成功`)
|
||||
// 加载
|
||||
close()
|
||||
}
|
||||
|
||||
|
@ -196,8 +197,15 @@ const getOperateLog = async () => {
|
|||
logList.value = data.list
|
||||
}
|
||||
|
||||
/** 从回款计划创建回款 */
|
||||
const receivableListRef = ref<InstanceType<typeof ReceivableList>>() // 回款列表 Ref
|
||||
const createReceivable = (planData: any) => {
|
||||
receivableListRef.value?.createReceivable(planData)
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
push({ name: 'CrmCustomer' })
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="规则适用人群" prop="userIds">
|
||||
<el-tree-select
|
||||
v-model="formData.userIds"
|
||||
:data="userTree"
|
||||
:props="defaultProps"
|
||||
multiple
|
||||
filterable
|
||||
check-on-click-node
|
||||
node-key="id"
|
||||
placeholder="请选择规则适用人群"
|
||||
/>
|
||||
<el-select multiple filterable v-model="formData.userIds">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规则适用部门" prop="deptIds">
|
||||
<el-tree-select
|
||||
|
@ -62,6 +60,7 @@ import { defaultProps, handleTree } from '@/utils/tree'
|
|||
import * as UserApi from '@/api/system/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { LimitConfType } from '@/api/crm/customer/limitConfig'
|
||||
import { aw } from '../../../../../dist-prod/assets/index-9eac537b'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -85,7 +84,7 @@ const formRules = reactive({
|
|||
const formRef = ref() // 表单 Ref
|
||||
// TODO @芋艿:看看怎么搞个部门选择组件
|
||||
const deptTree = ref() // 部门树形结构
|
||||
const userTree = ref() // 用户树形结构
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, limitConfType: LimitConfType, id?: number) => {
|
||||
|
@ -105,9 +104,9 @@ const open = async (type: string, limitConfType: LimitConfType, id?: number) =>
|
|||
formData.value.type = limitConfType
|
||||
}
|
||||
// 获得部门树
|
||||
await getDeptTree()
|
||||
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||
// 获得用户
|
||||
await getUserTree()
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -149,76 +148,4 @@ const resetForm = () => {
|
|||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树
|
||||
*/
|
||||
const getDeptTree = async () => {
|
||||
const res = await DeptApi.getSimpleDeptList()
|
||||
deptTree.value = []
|
||||
deptTree.value.push(...handleTree(res))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户树
|
||||
*/
|
||||
const getUserTree = async () => {
|
||||
const res = await UserApi.getAllUser()
|
||||
userTree.value = []
|
||||
userTree.value = cloneDeep(unref(deptTree))
|
||||
|
||||
const deptUserMap = {}
|
||||
res.forEach((user) => {
|
||||
if (user.dept) {
|
||||
if (!deptUserMap[user.deptId]) {
|
||||
deptUserMap[user.deptId] = []
|
||||
}
|
||||
deptUserMap[user.deptId].push(user)
|
||||
}
|
||||
})
|
||||
|
||||
handleUserData(userTree.value, deptUserMap)
|
||||
}
|
||||
|
||||
// TODO @芋艿:看看怎么搞个用户选择的组件
|
||||
/**
|
||||
* 处理用户树
|
||||
*
|
||||
* @param deptTree
|
||||
* @param deptUserMap
|
||||
*/
|
||||
const handleUserData = (deptTree, deptUserMap) => {
|
||||
for (let i = 0; i < deptTree.length; i++) {
|
||||
// 如果是用户,就不用继续找部门下的用户
|
||||
if (deptTree[i].isUser) {
|
||||
continue
|
||||
}
|
||||
const users = deptUserMap[deptTree[i].id]
|
||||
if (users) {
|
||||
if (!deptTree[i].children) {
|
||||
deptTree[i].children = []
|
||||
}
|
||||
deptTree[i].children.push(
|
||||
...users.map((user) => {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.username + '-' + user.nickname,
|
||||
isUser: true,
|
||||
// 用户状态为关闭
|
||||
disabled: user.status === 1
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (deptTree[i].children && deptTree[i].children.length !== 0) {
|
||||
handleUserData(deptTree[i].children, deptUserMap)
|
||||
}
|
||||
|
||||
// 非人员选项禁用
|
||||
deptTree[i].disabled = true
|
||||
// 将非人员的 id 置为空
|
||||
deptTree[i].id = 'null'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
class="mt-4"
|
||||
>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="规则类型" align="center" prop="type" />
|
||||
<el-table-column
|
||||
label="规则适用人群"
|
||||
align="center"
|
||||
|
@ -39,6 +38,7 @@
|
|||
label="成交客户是否占用拥有客户数"
|
||||
align="center"
|
||||
prop="dealCountEnabled"
|
||||
min-width="100"
|
||||
>
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealCountEnabled" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!-- 跟进记录的添加表单弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
|
||||
<Dialog v-model="dialogVisible" title="添加跟进记录" width="50%">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
|
@ -36,32 +36,32 @@
|
|||
<el-input v-model="formData.content" :rows="3" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="图片" prop="content">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="图片" prop="picUrls">
|
||||
<UploadImgs v-model="formData.picUrls" class="min-w-80px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="附件" prop="content">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="附件" prop="fileUrls">
|
||||
<UploadFile v-model="formData.fileUrls" class="min-w-80px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
|
||||
<el-form-item label="关联联系人" prop="contactIds">
|
||||
<el-button @click="handleAddContact">
|
||||
<el-button @click="handleOpenContact">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
添加联系人
|
||||
</el-button>
|
||||
<contact-list v-model:contactIds="formData.contactIds" />
|
||||
<FollowUpRecordContactForm :contacts="formData.contacts" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
|
||||
<el-form-item label="关联商机" prop="businessIds">
|
||||
<el-button @click="handleAddBusiness">
|
||||
<el-button @click="handleOpenBusiness">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
添加商机
|
||||
</el-button>
|
||||
<business-list v-model:businessIds="formData.businessIds" />
|
||||
<FollowUpRecordBusinessForm :businesses="formData.businesses" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -71,13 +71,29 @@
|
|||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<ContactTableSelect ref="contactTableSelectRef" v-model="formData.contactIds" />
|
||||
<BusinessTableSelect ref="businessTableSelectRef" v-model="formData.businessIds" />
|
||||
|
||||
<!-- 弹窗 -->
|
||||
<ContactListModal
|
||||
ref="contactTableSelectRef"
|
||||
:customer-id="formData.bizId"
|
||||
@success="handleAddContact"
|
||||
/>
|
||||
<BusinessListModal
|
||||
ref="businessTableSelectRef"
|
||||
:customer-id="formData.bizId"
|
||||
@success="handleAddBusiness"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { FollowUpRecordApi, FollowUpRecordVO } from '@/api/crm/followup'
|
||||
import { BusinessList, BusinessTableSelect, ContactList, ContactTableSelect } from './components'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import FollowUpRecordBusinessForm from './components/FollowUpRecordBusinessForm.vue'
|
||||
import FollowUpRecordContactForm from './components/FollowUpRecordContactForm.vue'
|
||||
import BusinessListModal from '@/views/crm/business/components/BusinessListModal.vue'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
import ContactListModal from '@/views/crm/contact/components/ContactListModal.vue'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
|
||||
defineOptions({ name: 'FollowUpRecordForm' })
|
||||
|
||||
|
@ -87,8 +103,12 @@ const message = useMessage() // 消息弹窗
|
|||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref<FollowUpRecordVO>({} as FollowUpRecordVO)
|
||||
const formData = ref({
|
||||
bizType: undefined,
|
||||
bizId: undefined,
|
||||
businesses: [],
|
||||
contacts: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
type: [{ required: true, message: '跟进类型不能为空', trigger: 'change' }],
|
||||
content: [{ required: true, message: '跟进内容不能为空', trigger: 'blur' }],
|
||||
|
@ -98,22 +118,11 @@ const formRules = reactive({
|
|||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (bizType: number, bizId: number, type: string, id?: number) => {
|
||||
const open = async (bizType: number, bizId: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.bizType = bizType
|
||||
formData.value.bizId = bizId
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await FollowUpRecordApi.getFollowUpRecord(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -125,14 +134,13 @@ const submitForm = async () => {
|
|||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as FollowUpRecordVO
|
||||
if (formType.value === 'create') {
|
||||
await FollowUpRecordApi.createFollowUpRecord(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await FollowUpRecordApi.updateFollowUpRecord(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
const data = {
|
||||
...formData.value,
|
||||
contactIds: formData.value.contacts.map((item) => item.id),
|
||||
businessIds: formData.value.businesses.map((item) => item.id)
|
||||
} as unknown as FollowUpRecordVO
|
||||
await FollowUpRecordApi.createFollowUpRecord(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
|
@ -142,20 +150,39 @@ const submitForm = async () => {
|
|||
}
|
||||
|
||||
/** 关联联系人 */
|
||||
const contactTableSelectRef = ref<InstanceType<typeof ContactTableSelect>>()
|
||||
const handleAddContact = () => {
|
||||
const contactTableSelectRef = ref<InstanceType<typeof ContactListModal>>()
|
||||
const handleOpenContact = () => {
|
||||
contactTableSelectRef.value?.open()
|
||||
}
|
||||
const handleAddContact = (contactId: [], newContacts: ContactApi.ContactVO[]) => {
|
||||
newContacts.forEach((contact) => {
|
||||
if (!formData.value.contacts.some((item) => item.id === contact.id)) {
|
||||
formData.value.contacts.push(contact)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 关联商机 */
|
||||
const businessTableSelectRef = ref<InstanceType<typeof BusinessTableSelect>>()
|
||||
const handleAddBusiness = () => {
|
||||
const businessTableSelectRef = ref<InstanceType<typeof BusinessListModal>>()
|
||||
const handleOpenBusiness = () => {
|
||||
businessTableSelectRef.value?.open()
|
||||
}
|
||||
const handleAddBusiness = (businessId: [], newBusinesses: BusinessApi.BusinessVO[]) => {
|
||||
newBusinesses.forEach((business) => {
|
||||
if (!formData.value.businesses.some((item) => item.id === business.id)) {
|
||||
formData.value.businesses.push(business)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
formData.value = {} as FollowUpRecordVO
|
||||
formData.value = {
|
||||
bizId: undefined,
|
||||
bizType: undefined,
|
||||
businesses: [],
|
||||
contacts: []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
<template>
|
||||
<el-table :data="list" :show-overflow-tooltip="true" :stripe="true" height="200">
|
||||
<el-table-column align="center" label="商机名称" prop="name" />
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" />
|
||||
<el-table-column align="center" label="商机金额" prop="price" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="预计成交日期"
|
||||
prop="dealTime"
|
||||
width="120px"
|
||||
/>
|
||||
<el-table-column align="center" label="商机状态类型" prop="statusTypeName" width="120" />
|
||||
<el-table-column align="center" label="商机状态" prop="statusName" />
|
||||
<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="ownerUserName" width="120" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="130">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as BusinessApi from '@/api/crm/business'
|
||||
|
||||
defineOptions({ name: 'BusinessList' })
|
||||
const props = withDefaults(defineProps<{ businessIds: number[] }>(), {
|
||||
businessIds: () => []
|
||||
})
|
||||
const list = ref<BusinessApi.BusinessVO[]>([] as BusinessApi.BusinessVO[])
|
||||
watch(
|
||||
() => props.businessIds,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
list.value = BusinessApi.getBusinessListByIds(unref(val)) as unknown as BusinessApi.BusinessVO[]
|
||||
}
|
||||
)
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:businessIds', businessIds: number[]): void
|
||||
}>()
|
||||
const handleDelete = (id: number) => {
|
||||
const index = list.value.findIndex((item) => item.id === id)
|
||||
if (index !== -1) {
|
||||
list.value.splice(index, 1)
|
||||
}
|
||||
emits(
|
||||
'update:businessIds',
|
||||
list.value.map((item) => item.id)
|
||||
)
|
||||
}
|
||||
</script>
|
|
@ -1,88 +0,0 @@
|
|||
<!-- 商机的选择列表 TODO 芋艿:后面看看要不要搞到统一封装里 -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :appendToBody="true" title="选择商机" width="700">
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column align="center" label="商机名称" prop="name" />
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" />
|
||||
<el-table-column align="center" label="商机金额" prop="price" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="预计成交日期"
|
||||
prop="dealTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
</el-table>
|
||||
<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 BusinessApi from '@/api/crm/business'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { ElTable } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'BusinessTableSelect' })
|
||||
withDefaults(defineProps<{ modelValue: number[] }>(), { modelValue: () => [] })
|
||||
|
||||
const list = ref<BusinessApi.BusinessVO[]>([]) // 列表的数据
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false)
|
||||
|
||||
// 确认选择时的触发事件
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', v: number[]): void
|
||||
}>()
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref<BusinessApi.BusinessVO[]>([])
|
||||
const handleSelectionChange = (val: BusinessApi.BusinessVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
/** 触发 */
|
||||
const submitForm = () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
emits(
|
||||
'update:modelValue',
|
||||
multipleSelection.value.map((item) => item.id)
|
||||
)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
// 关闭弹窗
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
list.value = await BusinessApi.getSimpleBusinessList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async () => {
|
||||
dialogVisible.value = true
|
||||
await nextTick()
|
||||
if (multipleSelection.value.length > 0) {
|
||||
multipleTableRef.value!.clearSelection()
|
||||
}
|
||||
await getList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
|
@ -1,97 +0,0 @@
|
|||
<template>
|
||||
<el-table :data="list" :show-overflow-tooltip="true" :stripe="true" height="200">
|
||||
<el-table-column align="center" fixed="left" label="姓名" prop="name" width="140" />
|
||||
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="120" />
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="电话" prop="telephone" width="120" />
|
||||
<el-table-column align="center" label="邮箱" prop="email" width="120" />
|
||||
<el-table-column align="center" label="职位" prop="post" width="120" />
|
||||
<el-table-column align="center" label="地址" prop="detailAddress" width="120" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="下次联系时间"
|
||||
prop="contactNextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="关键决策人" prop="master" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="直属上级" prop="parentName" width="140" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="最后跟进时间"
|
||||
prop="contactLastTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="性别" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
|
||||
<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="remark" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="130">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
|
||||
defineOptions({ name: 'ContactList' })
|
||||
const props = withDefaults(defineProps<{ contactIds: number[] }>(), {
|
||||
contactIds: () => []
|
||||
})
|
||||
const list = ref<ContactApi.ContactVO[]>([] as ContactApi.ContactVO[])
|
||||
const getContactList = async () => {
|
||||
list.value = (await ContactApi.getContactListByIds(
|
||||
unref(props.contactIds)
|
||||
)) as unknown as ContactApi.ContactVO[]
|
||||
}
|
||||
watch(
|
||||
() => props.contactIds,
|
||||
(val) => {
|
||||
if (!val || val.length === 0) {
|
||||
return
|
||||
}
|
||||
getContactList()
|
||||
}
|
||||
)
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:contactIds', contactIds: number[]): void
|
||||
}>()
|
||||
const handleDelete = (id: number) => {
|
||||
const index = list.value.findIndex((item) => item.id === id)
|
||||
if (index !== -1) {
|
||||
list.value.splice(index, 1)
|
||||
}
|
||||
emits(
|
||||
'update:contactIds',
|
||||
list.value.map((item) => item.id)
|
||||
)
|
||||
}
|
||||
</script>
|
|
@ -1,87 +0,0 @@
|
|||
<!-- 联系人的选择列表 TODO 芋艿:后面看看要不要搞到统一封装里 -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :appendToBody="true" title="选择联系人" width="700">
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column align="center" fixed="left" label="姓名" prop="name" width="140" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
fixed="left"
|
||||
label="客户名称"
|
||||
prop="customerName"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="电话" prop="telephone" width="120" />
|
||||
<el-table-column align="center" label="邮箱" prop="email" width="120" />
|
||||
<el-table-column align="center" label="职位" prop="post" width="120" />
|
||||
</el-table>
|
||||
<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 ContactApi from '@/api/crm/contact'
|
||||
import { ElTable } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'ContactTableSelect' })
|
||||
withDefaults(defineProps<{ modelValue: number[] }>(), { modelValue: () => [] })
|
||||
|
||||
const list = ref<ContactApi.ContactVO[]>([]) // 列表的数据
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const formLoading = ref(false)
|
||||
|
||||
// 确认选择时的触发事件
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', v: number[]): void
|
||||
}>()
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref<ContactApi.ContactVO[]>([])
|
||||
const handleSelectionChange = (val: ContactApi.ContactVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
/** 触发 */
|
||||
const submitForm = () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
emits(
|
||||
'update:modelValue',
|
||||
multipleSelection.value.map((item) => item.id)
|
||||
)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
// 关闭弹窗
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
list.value = await ContactApi.getSimpleContactList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async () => {
|
||||
dialogVisible.value = true
|
||||
await nextTick()
|
||||
if (multipleSelection.value.length > 0) {
|
||||
multipleTableRef.value!.clearSelection()
|
||||
}
|
||||
await getList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<el-table :data="formData" :show-overflow-tooltip="true" :stripe="true" height="120">
|
||||
<el-table-column label="商机名称" fixed="left" align="center" prop="name" />
|
||||
<el-table-column
|
||||
label="商机金额"
|
||||
align="center"
|
||||
prop="totalPrice"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
||||
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="80">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="handleDelete($index)"> 移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
businesses: undefined
|
||||
}>()
|
||||
const formData = ref([])
|
||||
|
||||
/** 初始化商机列表 */
|
||||
watch(
|
||||
() => props.businesses,
|
||||
async (val) => {
|
||||
formData.value = val
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index: number) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,47 @@
|
|||
<template>
|
||||
<el-table :data="contacts" :show-overflow-tooltip="true" :stripe="true" height="150">
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="master" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="130">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const props = defineProps<{
|
||||
contacts: undefined
|
||||
}>()
|
||||
const formData = ref([])
|
||||
|
||||
/** 初始化联系人列表 */
|
||||
watch(
|
||||
() => props.contacts,
|
||||
async (val) => {
|
||||
formData.value = val
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index: number) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
</script>
|
|
@ -1,6 +0,0 @@
|
|||
import BusinessList from './BusinessList.vue'
|
||||
import BusinessTableSelect from './BusinessTableSelect.vue'
|
||||
import ContactList from './ContactList.vue'
|
||||
import ContactTableSelect from './ContactTableSelect.vue'
|
||||
|
||||
export { BusinessList, BusinessTableSelect, ContactList, ContactTableSelect }
|
|
@ -2,7 +2,7 @@
|
|||
<template>
|
||||
<!-- 操作栏 -->
|
||||
<el-row class="mb-10px" justify="end">
|
||||
<el-button @click="openForm('create')">
|
||||
<el-button @click="openForm">
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
写跟进
|
||||
</el-button>
|
||||
|
@ -10,8 +10,13 @@
|
|||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<!-- TODO @puhui999:展示不出来 -->
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="跟进人" prop="creatorName" />
|
||||
<el-table-column align="center" label="跟进类型" prop="type">
|
||||
<template #default="scope">
|
||||
|
@ -26,35 +31,47 @@
|
|||
prop="nextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<!-- TODO @puhui999:点击后,查看关联联系人 -->
|
||||
<el-table-column align="center" label="关联联系人" prop="contactIds" />
|
||||
<!-- TODO @puhui999:点击后,查看关联商机 -->
|
||||
<el-table-column align="center" label="关联商机" prop="businessIds" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
label="关联联系人"
|
||||
prop="contactIds"
|
||||
v-if="bizType === BizTypeEnum.CRM_CUSTOMER"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
v-for="contact in scope.row.contacts"
|
||||
:key="`key-${contact.id}`"
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openContactDetail(contact.id)"
|
||||
class="ml-5px"
|
||||
>
|
||||
{{ contact.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="关联商机"
|
||||
prop="businessIds"
|
||||
v-if="bizType === BizTypeEnum.CRM_CUSTOMER"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
v-for="business in scope.row.businesses"
|
||||
:key="`key-${business.id}`"
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openBusinessDetail(business.id)"
|
||||
class="ml-5px"
|
||||
>
|
||||
{{ business.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['crm:follow-up-record:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:follow-up-record:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -76,6 +93,7 @@ import { dateFormatter } from '@/utils/formatTime'
|
|||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { FollowUpRecordApi, FollowUpRecordVO } from '@/api/crm/followup'
|
||||
import FollowUpRecordForm from './FollowUpRecordForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
|
||||
/** 跟进记录列表 */
|
||||
defineOptions({ name: 'FollowUpRecord' })
|
||||
|
@ -110,8 +128,8 @@ const getList = async () => {
|
|||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref<InstanceType<typeof FollowUpRecordForm>>()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value?.open(props.bizType, props.bizId, type, id)
|
||||
const openForm = () => {
|
||||
formRef.value?.open(props.bizType, props.bizId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
|
@ -127,6 +145,17 @@ const handleDelete = async (id: number) => {
|
|||
} catch {}
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
const { push } = useRouter()
|
||||
const openContactDetail = (id: number) => {
|
||||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
const openBusinessDetail = (id: number) => {
|
||||
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.bizId,
|
||||
() => {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- TODO @puhui999:同时添加至,还没想好下次搞 -->
|
||||
<el-form-item v-if="formType === 'create'" label="同时添加至" prop="toBizType">
|
||||
<el-form-item v-if="false && formType === 'create'" label="同时添加至" prop="toBizType">
|
||||
<el-select v-model="formData.userId">
|
||||
<el-option :value="1" label="联系人" />
|
||||
<el-option :value="1" label="商机" />
|
||||
|
|
|
@ -100,11 +100,10 @@
|
|||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/productCategory'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/category'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import { getSimpleUserList, UserVO } from '@/api/system/user'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { fenToYuan, yuanToFen } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'CrmProductForm' })
|
||||
|
||||
|
@ -149,7 +148,6 @@ const open = async (type: string, id?: number) => {
|
|||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ProductApi.getProduct(id)
|
||||
formData.value.price = Number(fenToYuan(formData.value.price))
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
|
@ -169,10 +167,7 @@ const submitForm = async () => {
|
|||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = {
|
||||
...formData.value,
|
||||
price: yuanToFen(formData.value.price)
|
||||
} as unknown as ProductApi.ProductVO
|
||||
const data = formData.value as unknown as ProductApi.ProductVO
|
||||
if (formType.value === 'create') {
|
||||
await ProductApi.createProduct(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ProductCategoryApi from '@/api/crm/product/productCategory'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/category'
|
||||
|
||||
defineOptions({ name: 'CrmProductCategoryForm' })
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/productCategory'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/category'
|
||||
import ProductCategoryForm from './ProductCategoryForm.vue'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="产品类别">
|
||||
{{ productCategoryList?.find((c) => c.id === product.categoryId)?.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类别">{{ product.categoryName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品单位">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="product.unit" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品价格">{{ fenToYuan(product.price) }}元</el-descriptions-item>
|
||||
<el-descriptions-item label="产品价格">
|
||||
{{ erpPriceInputFormatter(product.price) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品编码">{{ product.no }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
|
@ -34,9 +34,8 @@
|
|||
<script setup lang="ts">
|
||||
import ProductForm from '@/views/crm/product/ProductForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/productCategory'
|
||||
|
||||
// 操作修改
|
||||
const formRef = ref()
|
||||
|
@ -44,12 +43,4 @@ const openForm = (type: string, id?: number) => {
|
|||
formRef.value.open(type, id)
|
||||
}
|
||||
const { product } = defineProps<{ product: ProductApi.ProductVO }>()
|
||||
const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调
|
||||
|
||||
/** 初始化 */
|
||||
const productCategoryList = ref([]) // 产品分类树
|
||||
|
||||
onMounted(async () => {
|
||||
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({})
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="产品名称">{{ product.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品编码">{{ product.no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="价格">{{ fenToYuan(product.price) }}元</el-descriptions-item>
|
||||
<el-descriptions-item label="产品描述">{{ product.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型">
|
||||
{{ productCategoryList?.find((c) => c.id === product.categoryId)?.name }}
|
||||
<el-descriptions-item label="价格">
|
||||
{{ erpPriceInputFormatter(product.price) }} 元
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品描述">{{ product.description }}</el-descriptions-item>
|
||||
<el-descriptions-item label="产品类型">{{ product.categoryName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否上下架">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="product.status" />
|
||||
</el-descriptions-item>
|
||||
|
@ -27,8 +27,7 @@
|
|||
<script setup lang="ts">
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import * as ProductCategoryApi from '@/api/crm/product/productCategory'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { product } = defineProps<{
|
||||
product: ProductApi.ProductVO
|
||||
|
@ -36,10 +35,4 @@ const { product } = defineProps<{
|
|||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo'])
|
||||
|
||||
/** 初始化 */
|
||||
const productCategoryList = ref([]) // 产品分类树
|
||||
onMounted(async () => {
|
||||
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({})
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
label="价格(元)"
|
||||
align="center"
|
||||
prop="price"
|
||||
:formatter="fenToYuanFormat"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column label="产品描述" align="center" prop="description" width="150" />
|
||||
|
@ -133,7 +133,7 @@ import { dateFormatter } from '@/utils/formatTime'
|
|||
import download from '@/utils/download'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import ProductForm from './ProductForm.vue'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'CrmProduct' })
|
||||
|
||||
|
|
|
@ -1,132 +1,182 @@
|
|||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="回款编号" prop="no">
|
||||
<el-input v-model="formData.no" placeholder="请输入回款编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="回款计划" prop="planId">
|
||||
<el-input v-model="formData.planId" placeholder="请输入回款计划" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同名称" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="审批状态" prop="checkStatus">
|
||||
<el-select v-model="formData.checkStatus" placeholder="请选择审批状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_RECEIVABLE_CHECK_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>-->
|
||||
<!--<el-form-item label="工作流编号" prop="processInstanceId">
|
||||
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
|
||||
</el-form-item>-->
|
||||
<el-form-item label="回款日期" prop="returnTime">
|
||||
<el-date-picker
|
||||
v-model="formData.returnTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择回款日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="回款方式" prop="returnType">
|
||||
<el-select v-model="formData.returnType" placeholder="请选择回款方式">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="回款金额" prop="price">
|
||||
<el-input-number v-model="formData.price" placeholder="请输入回款金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select v-model="formData.ownerUserId" clearable placeholder="请输入负责人">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="批次" prop="batchId">
|
||||
<el-input-number v-model="formData.batchId" placeholder="请输入批次" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>-->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" :rows="3" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款编号" prop="no">
|
||||
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select
|
||||
v-model="formData.customerId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleCustomerChange"
|
||||
placeholder="请选择客户"
|
||||
>
|
||||
<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="12">
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-select
|
||||
v-model="formData.contractId"
|
||||
:disabled="formType !== 'create' || !formData.customerId"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleContractChange"
|
||||
placeholder="请选择合同"
|
||||
>
|
||||
<el-option
|
||||
v-for="data in contractList"
|
||||
:key="data.id"
|
||||
:label="data.name"
|
||||
:value="data.id!"
|
||||
:disabled="data.auditStatus !== 20"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款期数" prop="planId">
|
||||
<el-select
|
||||
v-model="formData.planId"
|
||||
:disabled="formType !== 'create' || !formData.contractId"
|
||||
class="!w-1/1"
|
||||
@change="handleReceivablePlanChange"
|
||||
placeholder="请选择回款期数"
|
||||
>
|
||||
<el-option
|
||||
v-for="data in receivablePlanList"
|
||||
:key="data.id"
|
||||
:label="'第 ' + data.period + ' 期'"
|
||||
:value="data.id!"
|
||||
:disabled="data.receivableId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款方式" prop="returnType">
|
||||
<el-select v-model="formData.returnType" class="w-1/1" placeholder="请选择回款方式">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款金额" prop="price">
|
||||
<el-input-number
|
||||
v-model="formData.price"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
placeholder="请输入回款金额"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款日期" prop="returnTime">
|
||||
<el-date-picker
|
||||
v-model="formData.returnTime"
|
||||
placeholder="选择回款日期"
|
||||
type="date"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import form from '@/components/Form/src/Form.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
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,
|
||||
planId: undefined,
|
||||
customerId: undefined,
|
||||
contractId: undefined,
|
||||
checkStatus: undefined,
|
||||
processInstanceId: undefined,
|
||||
returnTime: undefined,
|
||||
returnType: undefined,
|
||||
price: undefined,
|
||||
ownerUserId: undefined,
|
||||
batchId: undefined,
|
||||
sort: undefined,
|
||||
dataScope: undefined,
|
||||
dataScopeDeptIds: undefined,
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
const formData = ref<ReceivableApi.ReceivableVO>({} as ReceivableApi.ReceivableVO)
|
||||
const formRules = reactive({
|
||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
||||
contractId: [{ required: true, message: '合同不能为空', trigger: 'blur' }],
|
||||
returnTime: [{ required: true, message: '回款日期不能为空', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '回款金额不能为空', trigger: 'blur' }]
|
||||
})
|
||||
// const formRules = reactive({
|
||||
// status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
// })
|
||||
const formRef = ref() // 表单 Ref
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
const contractList = ref<ContractApi.ContractVO[]>([]) // 合同列表
|
||||
const receivablePlanList = ref<ReceivablePlanApi.ReceivablePlanVO[]>([]) // 回款计划列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
const open = async (
|
||||
type: string,
|
||||
id?: number,
|
||||
receivablePlan?: ReceivablePlanApi.ReceivablePlanVO
|
||||
) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
|
@ -141,7 +191,25 @@ const open = async (type: string, id?: number) => {
|
|||
}
|
||||
}
|
||||
// 获得用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
// 默认新建时选中自己
|
||||
if (formType.value === 'create') {
|
||||
formData.value.ownerUserId = useUserStore().getUser.id
|
||||
}
|
||||
// 从回款计划创建回款
|
||||
if (receivablePlan) {
|
||||
formData.value.customerId = receivablePlan.customerId
|
||||
await handleCustomerChange(receivablePlan.customerId)
|
||||
formData.value.contractId = receivablePlan.contractId
|
||||
await handleContractChange(receivablePlan.contractId)
|
||||
if (receivablePlan.id) {
|
||||
formData.value.planId = receivablePlan.id
|
||||
formData.value.price = receivablePlan.price
|
||||
formData.value.returnType = receivablePlan.returnType
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -173,25 +241,50 @@ const submitForm = async () => {
|
|||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
no: undefined,
|
||||
planId: undefined,
|
||||
customerId: undefined,
|
||||
contractId: undefined,
|
||||
checkStatus: undefined,
|
||||
processInstanceId: undefined,
|
||||
returnTime: undefined,
|
||||
returnType: undefined,
|
||||
price: undefined,
|
||||
ownerUserId: undefined,
|
||||
batchId: undefined,
|
||||
sort: undefined,
|
||||
dataScope: undefined,
|
||||
dataScopeDeptIds: undefined,
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
formData.value = {} as ReceivableApi.ReceivableVO
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 处理切换客户 */
|
||||
const handleCustomerChange = async (customerId: number) => {
|
||||
// 重置合同编号
|
||||
formData.value.contractId = undefined
|
||||
// 获得合同列表
|
||||
if (customerId) {
|
||||
contractList.value = []
|
||||
contractList.value = await ContractApi.getContractSimpleList(customerId)
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理切换合同 */
|
||||
const handleContractChange = async (contractId: number) => {
|
||||
// 重置回款计划编号
|
||||
formData.value.planId = undefined
|
||||
if (contractId) {
|
||||
// 获得回款计划列表
|
||||
receivablePlanList.value = []
|
||||
receivablePlanList.value = await ReceivablePlanApi.getReceivablePlanSimpleList(
|
||||
formData.value.customerId,
|
||||
contractId
|
||||
)
|
||||
// 设置金额
|
||||
const contract = contractList.value.find((item) => item.id === contractId)
|
||||
if (contract) {
|
||||
formData.value.price = contract.totalPrice - contract.totalReceivablePrice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理切换回款计划 */
|
||||
const handleReceivablePlanChange = (planId: number) => {
|
||||
if (!planId) {
|
||||
return
|
||||
}
|
||||
const receivablePlan = receivablePlanList.value.find((item) => item.id === planId)
|
||||
if (!receivablePlan) {
|
||||
return
|
||||
}
|
||||
formData.value.price = receivablePlan.price
|
||||
formData.value.returnType = receivablePlan.returnType
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<!-- 操作栏 -->
|
||||
<el-row justify="end">
|
||||
<el-button @click="openForm">
|
||||
<el-button @click="openForm('create')">
|
||||
<Icon class="mr-5px" icon="icon-park:income-one" />
|
||||
创建回款
|
||||
</el-button>
|
||||
|
@ -9,40 +9,56 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="回款编号" fixed="left" align="center" prop="no">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.no }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="合同编号" align="center" prop="contractNo" />
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="回款编号" prop="no" />
|
||||
<el-table-column align="center" label="客户" prop="customerName" />
|
||||
<el-table-column align="center" label="合同" prop="contract.no" />
|
||||
<el-table-column
|
||||
label="回款金额(元)"
|
||||
:formatter="dateFormatter2"
|
||||
align="center"
|
||||
prop="price"
|
||||
:formatter="fenToYuanFormat"
|
||||
/>
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserName" />
|
||||
<el-table-column align="center" label="状态" prop="auditStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="回款日期"
|
||||
align="center"
|
||||
prop="returnTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
width="150px"
|
||||
/>
|
||||
<el-table-column align="center" label="回款方式" prop="returnType" width="130px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="回款金额(元)"
|
||||
prop="price"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" />
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="130px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
@ -50,45 +66,44 @@
|
|||
<!-- 表单弹窗:添加 -->
|
||||
<ReceivableForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import ReceivableForm from './../ReceivableForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { dateFormatter2 } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'CrmReceivableList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId?: number // 客户编号
|
||||
contractId?: number // 合同编号
|
||||
}>()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerId: undefined as unknown // 允许 undefined + number
|
||||
customerId: undefined as unknown, // 允许 undefined + number
|
||||
contractId: undefined as unknown // 允许 undefined + number
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 置空参数
|
||||
queryParams.customerId = undefined
|
||||
// 执行查询
|
||||
let data = { list: [], total: 0 }
|
||||
switch (props.bizType) {
|
||||
case BizTypeEnum.CRM_CUSTOMER:
|
||||
queryParams.customerId = props.bizId
|
||||
data = await ReceivableApi.getReceivablePageByCustomer(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId
|
||||
queryParams.contractId = props.contractId
|
||||
}
|
||||
const data = await ReceivableApi.getReceivablePageByCustomer(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
|
@ -99,25 +114,49 @@ const getList = async () => {
|
|||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
// 置空参数
|
||||
queryParams.customerId = undefined
|
||||
queryParams.contractId = undefined
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加 */
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open('create')
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id, {
|
||||
customerId: props.customerId,
|
||||
contractId: props.contractId
|
||||
})
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmReceivableDetail', params: { id } })
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ReceivableApi.deleteReceivable(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||
/** 从回款计划创建回款 */
|
||||
const createReceivable = (planData: any) => {
|
||||
const data = planData as unknown as ReceivablePlanApi.ReceivablePlanVO
|
||||
formRef.value.open('create', undefined, data)
|
||||
}
|
||||
defineExpose({ createReceivable })
|
||||
|
||||
/** 监听打开的 customerId + contractId,从而加载最新的列表 */
|
||||
watch(
|
||||
() => [props.bizId, props.bizType],
|
||||
() => {
|
||||
() => [props.customerId, props.contractId],
|
||||
(newVal) => {
|
||||
// 保证至少客户编号有值
|
||||
if (!newVal[0]) {
|
||||
return
|
||||
}
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<el-col>
|
||||
<el-row>
|
||||
<span class="text-xl font-bold">{{ receivable.no }}</span>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ receivable.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同金额">
|
||||
{{ erpPriceInputFormatter(receivable.contract?.totalPrice) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款日期">
|
||||
{{ formatDate(receivable.returnTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款金额">
|
||||
{{ erpPriceInputFormatter(receivable.price) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ receivable.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { receivable } = defineProps<{ receivable: ReceivableApi.ReceivableVO }>()
|
||||
</script>
|
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="回款编号">{{ receivable.no }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ receivable.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同编号">
|
||||
{{ receivable.contract?.no }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款日期">
|
||||
{{ formatDate(receivable.returnTime, 'YYYY-MM-DD') }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款金额">
|
||||
{{ erpPriceInputFormatter(receivable.price) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回款方式">
|
||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="receivable.returnType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ receivable.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ receivable.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ receivable.creatorName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(receivable.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ formatDate(receivable.updateTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { receivable } = defineProps<{
|
||||
receivable: ReceivableApi.ReceivableVO
|
||||
}>()
|
||||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||
</script>
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<ReceivableDetailsHeader v-loading="loading" :receivable="receivable">
|
||||
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', receivable.id)">
|
||||
编辑
|
||||
</el-button>
|
||||
</ReceivableDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="详细资料">
|
||||
<ReceivableDetailsInfo :receivable="receivable" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志">
|
||||
<OperateLogV2 :log-list="logList" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="团队成员">
|
||||
<PermissionList
|
||||
ref="permissionListRef"
|
||||
:biz-id="receivable.id!"
|
||||
:biz-type="BizTypeEnum.CRM_RECEIVABLE"
|
||||
:show-action="true"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ReceivableForm ref="formRef" @success="getReceivable(receivable.id)" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import ReceivableDetailsHeader from './ReceivableDetailsHeader.vue'
|
||||
import ReceivableDetailsInfo from './ReceivableDetailsInfo.vue'
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
||||
import ReceivableForm from '@/views/crm/receivable/ReceivableForm.vue'
|
||||
|
||||
defineOptions({ name: 'CrmReceivablePlanDetail' })
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const receivableId = ref(0) // 回款编号
|
||||
const loading = ref(true) // 加载中
|
||||
const receivable = ref<ReceivableApi.ReceivableVO>({} as ReceivableApi.ReceivableVO) // 回款详情
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getReceivable = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
receivable.value = await ReceivableApi.getReceivable(id)
|
||||
await getOperateLog(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 编辑 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
|
||||
const getOperateLog = async (receivableId: number) => {
|
||||
if (!receivableId) {
|
||||
return
|
||||
}
|
||||
const data = await getOperateLogPage({
|
||||
bizType: BizTypeEnum.CRM_RECEIVABLE,
|
||||
bizId: receivableId
|
||||
})
|
||||
logList.value = data.list
|
||||
}
|
||||
|
||||
/** 关闭窗口 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const { params } = useRoute()
|
||||
onMounted(async () => {
|
||||
const id = props.id || route.params.id
|
||||
if (!id) {
|
||||
message.warning('参数错误,回款不能为空!')
|
||||
close()
|
||||
return
|
||||
}
|
||||
receivableId.value = id
|
||||
await getReceivable(receivableId.value)
|
||||
})
|
||||
</script>
|
|
@ -2,49 +2,63 @@
|
|||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="回款编号" prop="no">
|
||||
<el-input
|
||||
v-model="queryParams.no"
|
||||
placeholder="请输入回款编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入回款编号"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input
|
||||
<el-select
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
placeholder="请选择客户"
|
||||
@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-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['crm:receivable:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<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
|
||||
type="success"
|
||||
v-hasPermi="['crm:receivable:create']"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:receivable:export']"
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:export']"
|
||||
:loading="exportLoading"
|
||||
plain
|
||||
type="success"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:download" />
|
||||
导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -52,66 +66,123 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="ID" align="center" prop="id" />
|
||||
<el-table-column label="回款编号" align="center" prop="no" />
|
||||
<!-- <el-table-column label="回款计划ID" align="center" prop="planId" />-->
|
||||
<el-table-column label="客户" align="center" prop="customerId" />
|
||||
<el-table-column label="合同" align="center" prop="contractId" />
|
||||
<el-table-column label="审批状态" align="center" prop="checkStatus" width="130px">
|
||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="我负责的" name="1" />
|
||||
<el-tab-pane label="我参与的" name="2" />
|
||||
<el-tab-pane label="下属负责的" name="3" />
|
||||
</el-tabs>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" fixed="left" label="回款编号" prop="no" width="180">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.checkStatus" />
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.no }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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="contractNo" width="180">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="openContractDetail(scope.row.contractId)"
|
||||
>
|
||||
{{ scope.row.contract.no }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="工作流编号" align="center" prop="processInstanceId" />-->
|
||||
<el-table-column
|
||||
label="回款日期"
|
||||
align="center"
|
||||
prop="returnTime"
|
||||
:formatter="dateFormatter2"
|
||||
align="center"
|
||||
label="回款日期"
|
||||
prop="returnTime"
|
||||
width="150px"
|
||||
/>
|
||||
<el-table-column label="回款方式" align="center" prop="returnType" width="130px">
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="回款金额(元)"
|
||||
prop="price"
|
||||
width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column align="center" label="回款方式" prop="returnType" width="130px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回款金额(元)" align="center" prop="price" />
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId" />
|
||||
<el-table-column label="批次" align="center" prop="batchId" />
|
||||
<!--<el-table-column label="显示顺序" align="center" prop="sort" />-->
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column align="center" label="备注" prop="remark" width="200" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
label="合同金额(元)"
|
||||
prop="contract.totalPrice"
|
||||
width="140"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<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 label="操作" align="center" 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="180px">
|
||||
<template #default="scope">
|
||||
<!-- todo @liuhongfeng:用路径参数哈,receivableId -->
|
||||
<!--<router-link :to="'/crm/receivable-plan?receivableId=' + scope.row.receivableId">
|
||||
<el-button link type="primary">详情</el-button>
|
||||
</router-link>-->
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:receivable:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.auditStatus === 0"
|
||||
v-hasPermi="['crm:receivable:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSubmit(scope.row)"
|
||||
>
|
||||
提交审核
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
v-hasPermi="['crm:receivable:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleProcessDetail(scope.row)"
|
||||
>
|
||||
查看审批
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:receivable:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
|
@ -120,9 +191,9 @@
|
|||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
@ -130,30 +201,40 @@
|
|||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ReceivableForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import ReceivableForm from './ReceivableForm.vue'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'Receivable' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
no: null,
|
||||
customerId: null
|
||||
sceneType: '1', // 默认和 activeName 相等
|
||||
no: undefined,
|
||||
customerId: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const activeName = ref('1') // 列表 tab
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
|
||||
/** tab 切换 */
|
||||
const handleTabClick = (tab: TabsPaneContext) => {
|
||||
queryParams.sceneType = tab.paneName
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -198,6 +279,35 @@ const handleDelete = async (id: number) => {
|
|||
} catch {}
|
||||
}
|
||||
|
||||
/** 提交审核 **/
|
||||
const handleSubmit = async (row: ReceivableApi.ReceivableVO) => {
|
||||
await message.confirm(`您确定提交编号为【${row.no}】的回款审核吗?`)
|
||||
await ReceivableApi.submitReceivable(row.id)
|
||||
message.success('提交审核成功!')
|
||||
await getList()
|
||||
}
|
||||
|
||||
/** 打开回款详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmReceivableDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const openContractDetail = (id: number) => {
|
||||
push({ name: 'CrmContractDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 查看审批 */
|
||||
const handleProcessDetail = (row: ReceivableApi.ReceivableVO) => {
|
||||
push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
|
@ -214,7 +324,9 @@ const handleExport = async () => {
|
|||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,126 +1,164 @@
|
|||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select v-model="formData.ownerUserId" clearable placeholder="请输入负责人">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="期数" prop="period">
|
||||
<el-input-number v-model="formData.period" placeholder="请输入期数" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="回款ID" prop="receivableId">
|
||||
<el-input v-model="formData.receivableId" placeholder="请输入回款ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="完成状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择完成状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批状态" prop="checkStatus">
|
||||
<el-select v-model="formData.checkStatus" placeholder="请选择审批状态">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_RECEIVABLE_CHECK_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工作流编号" prop="processInstanceId">
|
||||
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
|
||||
</el-form-item>-->
|
||||
<el-form-item label="计划回款金额" prop="price">
|
||||
<el-input-number v-model="formData.price" placeholder="请输入计划回款金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划回款日期" prop="returnTime">
|
||||
<el-date-picker
|
||||
v-model="formData.returnTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择计划回款日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提前几天提醒" prop="remindDays">
|
||||
<el-input-number v-model="formData.remindDays" placeholder="请输入提前几天提醒" />
|
||||
</el-form-item>
|
||||
<el-form-item label="提醒日期" prop="remindTime">
|
||||
<el-date-picker
|
||||
v-model="formData.remindTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择提醒日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" :rows="3" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="还款期数" prop="period">
|
||||
<el-input disabled v-model="formData.period" placeholder="保存时自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select
|
||||
v-model="formData.customerId"
|
||||
:disabled="formType !== 'create'"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
@change="handleCustomerChange"
|
||||
placeholder="请选择客户"
|
||||
>
|
||||
<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="12">
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-select
|
||||
v-model="formData.contractId"
|
||||
:disabled="formType !== 'create' || !formData.customerId"
|
||||
class="w-1/1"
|
||||
filterable
|
||||
placeholder="请选择合同"
|
||||
>
|
||||
<el-option
|
||||
v-for="data in contractList"
|
||||
:key="data.id"
|
||||
:label="data.name"
|
||||
:value="data.id!"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划回款金额" prop="price">
|
||||
<el-input-number
|
||||
v-model="formData.price"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
placeholder="请输入计划回款金额"
|
||||
:min="0.01"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="计划回款日期" prop="returnTime">
|
||||
<el-date-picker
|
||||
v-model="formData.returnTime"
|
||||
placeholder="选择计划回款日期"
|
||||
type="date"
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提前几天提醒" prop="remindDays">
|
||||
<el-input-number
|
||||
v-model="formData.remindDays"
|
||||
class="!w-100%"
|
||||
controls-position="right"
|
||||
placeholder="请输入提前几天提醒"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="回款方式" prop="returnType">
|
||||
<el-select v-model="formData.returnType" class="w-1/1" placeholder="请选择回款方式">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { aw } from '../../../../../dist-prod/assets/index-9eac537b'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
period: undefined,
|
||||
receivableId: undefined,
|
||||
status: undefined,
|
||||
checkStatus: undefined,
|
||||
processInstanceId: undefined,
|
||||
price: undefined,
|
||||
returnTime: undefined,
|
||||
remindDays: undefined,
|
||||
remindTime: undefined,
|
||||
customerId: undefined,
|
||||
contractId: undefined,
|
||||
ownerUserId: undefined,
|
||||
sort: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
const formData = ref<ReceivablePlanApi.ReceivablePlanVO>({} as ReceivablePlanApi.ReceivablePlanVO)
|
||||
const formRules = reactive({
|
||||
status: [{ required: true, message: '完成状态不能为空', trigger: 'change' }]
|
||||
price: [{ required: true, message: '计划回款金额不能为空', trigger: 'blur' }],
|
||||
returnTime: [{ required: true, message: '计划回款日期不能为空', trigger: 'blur' }],
|
||||
customerId: [{ required: true, message: '客户编号不能为空', trigger: 'blur' }],
|
||||
contractId: [{ required: true, message: '合同编号不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
const contractList = ref<ContractApi.ContractVO[]>([]) // 合同列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
const open = async (type: string, id?: number, customerId?: number, contractId?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
|
@ -134,9 +172,22 @@ const open = async (type: string, id?: number) => {
|
|||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获得用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
// 默认新建时选中自己
|
||||
if (formType.value === 'create') {
|
||||
formData.value.ownerUserId = useUserStore().getUser.id
|
||||
}
|
||||
// 设置 customerId 和 contractId 默认值
|
||||
if (customerId) {
|
||||
formData.value.customerId = customerId
|
||||
await handleCustomerChange(customerId)
|
||||
}
|
||||
if (contractId) {
|
||||
formData.value.contractId = contractId
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
@ -168,23 +219,18 @@ const submitForm = async () => {
|
|||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
period: undefined,
|
||||
receivableId: undefined,
|
||||
status: undefined,
|
||||
checkStatus: undefined,
|
||||
processInstanceId: undefined,
|
||||
price: undefined,
|
||||
returnTime: undefined,
|
||||
remindDays: undefined,
|
||||
remindTime: undefined,
|
||||
customerId: undefined,
|
||||
contractId: undefined,
|
||||
ownerUserId: undefined,
|
||||
sort: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
formData.value = {} as ReceivablePlanApi.ReceivablePlanVO
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 处理切换客户 */
|
||||
const handleCustomerChange = async (customerId: number) => {
|
||||
// 重置合同编号
|
||||
formData.value.contractId = undefined
|
||||
// 获得合同列表
|
||||
if (customerId) {
|
||||
contractList.value = []
|
||||
contractList.value = await ContractApi.getContractSimpleList(customerId)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<!-- 操作栏 -->
|
||||
<el-row justify="end">
|
||||
<el-button @click="openForm">
|
||||
<el-button @click="openForm('create', undefined)">
|
||||
<Icon class="mr-5px" icon="icon-park:income" />
|
||||
创建回款计划
|
||||
</el-button>
|
||||
|
@ -9,43 +9,69 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="期数" fixed="left" align="center" prop="no">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.period }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="合同编号" align="center" prop="contractNo" />
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="客户名称" prop="customerName" width="150px" />
|
||||
<el-table-column align="center" label="合同编号" prop="contractNo" width="200px" />
|
||||
<el-table-column align="center" label="期数" prop="period" />
|
||||
<el-table-column
|
||||
label="计划还款金额(元)"
|
||||
align="center"
|
||||
label="计划回款(元)"
|
||||
prop="price"
|
||||
:formatter="fenToYuanFormat"
|
||||
width="120"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="计划还款日期"
|
||||
:formatter="dateFormatter2"
|
||||
align="center"
|
||||
label="计划回款日期"
|
||||
prop="returnTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="计划还款方式" prop="auditStatus">
|
||||
<el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter2"
|
||||
align="center"
|
||||
label="提醒日期"
|
||||
prop="remindTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="负责人" prop="ownerUserName" width="120" />
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="200px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:create']"
|
||||
link
|
||||
type="primary"
|
||||
@click="createReceivable(scope.row)"
|
||||
:disabled="scope.row.receivableId"
|
||||
>
|
||||
创建回款
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable-plan:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable-plan:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提前几日提醒" align="center" prop="remindDays" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<!-- TODO 芋艿:新建回款、编辑、删除 -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
@ -53,45 +79,42 @@
|
|||
<!-- 表单弹窗:添加 -->
|
||||
<ReceivableForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import ReceivableForm from './../ReceivablePlanForm.vue'
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { fenToYuanFormat } from '@/utils/formatter'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter2 } from '@/utils/formatTime'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
|
||||
defineOptions({ name: 'CrmReceivablePlanList' })
|
||||
const props = defineProps<{
|
||||
bizType: number // 业务类型
|
||||
bizId: number // 业务编号
|
||||
customerId?: number // 客户编号
|
||||
contractId?: number // 合同编号
|
||||
}>()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerId: undefined as unknown // 允许 undefined + number
|
||||
customerId: undefined as unknown, // 允许 undefined + number
|
||||
contractId: undefined as unknown // 允许 undefined + number
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 置空参数
|
||||
queryParams.customerId = undefined
|
||||
// 执行查询
|
||||
let data = { list: [], total: 0 }
|
||||
switch (props.bizType) {
|
||||
case BizTypeEnum.CRM_CUSTOMER:
|
||||
queryParams.customerId = props.bizId
|
||||
data = await ReceivablePlanApi.getReceivablePlanPageByCustomer(queryParams)
|
||||
break
|
||||
default:
|
||||
return
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId
|
||||
queryParams.contractId = props.contractId
|
||||
}
|
||||
const data = await ReceivablePlanApi.getReceivablePlanPageByCustomer(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
|
@ -102,25 +125,47 @@ const getList = async () => {
|
|||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
// 置空参数
|
||||
queryParams.customerId = undefined
|
||||
queryParams.contractId = undefined
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加 */
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = () => {
|
||||
formRef.value.open('create')
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id, props.customerId, props.contractId)
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmReceivablePlanDetail', params: { id } })
|
||||
/** 创建回款 */
|
||||
const emits = defineEmits<{
|
||||
(e: 'createReceivable', v: ReceivablePlanApi.ReceivablePlanVO)
|
||||
}>()
|
||||
const createReceivable = (row: ReceivablePlanApi.ReceivablePlanVO) => {
|
||||
emits('createReceivable', row)
|
||||
}
|
||||
|
||||
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ReceivablePlanApi.deleteReceivablePlan(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 监听打开的 customerId + contractId,从而加载最新的列表 */
|
||||
watch(
|
||||
() => [props.bizId, props.bizType],
|
||||
() => {
|
||||
() => [props.customerId, props.contractId],
|
||||
(newVal) => {
|
||||
// 保证至少客户编号有值
|
||||
if (!newVal[0]) {
|
||||
return
|
||||
}
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<el-col>
|
||||
<el-row>
|
||||
<span class="text-xl font-bold">第 {{ receivablePlan.period }} 期</span>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ receivablePlan.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同编号">{{ receivablePlan.contractNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="计划回款金额">
|
||||
{{ erpPriceInputFormatter(receivablePlan.price) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计划回款日期">
|
||||
{{ formatDate(receivablePlan.returnTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="实际回款金额">
|
||||
<el-text v-if="receivablePlan.receivable">
|
||||
{{ erpPriceInputFormatter(receivablePlan.receivable.price) }}
|
||||
</el-text>
|
||||
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { receivablePlan } = defineProps<{ receivablePlan: ReceivablePlanApi.ReceivablePlanVO }>()
|
||||
</script>
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="期数">{{ receivablePlan.period }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ receivablePlan.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同编号">
|
||||
{{ receivablePlan.contractNo }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计划回款金额">
|
||||
{{ erpPriceInputFormatter(receivablePlan.price) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计划回款日期">
|
||||
{{ formatDate(receivablePlan.returnTime, 'YYYY-MM-DD') }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="计划回款方式">
|
||||
<dict-tag
|
||||
:type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE"
|
||||
:value="receivablePlan.returnType"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提前几天提醒">
|
||||
{{ receivablePlan.remindDays }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ receivablePlan.remark }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实际回款金额">
|
||||
<el-text v-if="receivablePlan.receivable">
|
||||
{{ erpPriceInputFormatter(receivablePlan.receivable.price) }}
|
||||
</el-text>
|
||||
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="未回款金额">
|
||||
<el-text v-if="receivablePlan.receivable">
|
||||
{{ erpPriceInputFormatter(receivablePlan.price - receivablePlan.receivable.price) }}
|
||||
</el-text>
|
||||
<el-text v-else>{{ erpPriceInputFormatter(receivablePlan.price) }}</el-text>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="实际回款日期">
|
||||
{{ formatDate(receivablePlan.receivable?.returnTime, 'YYYY-MM-DD') }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ receivablePlan.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ receivablePlan.creatorName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(receivablePlan.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ formatDate(receivablePlan.updateTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { erpPriceInputFormatter } from '@/utils'
|
||||
|
||||
const { receivablePlan } = defineProps<{
|
||||
receivablePlan: ReceivablePlanApi.ReceivablePlanVO
|
||||
}>()
|
||||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||
</script>
|
|
@ -0,0 +1,103 @@
|
|||
<template>
|
||||
<ReceivablePlanDetailsHeader v-loading="loading" :receivable-plan="receivablePlan">
|
||||
<el-button
|
||||
v-if="permissionListRef?.validateWrite"
|
||||
@click="openForm('update', receivablePlan.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
</ReceivablePlanDetailsHeader>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="详细资料">
|
||||
<ReceivablePlanDetailsInfo :receivable-plan="receivablePlan" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志">
|
||||
<OperateLogV2 :log-list="logList" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="团队成员">
|
||||
<PermissionList
|
||||
ref="permissionListRef"
|
||||
:biz-id="receivablePlan.id!"
|
||||
:biz-type="BizTypeEnum.CRM_RECEIVABLE_PLAN"
|
||||
:show-action="true"
|
||||
@quit-team="close"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ReceivablePlanForm ref="formRef" @success="getReceivablePlan(receivablePlan.id)" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import ReceivablePlanDetailsHeader from './ReceivablePlanDetailsHeader.vue'
|
||||
import ReceivablePlanDetailsInfo from './ReceivablePlanDetailsInfo.vue'
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { OperateLogV2VO } from '@/api/system/operatelog'
|
||||
import { getOperateLogPage } from '@/api/crm/operateLog'
|
||||
import ReceivablePlanForm from '@/views/crm/receivable/plan/ReceivablePlanForm.vue'
|
||||
|
||||
defineOptions({ name: 'CrmReceivablePlanDetail' })
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const receivablePlanId = ref(0) // 回款计划编号
|
||||
const loading = ref(true) // 加载中
|
||||
const receivablePlan = ref<ReceivablePlanApi.ReceivablePlanVO>(
|
||||
{} as ReceivablePlanApi.ReceivablePlanVO
|
||||
) // 回款计划详情
|
||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
|
||||
|
||||
/** 获取详情 */
|
||||
const getReceivablePlan = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
receivablePlan.value = await ReceivablePlanApi.getReceivablePlan(id)
|
||||
await getOperateLog(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 编辑 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 获取操作日志 */
|
||||
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
|
||||
const getOperateLog = async (receivablePlanId: number) => {
|
||||
if (!receivablePlanId) {
|
||||
return
|
||||
}
|
||||
const data = await getOperateLogPage({
|
||||
bizType: BizTypeEnum.CRM_RECEIVABLE_PLAN,
|
||||
bizId: receivablePlanId
|
||||
})
|
||||
logList.value = data.list
|
||||
}
|
||||
|
||||
/** 关闭窗口 */
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const close = () => {
|
||||
delView(unref(currentRoute))
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
const { params } = useRoute()
|
||||
onMounted(async () => {
|
||||
if (!params.id) {
|
||||
message.warning('参数错误,回款计划不能为空!')
|
||||
close()
|
||||
return
|
||||
}
|
||||
receivablePlanId.value = params.id as unknown as number
|
||||
await getReceivablePlan(receivablePlanId.value)
|
||||
})
|
||||
</script>
|
|
@ -2,49 +2,63 @@
|
|||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-input
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-select
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
placeholder="请选择客户"
|
||||
@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 label="合同" prop="contractId">
|
||||
<el-form-item label="合同编号" prop="contractNo">
|
||||
<el-input
|
||||
v-model="queryParams.contractId"
|
||||
placeholder="请输入合同"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
v-model="queryParams.contractNo"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入合同编号"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['crm:receivable-plan:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<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
|
||||
type="success"
|
||||
v-hasPermi="['crm:receivable-plan:create']"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:receivable-plan:export']"
|
||||
type="primary"
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable-plan:export']"
|
||||
:loading="exportLoading"
|
||||
plain
|
||||
type="success"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon class="mr-5px" icon="ep:download" />
|
||||
导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -52,68 +66,131 @@
|
|||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!--<el-table-column label="ID" align="center" prop="id" />-->
|
||||
<el-table-column label="客户名称" align="center" prop="customerId" width="150px" />
|
||||
<el-table-column label="合同名称" align="center" prop="contractId" width="150px" />
|
||||
<el-table-column label="期数" align="center" prop="period" />
|
||||
<el-table-column label="计划回款" align="center" prop="price" />
|
||||
<el-tabs v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="我负责的" name="1" />
|
||||
<el-tab-pane label="下属负责的" name="3" />
|
||||
</el-tabs>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="150">
|
||||
<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="contractNo" width="200px" />
|
||||
<el-table-column align="center" label="期数" prop="period">
|
||||
<template #default="scope">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
{{ scope.row.period }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="计划回款日期"
|
||||
align="center"
|
||||
prop="returnTime"
|
||||
label="计划回款金额(元)"
|
||||
prop="price"
|
||||
width="160"
|
||||
:formatter="erpPriceTableColumnFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter2"
|
||||
align="center"
|
||||
label="计划回款日期"
|
||||
prop="returnTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="提前几天提醒" align="center" prop="remindDays" />
|
||||
<!--<el-table-column
|
||||
label="提醒日期"
|
||||
align="center"
|
||||
prop="remindTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>-->
|
||||
<!--<el-table-column label="回款ID" align="center" prop="receivableId" />-->
|
||||
<el-table-column label="完成状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批状态" align="center" prop="checkStatus" width="130px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.checkStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!--<el-table-column label="工作流编号" align="center" prop="processInstanceId" />-->
|
||||
<el-table-column prop="ownerUserId" label="负责人" width="120">
|
||||
<template #default="scope">
|
||||
{{ userList.find((user) => user.id === scope.row.ownerUserId)?.nickname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="显示顺序" align="center" prop="sort" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column align="center" label="提前几天提醒" prop="remindDays" width="150" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
label="提醒日期"
|
||||
prop="remindTime"
|
||||
width="180px"
|
||||
:formatter="dateFormatter2"
|
||||
/>
|
||||
<el-table-column align="center" label="回款方式" prop="returnType" width="130px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE" :value="scope.row.returnType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
<el-table-column label="负责人" prop="ownerUserName" width="120" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="实际回款金额(元)"
|
||||
prop="receivable.price"
|
||||
width="160"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-text v-if="scope.row.receivable">
|
||||
{{ erpPriceInputFormatter(scope.row.receivable.price) }}
|
||||
</el-text>
|
||||
<el-text v-else>{{ erpPriceInputFormatter(0) }}</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="实际回款日期"
|
||||
prop="receivable.returnTime"
|
||||
width="180px"
|
||||
:formatter="dateFormatter2"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="实际回款金额(元)"
|
||||
prop="receivable.price"
|
||||
width="160"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-text v-if="scope.row.receivable">
|
||||
{{ erpPriceInputFormatter(scope.row.price - scope.row.receivable.price) }}
|
||||
</el-text>
|
||||
<el-text v-else>{{ erpPriceInputFormatter(scope.row.price) }}</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="更新时间"
|
||||
prop="updateTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="130px">
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
|
||||
<el-table-column align="center" fixed="right" label="操作" width="180px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable:create']"
|
||||
link
|
||||
type="success"
|
||||
@click="openReceivableForm(scope.row)"
|
||||
:disabled="scope.row.receivableId"
|
||||
>
|
||||
创建回款
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable-plan:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:receivable-plan:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:receivable-plan:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:receivable-plan:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
|
@ -122,24 +199,28 @@
|
|||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ReceivablePlanForm ref="formRef" @success="getList" />
|
||||
<ReceivableForm ref="receivableFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivable/plan'
|
||||
import ReceivablePlanForm from './ReceivablePlanForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { TabsPaneContext } from 'element-plus'
|
||||
import ReceivableForm from '@/views/crm/receivable/ReceivableForm.vue'
|
||||
|
||||
defineOptions({ name: 'ReceivablePlan' })
|
||||
|
||||
|
@ -149,15 +230,23 @@ const { t } = useI18n() // 国际化
|
|||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerId: null,
|
||||
contractId: null
|
||||
sceneType: '1', // 默认和 activeName 相等
|
||||
customerId: undefined,
|
||||
contractNo: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const activeName = ref('1') // 列表 tab
|
||||
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||
|
||||
/** tab 切换 */
|
||||
const handleTabClick = (tab: TabsPaneContext) => {
|
||||
queryParams.sceneType = tab.paneName
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
@ -189,6 +278,12 @@ const openForm = (type: string, id?: number) => {
|
|||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 创建回款操作 */
|
||||
const receivableFormRef = ref()
|
||||
const openReceivableForm = (row: ReceivablePlanApi.ReceivablePlanVO) => {
|
||||
receivableFormRef.value.open('create', undefined, row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
|
@ -217,10 +312,21 @@ const handleExport = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
/** 打开详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmReceivablePlanDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const openCustomerDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 获取用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
// 获得客户列表
|
||||
customerList.value = await CustomerApi.getCustomerSimpleList()
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'ContactsCountRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getContactsCountRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getContactsCountRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'ContractCountRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getContractCountRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getContractCountRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'ContractPriceRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getContractPriceRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getContractPriceRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'CustomerCountRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getCustomerCountRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getCustomerCountRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'FollowCountRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getFollowCountRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getFollowCountRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'FollowCustomerCountRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getFollowCustomerCountRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getFollowCustomerCountRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'ProductSalesRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getProductSalesRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getProductSalesRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -18,7 +18,7 @@
|
|||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
|
||||
import { StatisticsRankApi, StatisticsRankRespVO } from '@/api/crm/statistics/rank'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
|
@ -26,7 +26,7 @@ defineOptions({ name: 'ReceivablePriceRank' })
|
|||
const props = defineProps<{ queryParams: any }>() // 搜索参数
|
||||
|
||||
const loading = ref(false) // 加载中
|
||||
const list = ref<BiRankRespVO[]>([]) // 列表的数据
|
||||
const list = ref<StatisticsRankRespVO[]>([]) // 列表的数据
|
||||
|
||||
/** 柱状图配置:横向 */
|
||||
const echartsOption = reactive<EChartsOption>({
|
||||
|
@ -81,7 +81,7 @@ const echartsOption = reactive<EChartsOption>({
|
|||
const loadData = async () => {
|
||||
// 1. 加载排行数据
|
||||
loading.value = true
|
||||
const rankingList = await RankApi.getReceivablePriceRank(props.queryParams)
|
||||
const rankingList = await StatisticsRankApi.getReceivablePriceRank(props.queryParams)
|
||||
// 2.1 更新 Echarts 数据
|
||||
if (echartsOption.dataset && echartsOption.dataset['source']) {
|
||||
echartsOption.dataset['source'] = clone(rankingList).reverse()
|
|
@ -90,7 +90,7 @@ import * as DeptApi from '@/api/system/dept'
|
|||
import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
defineOptions({ name: 'CrmBiRank' })
|
||||
defineOptions({ name: 'CrmStatisticsRank' })
|
||||
|
||||
const queryParams = reactive({
|
||||
deptId: useUserStore().getUser.deptId,
|
Loading…
Reference in New Issue