index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <template>
  2. <div v-if="hasHistoryPermission" class="message_overview">
  3. <!-- 左侧:产品概览与最新状态 -->
  4. <div class="left_panel hide_scrollbar">
  5. <div v-if="productDataList?.length > 0" class="product_list">
  6. <template v-for="(item,index) in productDataList" :key="item.productId" >
  7. <div class="list_item">
  8. <div class="item_title">{{item.productName}}</div>
  9. <div class="item_info">跟进次数:{{item.count}}</div>
  10. </div>
  11. <div v-if="index !== (productDataList?.length - 1)" class="page_solid_line" />
  12. </template>
  13. </div>
  14. <div v-else class="no_data_bg">
  15. <span>暂无数据</span>
  16. </div>
  17. </div>
  18. <!-- 中间:搜索与 我的跟进 -->
  19. <div class="center_panel">
  20. <div class="page_content hide_scrollbar " ref="scrollDom">
  21. <div v-if="scrollMsg.isScrollTop" :style="{height:scrollMsg.headerHeight || '132px',flexShrink:'0'}"></div>
  22. <div class="search_page" ref="headerDom">
  23. <DetailSelectSearch v-if="scrollMsg.isScrollTop" @searchChange="HandleSearchChange"></DetailSelectSearch>
  24. <DetailBlockSearch v-else @searchChange="HandleSearchChange"></DetailBlockSearch>
  25. </div>
  26. <!-- style="margin-top: 16px;" -->
  27. <FollowCard v-if="historyFollowRecord?.length > 0" :cardList="historyFollowRecord">
  28. <template #operation="{row}">
  29. <i></i>
  30. </template>
  31. </FollowCard>
  32. <div v-else class="no_data_bg">
  33. <span>暂无数据</span>
  34. </div>
  35. </div>
  36. <Pagination :showPageSize="true" :currentPage="currentPage"
  37. :pageSize="pageSize" :totalNum="totalNum"
  38. @paginationChange="handlePaginationChange" >
  39. <template #paginationDesc>
  40. {{ `共 ${totalNum} 条数据` }}
  41. </template>
  42. </Pagination>
  43. </div>
  44. <!-- 右侧:跟进记录统计 -->
  45. <div class="right_panel">
  46. <!-- 之前归属人的跟进记录 -->
  47. <!-- 历史跟进的状态是否展示 要判断是否给当前用户开放历史跟进权限 -->
  48. <div class="history_statistics">
  49. <div class="stat_header">
  50. <span>跟进记录</span>
  51. </div>
  52. <div class="stat_numbers">
  53. <div class="num_item">
  54. <div class="num">{{historyFollowData.hsitoryProductNum}}</div>
  55. <div class="label">历史产品</div>
  56. </div>
  57. <div class="vertical_solid"></div>
  58. <div class="num_item">
  59. <div class="num">{{historyFollowData.historyPeopleNum}}</div>
  60. <div class="label">历史人员</div>
  61. </div>
  62. <div class="vertical_solid"></div>
  63. <div class="num_item">
  64. <div class="num">{{historyFollowData.followTimes}}</div>
  65. <div class="label">跟进次数</div>
  66. </div>
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. <div v-else class="message_overview no_data_bg">
  72. <span>暂无权限 </span>
  73. </div>
  74. </template>
  75. <script setup lang="ts">
  76. import { ref,onMounted } from 'vue';
  77. import { useRoute } from 'vue-router';
  78. import DetailSelectSearch from '@/components/HighSeasCustomerHeaderSearch/DetailSelectSearch/index.vue'
  79. import DetailBlockSearch from '@/components/HighSeasCustomerHeaderSearch/DetailBlockSearch/index.vue'
  80. import {historyFollowSearchList} from '@/components/HighSeasCustomerHeaderSearch/DetailBlockSearch/searchBlock'
  81. import {historyFollowSearchSelectList} from '@/components/HighSeasCustomerHeaderSearch/DetailSelectSearch/formSearchGroup'
  82. import {historyFollowSearchParams} from '@/components/HighSeasCustomerHeaderSearch/commonVar'
  83. import FollowCard from '@/components/FollowCard.vue';
  84. import Pagination from '@/baseComponents/Pagination.vue';
  85. import {
  86. getHighSeasCustomerDetailApi,
  87. } from '@/api/highSeasCustomer'
  88. import {winnerCaseStatusMsg} from '@/components/dict'
  89. import { useHeaderFixed } from '@/baseComponents/useHeaderFixed'
  90. interface ProductStatusItm {
  91. productId:string,
  92. productName:string,
  93. count:string
  94. }
  95. const route = useRoute()
  96. const scrollDom = ref()
  97. const headerDom = ref()
  98. const scrollMsg = useHeaderFixed(scrollDom,headerDom,132) //搜索栏是否需要在滚动的时候固定到顶部
  99. /** 1. 页面展示有关变量 */
  100. // 左侧列表的跟进产品统计
  101. const productDataList = ref<ProductStatusItm[]>([]);
  102. // 历史跟进记录
  103. const historyFollowRecord = ref([])
  104. // 历史跟进 数据总览
  105. const historyFollowData = ref({
  106. hsitoryProductNum:0,
  107. historyPeopleNum:0,
  108. followTimes:0,
  109. })
  110. /**
  111. * 2. 列表分页参数及相关操作
  112. */
  113. const currentPage = ref(1)
  114. const pageSize = ref(20)
  115. const totalNum = ref(0)
  116. const hasHistoryPermission = ref(true) //是否有查看历史跟进记录的权限
  117. const handlePaginationChange = async (params)=>{
  118. const {type,val} = params
  119. if(type == 'currentPage'){
  120. currentPage.value = val
  121. }else if(type == 'pageSize'){
  122. pageSize.value = val
  123. }
  124. await GetHighSeasCustomerDetail()
  125. }
  126. // 获取信息概览页面的有关数据
  127. const GetHighSeasCustomerDetail = async ()=>{
  128. let params = {
  129. customerId: route.query.id,
  130. productId: historyFollowSearchParams.value.product == 'all' ? '' : historyFollowSearchParams.value.product,
  131. followUpStatus: historyFollowSearchParams.value.followStatus == 'all' ? '' : historyFollowSearchParams.value.followStatus,
  132. followUpUser: historyFollowSearchParams.value.member == 'all' ? '' : historyFollowSearchParams.value.member,
  133. pageNum:currentPage.value,
  134. pageSize:pageSize.value
  135. }
  136. let res = await getHighSeasCustomerDetailApi(params)
  137. if(res?.code == 200){
  138. const {followOverViewData,productOverviewData,
  139. selectData,dataPageList,historyFollowStatus
  140. } = res?.data || {}
  141. hasHistoryPermission.value = historyFollowStatus === 0 ? false : true
  142. // 右侧 跟进记录
  143. historyFollowData.value = {
  144. hsitoryProductNum:followOverViewData.historyProductCount,
  145. historyPeopleNum:followOverViewData.historyPersonCount,
  146. followTimes:followOverViewData.historyFollowCount,
  147. }
  148. // 左侧 产品列表
  149. productDataList.value = productOverviewData?.map(item=>{
  150. return {
  151. productId:item.productId,
  152. productName:item.productName,
  153. count:item.count,
  154. }
  155. }) || []
  156. // 中间 筛选项列表
  157. const {memberData, productData, followData} = selectData || {}
  158. let memberOptions = memberData?.map(item=>{
  159. return {
  160. label:item.username,
  161. value:item.id,
  162. }
  163. }) || []
  164. let productOptions = productData?.map(item=>{
  165. return {
  166. label:item.name,
  167. value:item.id,
  168. }
  169. }) || []
  170. let followOptions = followData?.map(item=>{
  171. return {
  172. label:item.name,
  173. value:item.id,
  174. }
  175. }) || []
  176. historyFollowSearchList.value = historyFollowSearchList.value.map(item=>{
  177. switch(item.key){
  178. case 'member':
  179. item.options = [
  180. {
  181. label:'全部',
  182. value:'all'
  183. },
  184. ...memberOptions
  185. ]
  186. break
  187. case 'product':
  188. item.options = [
  189. {
  190. label:'全部',
  191. value:'all'
  192. },
  193. ...productOptions
  194. ]
  195. break
  196. case 'followStatus':
  197. item.options = [
  198. {
  199. label:'全部',
  200. value:'all'
  201. },
  202. ...followOptions
  203. ]
  204. break
  205. }
  206. item.defaultValue = historyFollowSearchParams.value[item.key]
  207. return item
  208. })
  209. historyFollowSearchSelectList.value = historyFollowSearchSelectList.value.map(item=>{
  210. switch(item.key){
  211. case 'member':
  212. item.options = [
  213. {
  214. label:'全部人员',
  215. value:'all'
  216. },
  217. ...memberOptions
  218. ]
  219. break
  220. case 'product':
  221. item.options = [
  222. {
  223. label:'全部产品',
  224. value:'all'
  225. },
  226. ...productOptions
  227. ]
  228. break
  229. case 'followStatus':
  230. item.options = [
  231. {
  232. label:'全部状态',
  233. value:'all'
  234. },
  235. ...followOptions
  236. ]
  237. break
  238. }
  239. item.defaultValue = historyFollowSearchParams.value[item.key]
  240. return item
  241. })
  242. // 中间历史跟进信息
  243. totalNum.value = dataPageList.total
  244. historyFollowRecord.value = dataPageList.records?.map(item=>{
  245. let headerTags = []
  246. if(item.followName){
  247. headerTags.push(item.followName)
  248. }
  249. let statusTag = []
  250. // auditStatus 审核状态:0-待审核,1-已通过,2-已驳回 successStatus 是否是赢单 0否 1是
  251. if([0,1,2].includes(item.auditStatus) && item.successStatus === 1){
  252. statusTag.push(winnerCaseStatusMsg(item.auditStatus))
  253. }
  254. return {
  255. headerTitleList:[
  256. item.claimDatetime,
  257. item.customerName,
  258. item.productName
  259. ],
  260. headerTags,
  261. statusTag,
  262. middleDesc:[
  263. `跟进人:${item.claimUserName}`,
  264. `联系人:${item.customerPersonName}`,
  265. `职务:${item.positionName}`
  266. ],
  267. // orderStatus 预约/待办状态,0-待开始,1-已结束,2-已取消(预约) null-已关闭(待办)
  268. taskType:[0,1].includes(item.orderType) && [0,1].includes(item.orderStatus) ? (item.orderType === 0 ? '预约' : '待办') : '',
  269. bottomDesc:`跟进内容:${item.followContent}`,
  270. ...item,
  271. }
  272. }) || []
  273. }
  274. }
  275. // 筛选项变更触发的查询
  276. const HandleSearchChange = async (params,key)=>{
  277. historyFollowSearchParams.value = {
  278. ...historyFollowSearchParams.value,
  279. ...params,
  280. }
  281. await GetHighSeasCustomerDetail()
  282. }
  283. onMounted(async ()=>{
  284. await GetHighSeasCustomerDetail()
  285. })
  286. </script>
  287. <style lang="scss" scoped>
  288. .message_overview {
  289. overflow: auto;
  290. display: flex;
  291. align-items: flex-start;
  292. gap: 20px;
  293. background-color: #fff;
  294. font-size: 14px;
  295. color: #333;
  296. &.no_data_bg{
  297. span{
  298. margin-top: 0px;
  299. padding-top:290px;
  300. }
  301. }
  302. > * {
  303. height: 100%;
  304. overflow: auto;
  305. }
  306. // 左侧栏样式
  307. .left_panel {
  308. width: 200px; // 稍微加宽以容纳内容
  309. display: flex;
  310. flex-direction: column;
  311. gap: 16px;
  312. .product_list {
  313. display: flex;
  314. flex-direction: column;
  315. gap: 16px;
  316. padding:12px;
  317. background: #F8FAFE;
  318. border-radius: 10px;
  319. border: 1px solid #EBEEF5;
  320. .list_item {
  321. display: flex;
  322. flex-direction: column;
  323. gap:4px;
  324. .item_title {
  325. line-height: 20px;
  326. font-weight: 600;
  327. font-size: 14px;
  328. color: #333333;
  329. margin-bottom: 4px;
  330. }
  331. .item_info {
  332. line-height: 17px;
  333. font-weight: 400;
  334. font-size: 12px;
  335. color: #666666;
  336. }
  337. }
  338. }
  339. .no_data_bg{
  340. border: 1px solid #EBEEF5;
  341. height: 150px;
  342. background-position: 50% 10px;
  343. background-size: 200px auto;
  344. min-height: 150px;
  345. span{
  346. margin-top: 110px;
  347. }
  348. }
  349. }
  350. // 中间栏样式
  351. .center_panel {
  352. flex: 1;
  353. display: flex;
  354. flex-direction: column;
  355. .page_content{
  356. flex: 1 1;
  357. overflow: auto;
  358. padding-bottom: 10px;
  359. display: flex;
  360. flex-direction: column;
  361. .no_data_bg{
  362. flex: 1 1;
  363. background-position: 50% 20px;
  364. background-size: 200px auto;
  365. min-height: 150px;
  366. span{
  367. margin-top: 120px;
  368. }
  369. }
  370. }
  371. :deep(.follow_card){
  372. .audit_card{
  373. &:hover{
  374. border:solid #2E64FA 1px;
  375. background-color: rgba(46, 100, 250,0.04);
  376. }
  377. }
  378. }
  379. }
  380. // 右侧栏样式
  381. .right_panel {
  382. width: 320px;
  383. .history_statistics{
  384. padding:16px 14px 12px 16px;
  385. background: #F8FAFE;
  386. border-radius: 10px;
  387. border: 1px solid #EBEEF5;
  388. .stat_header{
  389. display: flex;
  390. align-items: center;
  391. justify-content: space-between;
  392. line-height: 22px;
  393. font-weight: 600;
  394. font-size: 16px;
  395. margin-bottom: 10px;
  396. }
  397. .stat_numbers{
  398. display: flex;
  399. justify-content: space-between;
  400. gap:10px;
  401. align-items: center;
  402. .num_item{
  403. display: flex;
  404. flex-direction: column;
  405. align-items: center;
  406. gap:4px;
  407. color: #666666;
  408. padding:0 12px;
  409. .num{
  410. font-weight: 600;
  411. font-size: 16px;
  412. line-height: 24px;
  413. }
  414. .label{
  415. font-size: 14px;
  416. line-height: 20px;
  417. }
  418. }
  419. .vertical_solid{
  420. width: 1px;
  421. height: 32px;
  422. background-color: #EBEEF5;
  423. }
  424. }
  425. }
  426. }
  427. }
  428. </style>