OfficeSystem/components/customer/customer-detail/FollowupTab.vue
2025-11-12 15:18:21 +08:00

305 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="tab-content">
<view class="followup-timeline">
<template v-for="(group, groupIndex) in groupedFollowupList" :key="groupIndex">
<view
class="followup-item"
v-for="(item, index) in group.items"
:key="index"
@click="handleFollowupClick(item)"
>
<view class="date-header" v-if="index === 0">
<view class="date-dot"></view>
<text class="date-text">{{ group.date }}</text>
</view>
<view class="followup-card">
<view class="followup-header">
<image
class="followup-avatar"
:src="item.userAvatar || '/static/default-avatar.png'"
mode="aspectFill"
/>
<view class="followup-user-info">
<text class="followup-user-name">{{ item.userName }}</text>
</view>
</view>
<text class="followup-text">{{ item.content }}</text>
<view class="followup-time-wrapper">
<text class="time-icon">🕐</text>
<text class="followup-time">{{ formatTimeOnly(item.followTime) }}</text>
</view>
<text class="followup-arrow"></text>
</view>
</view>
</template>
<view class="empty-state" v-if="followupList.length === 0">
<text>暂无跟进记录</text>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
followupList: {
type: Array,
default: () => []
}
});
const emit = defineEmits(['followup-click']);
// 按日期分组的跟进记录
const groupedFollowupList = computed(() => {
if (!props.followupList || props.followupList.length === 0) {
return [];
}
const groups = {};
props.followupList.forEach(item => {
if (!item.followTime) return;
const dateKey = formatDate(item.followTime);
if (!groups[dateKey]) {
groups[dateKey] = [];
}
groups[dateKey].push(item);
});
// 转换为数组并按日期倒序排列
return Object.keys(groups)
.sort((a, b) => {
// 从原始数据中获取完整日期进行比较
const getFullDate = (dateKey) => {
const items = groups[dateKey];
if (items && items.length > 0 && items[0].followTime) {
return new Date(items[0].followTime);
}
return new Date();
};
return getFullDate(b) - getFullDate(a);
})
.map(date => ({
date,
items: groups[date].sort((a, b) => {
// 同一天内的记录按时间倒序排列
const timeA = new Date(a.followTime || 0).getTime();
const timeB = new Date(b.followTime || 0).getTime();
return timeB - timeA;
})
}));
});
// 格式化日期(仅显示月-日)
const formatDate = (dateTime) => {
if (!dateTime) return '';
try {
const date = new Date(dateTime);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
} catch (e) {
return '';
}
};
// 格式化时间(仅显示时:分)
const formatTimeOnly = (dateTime) => {
if (!dateTime) return '';
try {
const date = new Date(dateTime);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
} catch (e) {
return dateTime;
}
};
// 跟进项点击
const handleFollowupClick = (item) => {
if (item && item.followId) {
uni.navigateTo({
url: `/pages/customer/follow/detail/index?followId=${item.followId}`
});
}
emit('followup-click', item);
};
// 详情页承载编辑与删除操作
</script>
<style lang="scss" scoped>
.tab-content {
padding: 16px;
background-color: #f0f0f0;
min-height: 100%;
}
// 跟进动态样式
.followup-timeline {
position: relative;
padding-bottom: 16px;
}
.followup-item {
margin-bottom: 16px;
position: relative;
transition: transform 0.2s ease;
&:active {
transform: scale(0.98);
}
}
.date-header {
display: flex;
align-items: center;
margin-bottom: 12px;
padding-left: 4px;
}
.date-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #1976d2;
margin-right: 10px;
position: relative;
box-shadow: 0 2px 4px rgba(25, 118, 210, 0.3);
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #fff;
}
}
.date-text {
font-size: 15px;
color: #333;
font-weight: 600;
letter-spacing: 0.5px;
}
.followup-card {
background-color: #ffffff;
border-radius: 12px;
padding: 16px;
padding-right: 32px; /* 为右侧箭头预留空间 */
cursor: pointer;
position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
border: 1px solid rgba(0, 0, 0, 0.04);
&:active {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
transform: translateY(1px);
}
}
.followup-header {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.followup-avatar {
width: 20px;
height: 20px;
border-radius: 20px;
margin-right: 12px;
background-color: #e0e0e0;
border: 2px solid #f5f5f5;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.followup-user-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.followup-user-name {
font-size: 16px;
color: #333;
font-weight: 600;
line-height: 1.3;
}
.followup-user-role {
font-size: 12px;
color: #888;
line-height: 1.2;
}
/* 编辑与删除按钮移动到详情页 */
.followup-arrow {
font-size: 22px;
color: #d0d0d0;
font-weight: 300;
transition: color 0.2s ease;
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
}
.followup-item:active .followup-arrow {
color: #1976d2;
}
.followup-text {
font-size: 15px;
color: #555;
line-height: 1.7;
margin-bottom: 12px;
word-break: break-word;
}
.followup-time-wrapper {
display: flex;
align-items: center;
gap: 6px;
padding-top: 8px;
border-top: 1px solid #f0f0f0;
}
.time-icon {
font-size: 13px;
color: #999;
}
.followup-time {
font-size: 12px;
color: #999;
line-height: 1.2;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
font-size: 15px;
background-color: #ffffff;
border-radius: 12px;
margin: 20px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
</style>