Просмотр исходного кода

线索公海+销售线索增加最新跟进记录

Yannay 1 месяц назад
Родитель
Сommit
51b343ae88
4 измененных файлов с 186 добавлено и 4 удалено
  1. 183 2
      pages/clue/post/index.vue
  2. 1 1
      pages/privateClue/index.vue
  3. 1 1
      pages/publicClue/index.vue
  4. 1 0
      utils/api.js

+ 183 - 2
pages/clue/post/index.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-	<view class="post_item">
2
+	<view class="post_item" @click.stop="handlePostRootClick">
3 3
 		<view class="post_top">
4 4
 			<view class="top_left">{{ item.name }}
5 5
 				&nbsp;&nbsp;&nbsp;
@@ -42,6 +42,14 @@
42 42
 			<view class="info">
43 43
 				<view class="operation">运营人 {{ item.clueOperationName ? item.clueOperationName : "-" }}</view>
44 44
 			</view>
45
+			<view class="info latest_follow_row" @click.stop="handleShowFollowDetail(item)">
46
+				<view class="label">最新跟进记录:</view>
47
+				<view class="latest_follow_content">
48
+					<text class="latest_follow_text">{{ item.latestDynamicData || '暂无' }}</text>
49
+					<text class="latest_follow_time" v-if="item.latestDynamicTime">{{ item.latestDynamicTime }}</text>
50
+				</view>
51
+				<!-- <view class="latest_follow_add" @click.stop="handleAddFollow(item)">增加</view> -->
52
+			</view>
45 53
 		</view>
46 54
 		<view class="clue_state_wrap">
47 55
 			<view class="clue_state">
@@ -65,6 +73,27 @@
65 73
 			<view>发单</view>
66 74
 		</view>
67 75
 		<add-inquiry-dialog ref="inquiryDialog" :clueId="clueId" :isClue="true" :editOrAdd="editOrAdd" :editInfo="editInfo" @cancel="handleInquiryCancel" :type="1"/>
76
+		<!-- 最新跟进记录详情弹窗;关闭时记时间戳,避免蒙层点击冒泡触发详情跳转 -->
77
+		<u-popup :show="followPopupVisible" mode="bottom" round="16" @close="onFollowPopupClose"
78
+			:closeOnClickOverlay="true" @open="loadFollowList">
79
+			<view class="follow_popup">
80
+				<view class="follow_popup_title">跟进记录</view>
81
+				<scroll-view scroll-y class="follow_popup_list" v-if="followList.length">
82
+					<view class="follow_item" v-for="f in followList" :key="f.id">
83
+						<view class="follow_top">
84
+							<text class="person_name">{{ f.createNickname || f.createNickName || '-' }}</text>
85
+							<text class="follow_time">{{ f.createTime }}</text>
86
+						</view>
87
+						<view class="follow_content">{{ f.content }}</view>
88
+					</view>
89
+				</scroll-view>
90
+				<view class="follow_popup_empty" v-else-if="!followLoading">{{ followListError || '暂无跟进记录' }}</view>
91
+				<view class="follow_popup_loading" v-else>加载中...</view>
92
+				<!-- <view class="follow_popup_footer">
93
+					<u-button type="primary" @click="handleAddFollowFromPopup">增加跟进</u-button>
94
+				</view> -->
95
+			</view>
96
+		</u-popup>
68 97
 	</view>
69 98
 </template>
70 99
 
@@ -96,7 +125,13 @@ export default {
96 125
 			caseStatusDicts: [],
97 126
 			clueId: '',
98 127
 			editOrAdd: 'add',
99
-			editInfo: {}
128
+			editInfo: {},
129
+			followPopupVisible: false,
130
+			followList: [],
131
+			followLoading: false,
132
+			followListError: '',
133
+			currentFollowClue: null,
134
+			closedFollowPopupAt: 0
100 135
 		}
101 136
 	},
102 137
 	methods: {
@@ -193,6 +228,51 @@ export default {
193 228
 		handleInquiryCancel() {
194 229
 			this.$refs.inquiryDialog.closeDialog()
195 230
 		},
231
+		// 卡片根节点点击:若刚因点蒙层关闭弹窗则不跳转,否则通知父组件跳转详情
232
+		handlePostRootClick() {
233
+			if (Date.now() - this.closedFollowPopupAt < 400) return
234
+			this.$emit('to-detail', this.item)
235
+		},
236
+		onFollowPopupClose() {
237
+			this.followPopupVisible = false
238
+			this.closedFollowPopupAt = Date.now()
239
+		},
240
+		// 点击最新跟进记录,打开详情弹窗
241
+		handleShowFollowDetail(item) {
242
+			this.currentFollowClue = item
243
+			this.followPopupVisible = true
244
+			this.followList = []
245
+			this.followListError = ''
246
+		},
247
+		// 弹窗打开时加载该线索的跟进列表
248
+		async loadFollowList() {
249
+			if (!this.currentFollowClue || !this.currentFollowClue.id) return
250
+			this.followLoading = true
251
+			this.followListError = ''
252
+			try {
253
+				const res = await uni.$u.api.getClueFollowListByClueId({ clueId: this.currentFollowClue.id })
254
+				this.followList = (res.data || res) || []
255
+			} catch (e) {
256
+				console.error('获取跟进记录失败', e)
257
+				this.followListError = '加载失败'
258
+				this.followList = []
259
+			} finally {
260
+				this.followLoading = false
261
+			}
262
+		},
263
+		// 增加跟进:跳转添加跟进页
264
+		handleAddFollow(item) {
265
+			const id = (item && item.id) || (this.currentFollowClue && this.currentFollowClue.id)
266
+			if (!id) return
267
+			uni.navigateTo({
268
+				url: `/pages/addFollow/index?clueId=${id}`
269
+			})
270
+			this.followPopupVisible = false
271
+		},
272
+		// 弹窗内点击「增加跟进」
273
+		handleAddFollowFromPopup() {
274
+			this.handleAddFollow(this.currentFollowClue)
275
+		},
196 276
 	},
197 277
 }
198 278
 </script>
@@ -250,6 +330,42 @@ export default {
250 330
 				flex-wrap: wrap;
251 331
 			}
252 332
 		}
333
+
334
+		.latest_follow_row {
335
+			align-items: flex-start;
336
+			flex-wrap: wrap;
337
+
338
+			.label {
339
+				flex: 0 0 90px;
340
+				color: #666;
341
+			}
342
+
343
+			.latest_follow_content {
344
+				flex: 1;
345
+				min-width: 0;
346
+				display: flex;
347
+				flex-direction: column;
348
+				gap: 4px;
349
+			}
350
+
351
+			.latest_follow_text {
352
+				font-size: 13px;
353
+				color: #333;
354
+				word-break: break-all;
355
+			}
356
+
357
+			.latest_follow_time {
358
+				font-size: 12px;
359
+				color: #999;
360
+			}
361
+
362
+			.latest_follow_add {
363
+				flex-shrink: 0;
364
+				color: #4fa5fe;
365
+				font-size: 14px;
366
+				margin-left: 8px;
367
+			}
368
+		}
253 369
 	}
254 370
 
255 371
 	.clue_state_wrap {
@@ -308,4 +424,69 @@ export default {
308 424
 		background-color: #fff;
309 425
 	}
310 426
 }
427
+
428
+.follow_popup {
429
+	padding: 24rpx 30rpx 40rpx;
430
+	max-height: 70vh;
431
+	display: flex;
432
+	flex-direction: column;
433
+
434
+	.follow_popup_title {
435
+		font-size: 34rpx;
436
+		font-weight: 600;
437
+		margin-bottom: 24rpx;
438
+		text-align: center;
439
+	}
440
+
441
+	.follow_popup_list {
442
+		flex: 1;
443
+		max-height: 50vh;
444
+		margin-bottom: 24rpx;
445
+	}
446
+
447
+	.follow_item {
448
+		padding: 20rpx 0;
449
+		border-bottom: 1rpx solid #eee;
450
+
451
+		&:last-child {
452
+			border-bottom: none;
453
+		}
454
+
455
+		.follow_top {
456
+			display: flex;
457
+			justify-content: space-between;
458
+			align-items: center;
459
+			margin-bottom: 12rpx;
460
+			font-size: 24rpx;
461
+
462
+			.person_name {
463
+				color: #333;
464
+				font-weight: 500;
465
+			}
466
+
467
+			.follow_time {
468
+				color: #999;
469
+			}
470
+		}
471
+
472
+		.follow_content {
473
+			font-size: 26rpx;
474
+			color: #666;
475
+			line-height: 1.5;
476
+			word-break: break-all;
477
+		}
478
+	}
479
+
480
+	.follow_popup_empty,
481
+	.follow_popup_loading {
482
+		text-align: center;
483
+		color: #999;
484
+		font-size: 28rpx;
485
+		padding: 60rpx 0;
486
+	}
487
+
488
+	.follow_popup_footer {
489
+		padding-top: 20rpx;
490
+	}
491
+}
311 492
 </style>

+ 1 - 1
pages/privateClue/index.vue

@@ -36,7 +36,7 @@
36 36
 			<show-emtry v-if="listData.length === 0"></show-emtry>
37 37
 			<view class="case_main_wrap" v-else>
38 38
 				<post v-for="(item,index) in listData" :key="item.id + index" :item="item" :dicts="dicts"
39
-					:type="queryParams.type" @click.native="handleToDetail(item)" @inquirySuccess="inquirySuccess">
39
+					:type="queryParams.type" @to-detail="handleToDetail" @inquirySuccess="inquirySuccess">
40 40
 				</post>
41 41
 				<u-loadmore :status="finished ? 'nomore' : loadStatus" icon line />
42 42
 			</view>

+ 1 - 1
pages/publicClue/index.vue

@@ -36,7 +36,7 @@
36 36
 			<show-emtry v-if="listData.length === 0"></show-emtry>
37 37
 			<view class="case_main_wrap" v-else>
38 38
 				<post v-for="(item,index) in listData" :key="item.id + index" :item="item" :dicts="dicts"
39
-					:type="queryParams.type" @click.native="handleToDetail(item)">
39
+					:type="queryParams.type" @to-detail="handleToDetail">
40 40
 				</post>
41 41
 				<u-loadmore :status="finished ? 'nomore' : loadStatus" icon line />
42 42
 			</view>

+ 1 - 0
utils/api.js

@@ -60,6 +60,7 @@ const install = (Vue, vm) => {
60 60
 		getClueMainInfoById: (params = {}, config = {}) => http.get(store.state.user.path + '/clueMainInfo/getClueMainInfoById', { params, ...config }),
61 61
 		getClueAdInfoByClueId: (params = {}, config = {}) => http.get(store.state.user.path + '/clueAdInfo/getClueAdInfoByClueId', { params, ...config }),
62 62
 		getClueFollowList: (params = {}, config = {}) => http.get(store.state.user.path + '/clueFollow/getClueFollowList', { params, ...config }),
63
+		getClueFollowListByClueId: (params = {}, config = {}) => http.get(store.state.user.path + '/clueFollow/getClueFollowListByClueId', { params, ...config }),
63 64
 		deleteClueFollow: (data, config = {}) => http.post(store.state.user.path + '/clueFollow/deleteClueFollow', data),
64 65
 		updateClueFixedFieldsClueOwner: (data, config = {}) => http.post(store.state.user.path + '/clueFixedFields/updateClueFixedFieldsClueOwner', data),
65 66
 		addClueFollow: (data, config = {}) => http.post(store.state.user.path + '/clueFollow/addClueFollow', data),