style(统计): 优化订单统计来源与类型模块样式

- 左右卡片等宽等高对齐,修复全局 ivu-mt 导致的顶部错位
- 占比进度条与百分比列自适应对齐,0% 显示灰色轨道
- 饼图复用 ECharts 实例、稳定容器 ID,过滤 0 值扇区并调整图例/标签布局
This commit is contained in:
吴昊天 2026-10-22 13:19:00 +08:00
parent 96a54240cd
commit efe8992022
2 changed files with 77 additions and 28 deletions

View File

@ -5,7 +5,7 @@
</template>
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from 'vue';
// 统一从按需注册入口引入(echarts/core)
import * as echarts from '@/libs/echarts';
@ -32,9 +32,9 @@ let myChart = null;
const infoLists = ref(props.infoList);
const seriesArray = ref(props.series);
const echartsId = computed(() => {
return 'echarts' + Math.ceil(Math.random() * 100);
});
// 组件实例 uid 保证同一页面多个图表容器 id 唯一,避免随机数碰撞导致图表渲染到错误节点
const instance = getCurrentInstance();
const echartsId = computed(() => `echarts-${instance ? instance.uid : Date.now()}`);
watch(
() => props.infoList,
@ -54,11 +54,21 @@ watch(
);
function wsFunc() {
myChart.resize();
if (myChart) {
myChart.resize();
}
}
function handleSetVisitChart() {
myChart = echarts.init(document.getElementById(echartsId.value));
const dom = document.getElementById(echartsId.value);
// 数据 watcher 可能在挂载前触发,容器未就绪时跳过,挂载后统一初始化
if (!dom) {
return;
}
// 复用实例,不能每次数据变化都重复 init(会产生 “chart instance already initialized” 并导致图表空白)
if (!myChart) {
myChart = echarts.init(dom);
}
let option = null;
if (props.echartsTitle === 'circle') {
option = {
@ -68,16 +78,40 @@ function handleSetVisitChart() {
},
legend: {
orient: 'vertical',
left: 'right',
right: 12,
top: 'middle',
itemWidth: 10,
itemHeight: 10,
itemGap: 12,
textStyle: {
color: '#606266',
fontSize: 12,
},
data: infoLists.value.bing_xdata || [],
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '70%',
center: ['50%', '60%'],
data: infoLists.value.bing_data || [],
radius: '58%',
center: ['42%', '50%'],
avoidLabelOverlap: true,
minAngle: 3,
label: {
show: true,
// 0 值扇区不显示标签,避免全部堆在圆心附近
formatter: (params) => (Number(params.value) > 0 ? `${params.name}\n${params.percent}%` : ''),
color: '#606266',
fontSize: 12,
lineHeight: 18,
},
labelLine: {
length: 12,
length2: 8,
lineStyle: { color: '#c0c4cc' },
},
// 0 值分类不绘制扇区,避免零角度扇区的标签/引导线堆积在圆心
data: (infoLists.value.bing_data || []).filter((item) => Number(item.value) > 0),
emphasis: {
itemStyle: {
shadowBlur: 10,

View File

@ -338,47 +338,62 @@ function getTrend() {
}
.code-row-bg {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
align-items: stretch;
gap: 16px;
}
.code-row-bg .ivu-mt {
width: calc(50% - 8px);
flex: 1;
min-width: 0;
width: auto;
/* .ivu-mt 的全局 margin-top 只作用于第二张卡,导致左右卡片顶部错位 */
margin-top: 0 !important;
}
.ech-box {
margin-top: 10px;
}
.change-style {
border: 1px solid #ccc;
border: 1px solid var(--prev-border-color-base, #dcdfe6);
border-radius: 15px;
padding: 0px 10px;
padding: 0 10px;
cursor: pointer;
color: var(--prev-color-text-regular, #606266);
}
.percent-box {
display: flex;
align-items: center;
padding-right: 10px;
gap: 8px;
}
.line {
width: 100%;
position: relative;
}
.bg {
position: absolute;
width: 100%;
flex: 1;
min-width: 40px;
height: 8px;
border-radius: 8px;
background-color: #f2f2f2;
overflow: hidden;
}
.bg {
display: none;
}
.percent {
position: absolute;
border-radius: 5px;
height: 8px;
height: 100%;
border-radius: 8px;
background-color: var(--prev-color-primary);
z-index: 999;
}
.num {
flex: 0 0 52px;
width: 52px;
margin: 0;
text-align: right;
white-space: nowrap;
margin: 0 10px;
width: 20px;
color: var(--prev-color-text-regular, #606266);
font-size: 12px;
}
@media (max-width: 1200px) {
.code-row-bg {
flex-wrap: wrap;
}
.code-row-bg .ivu-mt {
flex-basis: 100%;
}
}
</style>