fix(图表): 修复组件卸载时图表内存泄漏问题

添加组件挂载状态跟踪,确保在组件卸载时正确清理图表实例
移除无用注释,修正描述文字
This commit is contained in:
2025-10-14 18:58:30 +08:00
parent 73c84f7b8d
commit a6f4c96f1f
4 changed files with 43 additions and 22 deletions

View File

@@ -263,6 +263,9 @@ async function CenterGetSecondOrderAnalysisReport(time) {
// Chart.js 实例
const chartInstances = {};
// 添加组件挂载状态跟踪
const isComponentMounted = ref(true);
// DOM 元素引用
const personalFunnelChartCanvas = ref(null);
const contactTimeChartCanvas = ref(null);
@@ -288,7 +291,7 @@ const kpiDescriptions = {
},
avgDuration: {
title: '平均通话时长',
description: '所有通话总时长 ÷ 拨打电话次数。'
description: '所有通话总时长 ÷ 拨打电话次数。'
},
conversionRate: {
title: '成交转化率',
@@ -327,7 +330,8 @@ const createOrUpdateChart = (chartId, canvasRef, config) => {
if (chartInstances[chartId]) {
chartInstances[chartId].destroy();
}
if (canvasRef.value) {
// 确保组件仍然挂载且canvas引用存在
if (isComponentMounted.value && canvasRef.value) {
const ctx = canvasRef.value.getContext('2d');
chartInstances[chartId] = new Chart(ctx, config);
}
@@ -335,6 +339,9 @@ const createOrUpdateChart = (chartId, canvasRef, config) => {
// Chart.js: 渲染销售漏斗图
const renderPersonalFunnelChart = () => {
// 确保组件仍然挂载
if (!isComponentMounted.value) return;
const config = {
type: 'bar',
data: {
@@ -359,6 +366,9 @@ const renderPersonalFunnelChart = () => {
// Chart.js: 渲染黄金联络时段图
const renderContactTimeChart = () => {
// 确保组件仍然挂载
if (!isComponentMounted.value) return;
if (!props.contactTimeData || !props.contactTimeData.gold_contact_success_rate) {
return;
}
@@ -450,11 +460,13 @@ watch(() => props.contactTimeData, () => {
// --- 生命周期钩子 ---
onMounted(() => {
isComponentMounted.value = true;
renderPersonalFunnelChart();
renderContactTimeChart();
});
onBeforeUnmount(() => {
isComponentMounted.value = false;
Object.values(chartInstances).forEach(chart => chart.destroy());
});
</script>

View File

@@ -920,19 +920,6 @@ async function CenterGetSalesFunnel() {
const res = await getSalesFunnel(hasParams ? params : undefined)
if(res.code === 200){
SalesFunnel.value = res.data
/**
* "data": {
"user_name": "常琳",
"user_level": 1,
"sale_funnel": {
"线索总数": 11,
"有效沟通": 9,
"到课数据": 8,
"预付定金": 0,
"成功签单": 0
}
}
*/
}
}
// 黄金联络时间段

View File

@@ -27,18 +27,25 @@ const contextPanelRef = ref(null);
const sentimentChartCanvas = ref(null);
const chartInstances = {};
// 添加组件挂载状态跟踪
const isComponentMounted = ref(true);
// CHARTING
const createOrUpdateChart = (chartId, canvasRef, config) => {
if (chartInstances[chartId]) {
chartInstances[chartId].destroy();
}
if (canvasRef.value) {
// 确保组件仍然挂载且canvas引用存在
if (isComponentMounted.value && canvasRef.value) {
const ctx = canvasRef.value.getContext('2d');
chartInstances[chartId] = new Chart(ctx, config);
}
};
const renderSentimentChart = (history) => {
// 确保组件仍然挂载
if (!isComponentMounted.value) return;
if (!sentimentChartCanvas.value) return;
const ctx = sentimentChartCanvas.value.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, 0, 120);
@@ -126,6 +133,21 @@ watch(() => props.selectedContact, (newContact) => {
});
}
}, { immediate: true });
// LIFECYCLE HOOKS
onMounted(() => {
isComponentMounted.value = true;
});
onBeforeUnmount(() => {
isComponentMounted.value = false;
// 清理所有图表实例
Object.values(chartInstances).forEach(chart => {
if (chart) {
chart.destroy();
}
});
});
</script>
<style lang="scss" scoped>

View File

@@ -28,7 +28,7 @@ const props = defineProps({
})
// 组件状态跟踪
let isComponentMounted = true
const isComponentMounted = ref(true)
// Chart.js 实例
const chartInstances = {}
@@ -44,13 +44,13 @@ const funnelData = reactive({
// 监听teamSalesFunnel变化并更新图表数据
watch(() => props.teamSalesFunnel, (newVal) => {
if (newVal && Object.keys(newVal).length > 0 && isComponentMounted) {
if (newVal && Object.keys(newVal).length > 0 && isComponentMounted.value) {
// 按照固定顺序提取数据
const order = ['线索', '加微', '到课', '定金', '成交']
funnelData.data = order.map(key => newVal[key] || 0)
// 确保在DOM更新后再更新图表
nextTick(() => {
if (isComponentMounted) {
if (isComponentMounted.value) {
renderPersonalFunnelChart()
}
})
@@ -94,19 +94,19 @@ const renderPersonalFunnelChart = () => {
// 生命周期钩子
onMounted(() => {
isComponentMounted = true
isComponentMounted.value = true
// 处理初始传入的teamSalesFunnel数据
if (props.teamSalesFunnel && Object.keys(props.teamSalesFunnel).length > 0) {
const order = ['线索', '加微', '到课', '定金', '成交']
funnelData.data = order.map(key => props.teamSalesFunnel[key] || 0)
}
if (isComponentMounted) {
if (isComponentMounted.value) {
renderPersonalFunnelChart()
}
})
onBeforeUnmount(() => {
isComponentMounted = false
isComponentMounted.value = false
Object.values(chartInstances).forEach(chart => chart.destroy())
})
</script>