diff --git a/src/api/crm/message/index.ts b/src/api/crm/message/index.ts new file mode 100644 index 00000000..fcd5fbd7 --- /dev/null +++ b/src/api/crm/message/index.ts @@ -0,0 +1,39 @@ +import request from '@/config/axios' + +export interface CustomerVO { + id?: number + name: string + industryId: number + level: number + source: number + followUpStatus?: boolean + lockStatus?: boolean + dealStatus?: boolean + mobile: string + telephone: string + website: string + qq: string + wechat: string + email: string + description: string + remark: string + ownerUserId?: number + ownerUserName?: string + ownerUserDept?: string + roUserIds?: string + rwUserIds?: string + areaId?: number + areaName?: string + detailAddress: string + contactLastTime?: Date + contactNextTime: Date + createTime?: Date + updateTime?: Date + creator?: string + creatorName?: string +} + +// 查询客户列表 +export const getTodayCustomerPage = async (params) => { + return await request.get({ url: `/crm/message/todayCustomer`, params }) +} diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 8fa8373c..35320edb 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -528,6 +528,16 @@ const remainingRouter: AppRouteRecordRaw[] = [ activeMenu: '/crm/product' }, component: () => import('@/views/crm/product/detail/index.vue') + }, + { + path: 'message', + name: 'CrmMessage', + meta: { + title: '待办事项', + noCache: true, + hidden: true + }, + component: () => import('@/views/crm/message/index.vue') } ] } diff --git a/src/views/crm/message/index.vue b/src/views/crm/message/index.vue new file mode 100644 index 00000000..1f809524 --- /dev/null +++ b/src/views/crm/message/index.vue @@ -0,0 +1,134 @@ +<template> + <el-row :gutter="20"> + <el-col :span="4" class="min-w-[200px]"> + <div class="side-item-list"> + <div + v-for="(item, index) in leftSides" + :key="index" + :class="leftType == item.infoType ? 'side-item-select' : 'side-item-default'" + class="side-item" + @click="sideClick(item)" + > + {{ item.name }} + <el-badge v-if="item.msgCount > 0" :max="99" :value="item.msgCount" /> + </div> + </div> + </el-col> + <el-col :span="20" :xs="24"> + <TodayCustomer v-if="leftType === 'todayCustomer'" /> + <FollowLeads v-if="leftType === 'followLeads'" /> + </el-col> + </el-row> +</template> + +<script lang="ts" setup> +import TodayCustomer from './tables/TodayCustomer.vue' +import FollowLeads from './tables/FollowLeads.vue' + +const leftType = ref('todayCustomer') +const leftSides = ref([ + { + name: '今日需联系客户', + infoType: 'todayCustomer', + msgCount: 1, + tips: '下次跟进时间为今日的客户' + }, + { + name: '分配给我的线索', + infoType: 'followLeads', + msgCount: 0, + tips: '转移之后未跟进的线索' + }, + { + name: '分配给我的客户', + infoType: 'followCustomer', + msgCount: 0, + tips: '转移、领取、分配之后未跟进的客户,默认显示自己负责的客户' + }, + { + name: '待进入公海的客户', + infoType: 'putInPoolRemind', + msgCount: 0, + tips: '' + }, + { + name: '待审核合同', + infoType: 'checkContract', + msgCount: 0, + tips: '' + }, + { + name: '待审核回款', + crmType: 'receivables', + infoType: 'checkReceivables', + msgCount: 0, + tips: '' + }, + { + name: '待回款提醒', + infoType: 'remindReceivablesPlan', + msgCount: 4, + tips: '' + }, + { + name: '即将到期的合同', + infoType: 'endContract', + msgCount: 20, + tips: '根据“合同到期时间”及设置的“提前提醒天数”提醒' + } +]) + +/** + * 侧边点击 + */ +const sideClick = (item) => { + leftType.value = item.infoType +} +</script> + +<style lang="scss" scoped> +.side-item-list { + top: 0; + bottom: 0; + left: 0; + z-index: 1; + font-size: 14px; + background-color: white; + border: 1px solid #e6e6e6; + border-radius: 5px; + + .side-item { + position: relative; + height: 50px; + padding: 0 20px; + line-height: 50px; + cursor: pointer; + + i { + color: #999; + } + } +} + +.side-item-default { + color: #333; + border-right: 2px solid transparent; +} + +.side-item-select { + color: #409eff; + background-color: #ecf5ff; + border-right: 2px solid var(--el-color-primary); +} + +.el-badge :deep(.el-badge__content) { + top: 0; + border: none; +} + +.el-badge { + position: absolute; + top: 0; + right: 15px; +} +</style> diff --git a/src/views/crm/message/tables/FollowLeads.vue b/src/views/crm/message/tables/FollowLeads.vue new file mode 100644 index 00000000..c94199fb --- /dev/null +++ b/src/views/crm/message/tables/FollowLeads.vue @@ -0,0 +1,14 @@ +<!-- 分配给我的线索 --> +<template> + <div> + TODO: 分配给我的线索 + </div> +</template> + +<script setup lang="ts" name="FollowLeads"> + +</script> + +<style scoped> + +</style> diff --git a/src/views/crm/message/tables/TodayCustomer.vue b/src/views/crm/message/tables/TodayCustomer.vue new file mode 100644 index 00000000..df70e599 --- /dev/null +++ b/src/views/crm/message/tables/TodayCustomer.vue @@ -0,0 +1,201 @@ +<template> + <ContentWrap> + <div class="pb-5 text-xl"> + {{ title }} + </div> + <!-- 搜索工作栏 --> + <el-form + ref="queryFormRef" + :inline="true" + :model="queryParams" + class="-mb-15px" + label-width="68px" + > + <el-form-item label="状态" prop="contactStatus"> + <el-select v-model="queryParams.contactStatus" class="!w-240px" placeholder="状态"> + <el-option + v-for="(option, index) in CONTACT_STATUS" + :label="option.label" + :value="option.value" + :key="index" + /> + </el-select> + </el-form-item> + <el-form-item label="归属" prop="sceneType"> + <el-select v-model="queryParams.sceneType" class="!w-240px" placeholder="归属"> + <el-option + v-for="(option, index) in SCENE_TYPES" + :label="option.label" + :value="option.value" + :key="index" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"> + <Icon class="mr-5px" icon="ep:search" /> + 搜索 + </el-button> + <el-button @click="resetQuery(undefined)"> + <Icon class="mr-5px" icon="ep:refresh" /> + 重置 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + <ContentWrap> + <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true"> + <el-table-column align="center" label="编号" prop="id" /> + <el-table-column align="center" 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="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"> + <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 + :formatter="dateFormatter" + align="center" + label="下次联系时间" + prop="contactNextTime" + width="180px" + /> + <el-table-column align="center" label="备注" prop="remark" width="200" /> + <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> + <!-- TODO @puhui999:距进入公海天数 --> + <el-table-column + :formatter="dateFormatter" + align="center" + label="最后跟进时间" + prop="contactLastTime" + width="180px" + /> + <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="100px" /> + <el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" /> + <el-table-column align="center" label="创建人" prop="creatorName" width="100px" /> + </el-table> + <!-- 分页 --> + <Pagination + v-model:limit="queryParams.pageSize" + v-model:page="queryParams.pageNo" + :total="total" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script lang="ts" setup name="TodayCustomer"> +import { DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import * as MessageApi from '@/api/crm/message' + +const title = ref('今日需联系客户') +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 列表的数据 +const queryParams = ref<{ + pageNo: number + pageSize: number + contactStatus: number | undefined + sceneType: number | undefined +}>({ + pageNo: 1, + pageSize: 10, + contactStatus: 1, + sceneType: 1 +}) +const queryFormRef = ref() // 搜索的表单 + +const CONTACT_STATUS = [ + { label: '今日需联系', value: 1 }, + { label: '已逾期', value: 2 }, + { label: '已联系', value: 3 } +] + +const SCENE_TYPES = [ + { label: '我负责的', value: 1 }, + { label: '我跟进的', value: 2 }, + { label: '我参与的', value: 3 }, + { label: '下属负责的', value: 4 } +] + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await MessageApi.getTodayCustomerPage(queryParams.value) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.value.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = (func: Function | undefined = undefined) => { + queryFormRef.value.resetFields() + queryParams.value = { + pageNo: 1, + pageSize: 10, + contactStatus: 1, + sceneType: 1 + } + func && func() + handleQuery() +} + +/** 打开客户详情 */ +const { push } = useRouter() +const openDetail = (id: number) => { + push({ name: 'CrmCustomerDetail', params: { id } }) +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> + +<style lang="scss"></style>