Files
TakeoutSaaS.TenantUI/apps/web-antd/src/views/customer/analysis/components/CompositionCard.vue

85 lines
2.1 KiB
Vue

<script setup lang="ts">
import type {
CustomerAnalysisCompositionItemDto,
CustomerAnalysisSegmentCode,
} from '#/api/customer';
import { computed } from 'vue';
import {
formatInteger,
formatPercent,
resolveCompositionToneColor,
} from '../composables/customer-analysis-page/helpers';
interface Props {
items: CustomerAnalysisCompositionItemDto[];
totalCustomers: number;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(event: 'segment', segmentCode: CustomerAnalysisSegmentCode): void;
}>();
const donutStyle = computed(() => {
if (props.items.length === 0) {
return {
background: 'conic-gradient(#e5e7eb 0deg 360deg)',
};
}
let angle = 0;
const parts: string[] = [];
for (const item of props.items) {
const start = angle;
const delta = Math.max(0, Math.min(360, (item.percent / 100) * 360));
angle += delta;
const end = Math.min(360, angle);
parts.push(
`${resolveCompositionToneColor(item.tone)} ${start}deg ${end}deg`,
);
}
if (angle < 360) {
parts.push(`#e5e7eb ${angle}deg 360deg`);
}
return {
background: `conic-gradient(${parts.join(',')})`,
};
});
</script>
<template>
<div class="ca-card">
<div class="ca-card-title">新老客占比</div>
<div class="ca-donut-wrap">
<div class="ca-donut" :style="donutStyle">
<div class="ca-donut-hole">
<div class="num">{{ formatInteger(props.totalCustomers) }}</div>
<div class="lbl">总客户</div>
</div>
</div>
<div class="ca-legend">
<button
v-for="item in props.items"
:key="item.segmentCode"
class="ca-legend-item"
type="button"
@click="emit('segment', item.segmentCode)"
>
<span
class="ca-legend-dot"
:style="{ backgroundColor: resolveCompositionToneColor(item.tone) }"
></span>
<span class="ca-legend-label">{{ item.label }}</span>
<span class="ca-legend-value">{{ formatPercent(item.percent) }}</span>
</button>
</div>
</div>
</div>
</template>