<template>
|
<HomePanel title="客户合同金额分析" :loading="loading">
|
<div class="contract">
|
<div class="contract__head">
|
<span class="contract__label">总合同金额(元)</span>
|
<div class="contract__metrics">
|
<span class="contract__sum app-num">{{ formatNum(sum) }}</span>
|
<span class="contract__rate">周同比 <b :class="yny >= 0 ? 'is-up' : 'is-down'">{{ yny }}%</b></span>
|
<span class="contract__rate">环比 <b :class="chain >= 0 ? 'is-up' : 'is-down'">{{ chain }}%</b></span>
|
</div>
|
</div>
|
<div class="contract__body">
|
<div class="contract__pie">
|
<Echarts :legend="{ show: false }" :chartStyle="{ width: '100%', height: '100%' }"
|
:series="pieSeries" :tooltip="pieTooltip" />
|
</div>
|
<ul class="contract__list">
|
<li v-for="item in pieList" :key="item.name" class="contract__row">
|
<span class="contract__dot" :style="{ background: item.itemStyle.color }"></span>
|
<span class="contract__name" :title="item.name">{{ item.name }}</span>
|
<span class="contract__rate-value app-num">{{ item.rate }}%</span>
|
<span class="contract__value app-num">¥{{ formatNum(item.value) }}</span>
|
</li>
|
<el-empty v-if="!pieList.length" description="暂无数据" :image-size="48" />
|
</ul>
|
</div>
|
</div>
|
</HomePanel>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from 'vue'
|
import Echarts from '@/components/Echarts/echarts.vue'
|
import HomePanel from '../HomePanel.vue'
|
import { analysisCustomerContractAmounts } from '@/api/viewIndex'
|
import { formatNum } from '../../useCountUp'
|
|
const PIE_COLORS = ['#2C51D9', '#0F9960', '#D97706', '#D54941', '#7A5AF8', '#19C6C6', '#FF8A3D', '#5B8CFF', '#B37FEB', '#1377EF']
|
|
const loading = ref(true)
|
const sum = ref(0)
|
const yny = ref(0)
|
const chain = ref(0)
|
const pieList = ref([])
|
|
const pieSeries = ref([
|
{
|
type: 'pie',
|
radius: ['62%', '86%'],
|
center: ['50%', '50%'],
|
avoidLabelOverlap: false,
|
itemStyle: { borderColor: '#fff', borderWidth: 2, borderRadius: 4 },
|
label: { show: false },
|
data: []
|
}
|
])
|
|
const pieTooltip = {
|
trigger: 'item',
|
formatter: (params) => `${params.name} ${formatNum(params.value)}元 ${params.percent}%`
|
}
|
|
onMounted(() => {
|
analysisCustomerContractAmounts().then((res) => {
|
const d = res.data || {}
|
sum.value = d.sum
|
yny.value = d.yny
|
chain.value = d.chain
|
pieList.value = (d.item || []).map((item, index) => ({
|
...item,
|
itemStyle: { color: PIE_COLORS[index % PIE_COLORS.length] }
|
}))
|
pieSeries.value[0].data = pieList.value
|
}).finally(() => { loading.value = false })
|
})
|
</script>
|
|
<style scoped>
|
.contract {
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
height: 100%;
|
}
|
|
.contract__head {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
flex-wrap: wrap;
|
gap: 8px;
|
padding: 10px 14px;
|
border-radius: var(--app-radius-sm);
|
background: var(--app-surface-hover);
|
}
|
|
.contract__label {
|
font-size: 13px;
|
color: var(--app-text-secondary);
|
}
|
|
.contract__metrics {
|
display: flex;
|
align-items: baseline;
|
gap: 16px;
|
}
|
|
.contract__sum {
|
font-size: 20px;
|
font-weight: 600;
|
color: var(--app-text);
|
}
|
|
.contract__rate {
|
font-size: 12px;
|
color: var(--app-text-tertiary);
|
}
|
|
.contract__rate .is-up { color: var(--app-danger); }
|
.contract__rate .is-down { color: var(--app-success); }
|
|
.contract__body {
|
display: flex;
|
align-items: center;
|
gap: 16px;
|
flex: 1;
|
min-height: 0;
|
}
|
|
.contract__pie {
|
width: 160px;
|
height: 160px;
|
flex-shrink: 0;
|
}
|
|
.contract__list {
|
flex: 1;
|
min-width: 0;
|
list-style: none;
|
margin: 0;
|
padding: 0;
|
max-height: 160px;
|
overflow-y: auto;
|
}
|
|
.contract__row {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
padding: 6px 0;
|
font-size: 13px;
|
border-bottom: 1px dashed var(--app-divider);
|
}
|
|
.contract__row:last-child {
|
border-bottom: none;
|
}
|
|
.contract__dot {
|
width: 8px;
|
height: 8px;
|
border-radius: 50%;
|
flex-shrink: 0;
|
}
|
|
.contract__name {
|
flex: 1;
|
min-width: 0;
|
color: var(--app-text-secondary);
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
.contract__rate-value {
|
width: 56px;
|
text-align: right;
|
color: var(--app-text-tertiary);
|
}
|
|
.contract__value {
|
width: 120px;
|
text-align: right;
|
color: var(--app-text);
|
}
|
</style>
|