Merge remote-tracking branch 'yudao/dev' into dev-crm

pull/393/head
puhui999 2024-02-25 00:40:49 +08:00
commit d9e1ffd522
20 changed files with 300 additions and 100 deletions

View File

@ -1,11 +1,6 @@
import request from '@/config/axios' import request from '@/config/axios'
// TODO 芋艿:融合下 // TODO 芋艿:融合下
// 3. 获得分配给我的客户数量
export const getFollowCustomerCount = async () => {
return await request.get({ url: '/crm/customer/follow-customer-count' })
}
// 5. 获得待审核合同数量 // 5. 获得待审核合同数量
export const getCheckContractCount = async () => { export const getCheckContractCount = async () => {
return await request.get({ url: '/crm/contract/check-contract-count' }) return await request.get({ url: '/crm/contract/check-contract-count' })

View File

@ -37,6 +37,11 @@ export interface ContactBusinessReqVO {
businessIds: number[] businessIds: number[]
} }
export interface ContactBusiness2ReqVO {
businessId: number
contactIds: number[]
}
// 查询 CRM 联系人列表 // 查询 CRM 联系人列表
export const getContactPage = async (params) => { export const getContactPage = async (params) => {
return await request.get({ url: `/crm/contact/page`, params }) return await request.get({ url: `/crm/contact/page`, params })
@ -87,11 +92,21 @@ export const createContactBusinessList = async (data: ContactBusinessReqVO) => {
return await request.post({ url: `/crm/contact/create-business-list`, data }) 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) => { export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => {
return await request.delete({ url: `/crm/contact/delete-business-list`, data }) 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) => { export const transferContact = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/contact/transfer', data }) return await request.put({ url: '/crm/contact/transfer', data })

View File

@ -101,3 +101,13 @@ export const submitContract = async (id: number) => {
export const transferContract = async (data: TransferReqVO) => { export const transferContract = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/contract/transfer', data }) 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' })
}

View File

@ -1,66 +1,66 @@
import request from '@/config/axios' import request from '@/config/axios'
export interface BiRankRespVO { export interface StatisticsRankRespVO {
count: number count: number
nickname: string nickname: string
deptName: string deptName: string
} }
// 排行 API // 排行 API
export const RankApi = { export const StatisticsRankApi = {
// 获得合同排行榜 // 获得合同排行榜
getContractPriceRank: (params: any) => { getContractPriceRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-contract-price-rank', url: '/crm/statistics-rank/get-contract-price-rank',
params params
}) })
}, },
// 获得回款排行榜 // 获得回款排行榜
getReceivablePriceRank: (params: any) => { getReceivablePriceRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-receivable-price-rank', url: '/crm/statistics-rank/get-receivable-price-rank',
params params
}) })
}, },
// 签约合同排行 // 签约合同排行
getContractCountRank: (params: any) => { getContractCountRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-contract-count-rank', url: '/crm/statistics-rank/get-contract-count-rank',
params params
}) })
}, },
// 产品销量排行 // 产品销量排行
getProductSalesRank: (params: any) => { getProductSalesRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-product-sales-rank', url: '/crm/statistics-rank/get-product-sales-rank',
params params
}) })
}, },
// 新增客户数排行 // 新增客户数排行
getCustomerCountRank: (params: any) => { getCustomerCountRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-customer-count-rank', url: '/crm/statistics-rank/get-customer-count-rank',
params params
}) })
}, },
// 新增联系人数排行 // 新增联系人数排行
getContactsCountRank: (params: any) => { getContactsCountRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-contacts-count-rank', url: '/crm/statistics-rank/get-contacts-count-rank',
params params
}) })
}, },
// 跟进次数排行 // 跟进次数排行
getFollowCountRank: (params: any) => { getFollowCountRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-follow-count-rank', url: '/crm/statistics-rank/get-follow-count-rank',
params params
}) })
}, },
// 跟进客户数排行 // 跟进客户数排行
getFollowCustomerCountRank: (params: any) => { getFollowCustomerCountRank: (params: any) => {
return request.get({ return request.get({
url: '/crm/bi-rank/get-follow-customer-count-rank', url: '/crm/statistics-rank/get-follow-customer-count-rank',
params params
}) })
} }

View File

@ -30,8 +30,14 @@
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <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" fixed="left" label="合同编号" prop="no" width="180" />
<el-table-column align="center" label="合同名称" prop="name" width="130" /> <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"> <el-table-column align="center" label="客户名称" prop="customerName" width="120">
<template #default="scope"> <template #default="scope">
<el-link <el-link
@ -43,8 +49,24 @@
</el-link> </el-link>
</template> </template>
</el-table-column> </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 <el-table-column
align="center" align="center"
label="下单时间" label="下单时间"
@ -52,13 +74,6 @@
width="120" width="120"
:formatter="dateFormatter2" :formatter="dateFormatter2"
/> />
<el-table-column
align="center"
label="合同金额"
prop="price"
width="130"
:formatter="fenToYuanFormat"
/>
<el-table-column <el-table-column
align="center" align="center"
label="合同开始时间" label="合同开始时间"
@ -78,17 +93,24 @@
<el-link <el-link
:underline="false" :underline="false"
type="primary" type="primary"
@click="openContactDetail(scope.row.contactId)" @click="openContactDetail(scope.row.signContactId)"
> >
{{ scope.row.contactName }} {{ scope.row.signContactName }}
</el-link> </el-link>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" /> <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
<el-table-column align="center" label="备注" prop="remark" width="130" /> <el-table-column align="center" label="备注" prop="remark" width="200" />
<!-- TODO @puhui999后续可加 已收款金额未收款金额 --> <!-- TODO @puhui999后续可加 已收款金额未收款金额 -->
<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="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 <el-table-column
:formatter="dateFormatter" :formatter="dateFormatter"
align="center" align="center"
@ -103,11 +125,24 @@
prop="createTime" prop="createTime"
width="180px" width="180px"
/> />
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120"> <el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" /> <dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template> </template>
</el-table-column> </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> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
@ -122,9 +157,9 @@
<script setup lang="ts" name="CheckContract"> <script setup lang="ts" name="CheckContract">
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime' import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import * as ContractApi from '@/api/crm/contract' import * as ContractApi from '@/api/crm/contract'
import { fenToYuanFormat } from '@/utils/formatter'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { AUDIT_STATUS } from './common' import { AUDIT_STATUS } from './common'
import { erpPriceTableColumnFormatter } from '@/utils'
const loading = ref(true) // const loading = ref(true) //
const total = ref(0) // const total = ref(0) //
@ -132,7 +167,8 @@ const list = ref([]) // 列表的数据
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
auditStatus: 20 sceneType: 1, //
auditStatus: 10
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
@ -154,8 +190,18 @@ const handleQuery = () => {
getList() 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) => { const openCustomerDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } }) push({ name: 'CrmCustomerDetail', params: { id } })
} }
@ -165,6 +211,16 @@ const openContactDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } }) push({ name: 'CrmContactDetail', params: { id } })
} }
/** 打开商机详情 */
const openBusinessDetail = (id: number) => {
push({ name: 'CrmBusinessDetail', params: { id } })
}
/** 激活时 */
onActivated(async () => {
await getList()
})
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()

View File

@ -30,8 +30,14 @@
<ContentWrap> <ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <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" fixed="left" label="合同编号" prop="no" width="180" />
<el-table-column align="center" label="合同名称" prop="name" width="130" /> <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"> <el-table-column align="center" label="客户名称" prop="customerName" width="120">
<template #default="scope"> <template #default="scope">
<el-link <el-link
@ -43,8 +49,24 @@
</el-link> </el-link>
</template> </template>
</el-table-column> </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 <el-table-column
align="center" align="center"
label="下单时间" label="下单时间"
@ -52,13 +74,6 @@
width="120" width="120"
:formatter="dateFormatter2" :formatter="dateFormatter2"
/> />
<el-table-column
align="center"
label="合同金额"
prop="price"
width="130"
:formatter="fenToYuanFormat"
/>
<el-table-column <el-table-column
align="center" align="center"
label="合同开始时间" label="合同开始时间"
@ -78,17 +93,24 @@
<el-link <el-link
:underline="false" :underline="false"
type="primary" type="primary"
@click="openContactDetail(scope.row.contactId)" @click="openContactDetail(scope.row.signContactId)"
> >
{{ scope.row.contactName }} {{ scope.row.signContactName }}
</el-link> </el-link>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" /> <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
<el-table-column align="center" label="备注" prop="remark" width="130" /> <el-table-column align="center" label="备注" prop="remark" width="200" />
<!-- TODO @puhui999后续可加 已收款金额未收款金额 --> <!-- TODO @puhui999后续可加 已收款金额未收款金额 -->
<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="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 <el-table-column
:formatter="dateFormatter" :formatter="dateFormatter"
align="center" align="center"
@ -103,11 +125,24 @@
prop="createTime" prop="createTime"
width="180px" width="180px"
/> />
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120"> <el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" /> <dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template> </template>
</el-table-column> </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> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination
@ -125,8 +160,7 @@ import * as ContractApi from '@/api/crm/contract'
import { fenToYuanFormat } from '@/utils/formatter' import { fenToYuanFormat } from '@/utils/formatter'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { CONTRACT_EXPIRY_TYPE } from './common' import { CONTRACT_EXPIRY_TYPE } from './common'
import { erpPriceTableColumnFormatter } from '@/utils'
const { push } = useRouter() //
const loading = ref(true) // const loading = ref(true) //
const total = ref(0) // const total = ref(0) //
@ -134,6 +168,7 @@ const list = ref([]) // 列表的数据
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
sceneType: '1', //
expiryType: 1 expiryType: 1
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
@ -156,6 +191,17 @@ const handleQuery = () => {
getList() 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) => { const openCustomerDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } }) push({ name: 'CrmCustomerDetail', params: { id } })
@ -166,10 +212,18 @@ const openContactDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } }) push({ name: 'CrmContactDetail', params: { id } })
} }
/** 打开商机详情 */
const openBusinessDetail = (id: number) => {
push({ name: 'CrmBusinessDetail', params: { id } })
}
/** 激活时 */
onActivated(async () => {
await getList()
})
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()
}) })
</script> </script>
<style scoped></style>

View File

@ -130,8 +130,8 @@ const list = ref([]) // 列表的数据
const queryParams = ref({ const queryParams = ref({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
followUpStatus: false, sceneType: 1,
sceneType: 1 followUpStatus: false
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
@ -158,10 +158,13 @@ const openDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } }) push({ name: 'CrmCustomerDetail', params: { id } })
} }
/** 激活时 */
onActivated(async () => {
await getList()
})
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getList() getList()
}) })
</script> </script>
<style scoped></style>

View File

@ -20,8 +20,9 @@ export const CONTACT_STATUS = [
/** 审批状态 */ /** 审批状态 */
export const AUDIT_STATUS = [ export const AUDIT_STATUS = [
{ label: '已审批', value: 20 }, { label: '待审批', value: 10 },
{ label: '待审批', value: 10 } { label: '审核通过', value: 20 },
{ label: '审核不通过', value: 30 }
] ]
/** 回款提醒类型 */ /** 回款提醒类型 */

View File

@ -17,9 +17,9 @@
<el-col :span="20" :xs="24"> <el-col :span="20" :xs="24">
<CustomerTodayContactList v-if="leftMenu === 'customerTodayContact'" /> <CustomerTodayContactList v-if="leftMenu === 'customerTodayContact'" />
<ClueFollowList v-if="leftMenu === 'clueFollow'" /> <ClueFollowList v-if="leftMenu === 'clueFollow'" />
<CheckContract v-if="leftMenu === 'checkContract'" /> <ContractAuditList v-if="leftMenu === 'contractAudit'" />
<CheckReceivables v-if="leftMenu === 'checkReceivables'" /> <CheckReceivables v-if="leftMenu === 'checkReceivables'" />
<EndContract v-if="leftMenu === 'endContract'" /> <ContractRemindList v-if="leftMenu === 'contractRemind'" />
<CustomerFollowList v-if="leftMenu === 'customerFollow'" /> <CustomerFollowList v-if="leftMenu === 'customerFollow'" />
<CustomerPutPoolRemindList v-if="leftMenu === 'customerPutPoolRemind'" /> <CustomerPutPoolRemindList v-if="leftMenu === 'customerPutPoolRemind'" />
<RemindReceivables v-if="leftMenu === 'remindReceivables'" /> <RemindReceivables v-if="leftMenu === 'remindReceivables'" />
@ -33,25 +33,26 @@ import CustomerFollowList from './components/CustomerFollowList.vue'
import CustomerTodayContactList from './components/CustomerTodayContactList.vue' import CustomerTodayContactList from './components/CustomerTodayContactList.vue'
import CustomerPutPoolRemindList from './components/CustomerPutPoolRemindList.vue' import CustomerPutPoolRemindList from './components/CustomerPutPoolRemindList.vue'
import ClueFollowList from './components/ClueFollowList.vue' import ClueFollowList from './components/ClueFollowList.vue'
import CheckContract from './tables/CheckContract.vue' import ContractAuditList from './components/ContractAuditList.vue'
import CheckReceivables from './tables/CheckReceivables.vue' import ContractRemindList from './components/ContractRemindList.vue'
import EndContract from './tables/EndContract.vue'
import RemindReceivables from './tables/RemindReceivables.vue' import RemindReceivables from './tables/RemindReceivables.vue'
import CheckReceivables from './tables/CheckReceivables.vue'
import * as CustomerApi from '@/api/crm/customer' import * as CustomerApi from '@/api/crm/customer'
import * as ClueApi from '@/api/crm/clue' import * as ClueApi from '@/api/crm/clue'
import * as ContractApi from '@/api/crm/contract'
defineOptions({ name: 'CrmBacklog' }) defineOptions({ name: 'CrmBacklog' })
const leftMenu = ref('customerTodayContact') const leftMenu = ref('customerTodayContact')
const customerTodayContactCount = ref(0)
const clueFollowCount = ref(0) const clueFollowCount = ref(0)
const customerFollowCount = ref(0) const customerFollowCount = ref(0)
const customerPutPoolRemindCount = 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 checkReceivablesCount = ref(0)
const remindReceivablesCount = ref(0) const remindReceivablesCount = ref(0)
const endContractCount = ref(0)
const leftSides = ref([ const leftSides = ref([
{ {
@ -76,8 +77,8 @@ const leftSides = ref([
}, },
{ {
name: '待审核合同', name: '待审核合同',
menu: 'checkContract', menu: 'contractAudit',
count: checkContractCount count: contractAuditCount
}, },
{ {
name: '待审核回款', name: '待审核回款',
@ -91,8 +92,8 @@ const leftSides = ref([
}, },
{ {
name: '即将到期的合同', name: '即将到期的合同',
menu: 'endContract', menu: 'contractRemind',
count: endContractCount count: contractRemindCount
} }
]) ])
@ -110,10 +111,10 @@ const getCount = () => {
) )
CustomerApi.getFollowCustomerCount().then((count) => (customerFollowCount.value = count)) CustomerApi.getFollowCustomerCount().then((count) => (customerFollowCount.value = count))
ClueApi.getFollowClueCount().then((count) => (clueFollowCount.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.getCheckReceivablesCount().then((count) => (checkReceivablesCount.value = count))
BacklogApi.getRemindReceivablePlanCount().then((count) => (remindReceivablesCount.value = count)) BacklogApi.getRemindReceivablePlanCount().then((count) => (remindReceivablesCount.value = count))
BacklogApi.getEndContractCount().then((count) => (endContractCount.value = count))
} }
/** 激活时 */ /** 激活时 */

View File

@ -5,11 +5,32 @@
<Icon class="mr-5px" icon="system-uicons:contacts" /> <Icon class="mr-5px" icon="system-uicons:contacts" />
创建联系人 创建联系人
</el-button> </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> </el-row>
<!-- 列表 --> <!-- 列表 -->
<ContentWrap class="mt-10px"> <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"> <el-table-column label="姓名" fixed="left" align="center" prop="name">
<template #default="scope"> <template #default="scope">
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)"> <el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
@ -37,12 +58,19 @@
<!-- 表单弹窗添加 --> <!-- 表单弹窗添加 -->
<ContactForm ref="formRef" @success="getList" /> <ContactForm ref="formRef" @success="getList" />
<!-- 关联商机选择弹框 -->
<ContactListModal
ref="contactModalRef"
:customer-id="props.customerId"
@success="createContactBusinessList"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import * as ContactApi from '@/api/crm/contact' import * as ContactApi from '@/api/crm/contact'
import ContactForm from './../ContactForm.vue' import ContactForm from './../ContactForm.vue'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { BizTypeEnum } from '@/api/crm/permission' import { BizTypeEnum } from '@/api/crm/permission'
import ContactListModal from './ContactListModal.vue'
defineOptions({ name: 'CrmContactList' }) defineOptions({ name: 'CrmContactList' })
const props = defineProps<{ const props = defineProps<{
@ -58,8 +86,10 @@ const list = ref([]) // 列表的数据
const queryParams = reactive({ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, 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 () => { const getList = async () => {
@ -106,6 +136,41 @@ const openDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } }) 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从而加载最新的列表 */ /** 监听打开的 bizId + bizType从而加载最新的列表 */
watch( watch(
() => [props.bizId, props.bizType], () => [props.bizId, props.bizType],

View File

@ -79,7 +79,7 @@
<el-tab-pane label="下属负责的" name="3" /> <el-tab-pane label="下属负责的" name="3" />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="合同编号" prop="no" width="160" /> <el-table-column align="center" fixed="left" label="合同编号" prop="no" width="180" />
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160"> <el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
<template #default="scope"> <template #default="scope">
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)"> <el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'ContactsCountRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getContactsCountRank(props.queryParams) const rankingList = await StatisticsRankApi.getContactsCountRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'ContractCountRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getContractCountRank(props.queryParams) const rankingList = await StatisticsRankApi.getContractCountRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'ContractPriceRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getContractPriceRank(props.queryParams) const rankingList = await StatisticsRankApi.getContractPriceRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'CustomerCountRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getCustomerCountRank(props.queryParams) const rankingList = await StatisticsRankApi.getCustomerCountRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'FollowCountRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getFollowCountRank(props.queryParams) const rankingList = await StatisticsRankApi.getFollowCountRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'FollowCustomerCountRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getFollowCustomerCountRank(props.queryParams) const rankingList = await StatisticsRankApi.getFollowCustomerCountRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'ProductSalesRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -80,7 +80,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getProductSalesRank(props.queryParams) const rankingList = await StatisticsRankApi.getProductSalesRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -18,7 +18,7 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <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 { EChartsOption } from 'echarts'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -26,7 +26,7 @@ defineOptions({ name: 'ReceivablePriceRank' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
const loading = ref(false) // const loading = ref(false) //
const list = ref<BiRankRespVO[]>([]) // const list = ref<StatisticsRankRespVO[]>([]) //
/** 柱状图配置:横向 */ /** 柱状图配置:横向 */
const echartsOption = reactive<EChartsOption>({ const echartsOption = reactive<EChartsOption>({
@ -81,7 +81,7 @@ const echartsOption = reactive<EChartsOption>({
const loadData = async () => { const loadData = async () => {
// 1. // 1.
loading.value = true loading.value = true
const rankingList = await RankApi.getReceivablePriceRank(props.queryParams) const rankingList = await StatisticsRankApi.getReceivablePriceRank(props.queryParams)
// 2.1 Echarts // 2.1 Echarts
if (echartsOption.dataset && echartsOption.dataset['source']) { if (echartsOption.dataset && echartsOption.dataset['source']) {
echartsOption.dataset['source'] = clone(rankingList).reverse() echartsOption.dataset['source'] = clone(rankingList).reverse()

View File

@ -90,7 +90,7 @@ import * as DeptApi from '@/api/system/dept'
import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime' import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
defineOptions({ name: 'CrmBiRank' }) defineOptions({ name: 'CrmStatisticsRank' })
const queryParams = reactive({ const queryParams = reactive({
deptId: useUserStore().getUser.deptId, deptId: useUserStore().getUser.deptId,