fix(图表): 修复组件卸载时图表内存泄漏问题
添加组件挂载状态跟踪,确保在组件卸载时正确清理图表实例 移除无用注释,修正描述文字
This commit is contained in:
@@ -263,6 +263,9 @@ async function CenterGetSecondOrderAnalysisReport(time) {
|
|||||||
// Chart.js 实例
|
// Chart.js 实例
|
||||||
const chartInstances = {};
|
const chartInstances = {};
|
||||||
|
|
||||||
|
// 添加组件挂载状态跟踪
|
||||||
|
const isComponentMounted = ref(true);
|
||||||
|
|
||||||
// DOM 元素引用
|
// DOM 元素引用
|
||||||
const personalFunnelChartCanvas = ref(null);
|
const personalFunnelChartCanvas = ref(null);
|
||||||
const contactTimeChartCanvas = ref(null);
|
const contactTimeChartCanvas = ref(null);
|
||||||
@@ -288,7 +291,7 @@ const kpiDescriptions = {
|
|||||||
},
|
},
|
||||||
avgDuration: {
|
avgDuration: {
|
||||||
title: '平均通话时长',
|
title: '平均通话时长',
|
||||||
description: '所有通话总时长 ÷ 拨打电话次数。'
|
description: '所有通话总时长 ÷ 拨打的电话次数。'
|
||||||
},
|
},
|
||||||
conversionRate: {
|
conversionRate: {
|
||||||
title: '成交转化率',
|
title: '成交转化率',
|
||||||
@@ -327,7 +330,8 @@ const createOrUpdateChart = (chartId, canvasRef, config) => {
|
|||||||
if (chartInstances[chartId]) {
|
if (chartInstances[chartId]) {
|
||||||
chartInstances[chartId].destroy();
|
chartInstances[chartId].destroy();
|
||||||
}
|
}
|
||||||
if (canvasRef.value) {
|
// 确保组件仍然挂载且canvas引用存在
|
||||||
|
if (isComponentMounted.value && canvasRef.value) {
|
||||||
const ctx = canvasRef.value.getContext('2d');
|
const ctx = canvasRef.value.getContext('2d');
|
||||||
chartInstances[chartId] = new Chart(ctx, config);
|
chartInstances[chartId] = new Chart(ctx, config);
|
||||||
}
|
}
|
||||||
@@ -335,6 +339,9 @@ const createOrUpdateChart = (chartId, canvasRef, config) => {
|
|||||||
|
|
||||||
// Chart.js: 渲染销售漏斗图
|
// Chart.js: 渲染销售漏斗图
|
||||||
const renderPersonalFunnelChart = () => {
|
const renderPersonalFunnelChart = () => {
|
||||||
|
// 确保组件仍然挂载
|
||||||
|
if (!isComponentMounted.value) return;
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
@@ -359,6 +366,9 @@ const renderPersonalFunnelChart = () => {
|
|||||||
|
|
||||||
// Chart.js: 渲染黄金联络时段图
|
// Chart.js: 渲染黄金联络时段图
|
||||||
const renderContactTimeChart = () => {
|
const renderContactTimeChart = () => {
|
||||||
|
// 确保组件仍然挂载
|
||||||
|
if (!isComponentMounted.value) return;
|
||||||
|
|
||||||
if (!props.contactTimeData || !props.contactTimeData.gold_contact_success_rate) {
|
if (!props.contactTimeData || !props.contactTimeData.gold_contact_success_rate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -450,11 +460,13 @@ watch(() => props.contactTimeData, () => {
|
|||||||
// --- 生命周期钩子 ---
|
// --- 生命周期钩子 ---
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
isComponentMounted.value = true;
|
||||||
renderPersonalFunnelChart();
|
renderPersonalFunnelChart();
|
||||||
renderContactTimeChart();
|
renderContactTimeChart();
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
isComponentMounted.value = false;
|
||||||
Object.values(chartInstances).forEach(chart => chart.destroy());
|
Object.values(chartInstances).forEach(chart => chart.destroy());
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -920,19 +920,6 @@ async function CenterGetSalesFunnel() {
|
|||||||
const res = await getSalesFunnel(hasParams ? params : undefined)
|
const res = await getSalesFunnel(hasParams ? params : undefined)
|
||||||
if(res.code === 200){
|
if(res.code === 200){
|
||||||
SalesFunnel.value = res.data
|
SalesFunnel.value = res.data
|
||||||
/**
|
|
||||||
* "data": {
|
|
||||||
"user_name": "常琳",
|
|
||||||
"user_level": 1,
|
|
||||||
"sale_funnel": {
|
|
||||||
"线索总数": 11,
|
|
||||||
"有效沟通": 9,
|
|
||||||
"到课数据": 8,
|
|
||||||
"预付定金": 0,
|
|
||||||
"成功签单": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 黄金联络时间段
|
// 黄金联络时间段
|
||||||
|
|||||||
@@ -27,18 +27,25 @@ const contextPanelRef = ref(null);
|
|||||||
const sentimentChartCanvas = ref(null);
|
const sentimentChartCanvas = ref(null);
|
||||||
const chartInstances = {};
|
const chartInstances = {};
|
||||||
|
|
||||||
|
// 添加组件挂载状态跟踪
|
||||||
|
const isComponentMounted = ref(true);
|
||||||
|
|
||||||
// CHARTING
|
// CHARTING
|
||||||
const createOrUpdateChart = (chartId, canvasRef, config) => {
|
const createOrUpdateChart = (chartId, canvasRef, config) => {
|
||||||
if (chartInstances[chartId]) {
|
if (chartInstances[chartId]) {
|
||||||
chartInstances[chartId].destroy();
|
chartInstances[chartId].destroy();
|
||||||
}
|
}
|
||||||
if (canvasRef.value) {
|
// 确保组件仍然挂载且canvas引用存在
|
||||||
|
if (isComponentMounted.value && canvasRef.value) {
|
||||||
const ctx = canvasRef.value.getContext('2d');
|
const ctx = canvasRef.value.getContext('2d');
|
||||||
chartInstances[chartId] = new Chart(ctx, config);
|
chartInstances[chartId] = new Chart(ctx, config);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSentimentChart = (history) => {
|
const renderSentimentChart = (history) => {
|
||||||
|
// 确保组件仍然挂载
|
||||||
|
if (!isComponentMounted.value) return;
|
||||||
|
|
||||||
if (!sentimentChartCanvas.value) return;
|
if (!sentimentChartCanvas.value) return;
|
||||||
const ctx = sentimentChartCanvas.value.getContext('2d');
|
const ctx = sentimentChartCanvas.value.getContext('2d');
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, 120);
|
const gradient = ctx.createLinearGradient(0, 0, 0, 120);
|
||||||
@@ -126,6 +133,21 @@ watch(() => props.selectedContact, (newContact) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// LIFECYCLE HOOKS
|
||||||
|
onMounted(() => {
|
||||||
|
isComponentMounted.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
isComponentMounted.value = false;
|
||||||
|
// 清理所有图表实例
|
||||||
|
Object.values(chartInstances).forEach(chart => {
|
||||||
|
if (chart) {
|
||||||
|
chart.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 组件状态跟踪
|
// 组件状态跟踪
|
||||||
let isComponentMounted = true
|
const isComponentMounted = ref(true)
|
||||||
|
|
||||||
// Chart.js 实例
|
// Chart.js 实例
|
||||||
const chartInstances = {}
|
const chartInstances = {}
|
||||||
@@ -44,13 +44,13 @@ const funnelData = reactive({
|
|||||||
|
|
||||||
// 监听teamSalesFunnel变化并更新图表数据
|
// 监听teamSalesFunnel变化并更新图表数据
|
||||||
watch(() => props.teamSalesFunnel, (newVal) => {
|
watch(() => props.teamSalesFunnel, (newVal) => {
|
||||||
if (newVal && Object.keys(newVal).length > 0 && isComponentMounted) {
|
if (newVal && Object.keys(newVal).length > 0 && isComponentMounted.value) {
|
||||||
// 按照固定顺序提取数据
|
// 按照固定顺序提取数据
|
||||||
const order = ['线索', '加微', '到课', '定金', '成交']
|
const order = ['线索', '加微', '到课', '定金', '成交']
|
||||||
funnelData.data = order.map(key => newVal[key] || 0)
|
funnelData.data = order.map(key => newVal[key] || 0)
|
||||||
// 确保在DOM更新后再更新图表
|
// 确保在DOM更新后再更新图表
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (isComponentMounted) {
|
if (isComponentMounted.value) {
|
||||||
renderPersonalFunnelChart()
|
renderPersonalFunnelChart()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -94,19 +94,19 @@ const renderPersonalFunnelChart = () => {
|
|||||||
|
|
||||||
// 生命周期钩子
|
// 生命周期钩子
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isComponentMounted = true
|
isComponentMounted.value = true
|
||||||
// 处理初始传入的teamSalesFunnel数据
|
// 处理初始传入的teamSalesFunnel数据
|
||||||
if (props.teamSalesFunnel && Object.keys(props.teamSalesFunnel).length > 0) {
|
if (props.teamSalesFunnel && Object.keys(props.teamSalesFunnel).length > 0) {
|
||||||
const order = ['线索', '加微', '到课', '定金', '成交']
|
const order = ['线索', '加微', '到课', '定金', '成交']
|
||||||
funnelData.data = order.map(key => props.teamSalesFunnel[key] || 0)
|
funnelData.data = order.map(key => props.teamSalesFunnel[key] || 0)
|
||||||
}
|
}
|
||||||
if (isComponentMounted) {
|
if (isComponentMounted.value) {
|
||||||
renderPersonalFunnelChart()
|
renderPersonalFunnelChart()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
isComponentMounted = false
|
isComponentMounted.value = false
|
||||||
Object.values(chartInstances).forEach(chart => chart.destroy())
|
Object.values(chartInstances).forEach(chart => chart.destroy())
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user