Skip to content

Commit 113d4bc

Browse files
authored
legend component (#354)
1 parent b328478 commit 113d4bc

File tree

3 files changed

+125
-96
lines changed

3 files changed

+125
-96
lines changed

swanlab/server/controller/experiment.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,16 @@ def get_tag_data(experiment_id: int, tag: str) -> dict:
270270
# 获取最大值和最小值
271271
max_value = max(data_values)
272272
min_value = min(data_values)
273-
return SUCCESS_200(data={"sum": len(tag_data), "max": max_value, "min": min_value, "list": tag_data})
273+
return SUCCESS_200(
274+
data={
275+
"sum": len(tag_data),
276+
"max": max_value,
277+
"min": min_value,
278+
"list": tag_data,
279+
# 标注此数据隶属于哪个实验
280+
"experiment_id": experiment_id,
281+
}
282+
)
274283

275284

276285
# 获取实验状态
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<template>
2+
<div class="lc-legend">
3+
<div
4+
class="lc-legend-item"
5+
:style="{ color: item.color }"
6+
v-for="item in items"
7+
:key="item.name"
8+
@mouseenter="handelMouseenter(item)"
9+
@mouseleave="handelMouseleave(item)"
10+
@click="handelClick(item)"
11+
>
12+
<RouterLink :to="'/experiment/' + item.experiment_id">
13+
{{ item.name }}
14+
</RouterLink>
15+
</div>
16+
</div>
17+
</template>
18+
19+
<script setup>
20+
/**
21+
* @description: 折线图图例组件
22+
* @file: LineChartLegend.vue
23+
* @since: 2024-02-27 15:02:13
24+
**/
25+
26+
defineProps({
27+
items: {
28+
type: Array,
29+
default: () => []
30+
}
31+
})
32+
// console.log('items', props.items)
33+
34+
const emit = defineEmits(['hoverin', 'hoverout'])
35+
36+
// ---------------------------------- 悬浮事件 ----------------------------------
37+
const handelMouseenter = (item) => {
38+
emit('hoverin', item)
39+
}
40+
const handelMouseleave = (item) => {
41+
emit('hoverout', item)
42+
}
43+
44+
// ---------------------------------- 点击事件 ----------------------------------
45+
const handelClick = (item) => {
46+
emit('click', item)
47+
}
48+
</script>
49+
50+
<style lang="scss" scoped>
51+
.lc-legend {
52+
max-height: 28px;
53+
min-height: 16px;
54+
@apply overflow-y-auto flex flex-wrap px-3 mb-1 gap-y-0.5 gap-x-2 leading-none text-[12px];
55+
@apply justify-center;
56+
.lc-legend-item {
57+
@apply flex flex-shrink-0 items-center hover:brightness-75;
58+
&:before {
59+
content: '';
60+
display: inline-block;
61+
width: 8px;
62+
height: 2px;
63+
margin-right: 4px;
64+
background-color: currentColor;
65+
}
66+
}
67+
}
68+
</style>

vue/src/charts/package/LineChart.vue

Lines changed: 47 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,24 @@
1111
<!-- x轴坐标单位 -->
1212
<p class="absolute right-5 bottom-10 text-xs text-dimmer scale-90 select-none">{{ xTitle }}</p>
1313
<!-- 图表主体 -->
14+
<LineChartLegend
15+
:items="legend"
16+
@hoverin="(item) => legendHoverin(item, false)"
17+
@hoverout="(item) => legendHoverout(item, false)"
18+
v-if="legend && mutli"
19+
/>
1420
<div class="relative" ref="g2Ref">
1521
<LineChartTooltip ref="tooltipRef" />
1622
</div>
1723
<!-- 放大效果 -->
1824
<SLModal class="p-10 pt-0 overflow-hidden" max-w="-1" v-model="isZoom">
1925
<p class="text-center mt-4 mb-10 text-2xl font-semibold">{{ title }}</p>
26+
<LineChartLegend
27+
:items="legend"
28+
@hoverin="(item) => legendHoverin(item, true)"
29+
@hoverout="(item) => legendHoverout(item, true)"
30+
v-if="legend && mutli"
31+
/>
2032
<div class="relative" ref="g2ZoomRef">
2133
<LineChartTooltip detail ref="tooltipZoomRef" />
2234
</div>
@@ -43,6 +55,7 @@ import { getTimes } from '@swanlab-vue/utils/time'
4355
import { isApple } from '@swanlab-vue/utils/browser'
4456
import { message } from '@swanlab-vue/components/message'
4557
import LineChartTooltip from '../components/LinChartTooltip.vue'
58+
import LineChartLegend from '../components/LineChartLegend.vue'
4659
4760
// ---------------------------------- 配置 ----------------------------------
4861
const props = defineProps({
@@ -80,7 +93,7 @@ const gridColor = rootStyle.getPropertyValue('--outline-dimmest')
8093
// 十字准线颜色,通过js获取css变量值
8194
const crosshairsColor = rootStyle.getPropertyValue('--primary-dimmest')
8295
// 线段默认宽度
83-
const lineWidth = 2
96+
const lineWidth = 1.5
8497
// 线段加粗宽度
8598
const thickerLineWidth = 3.5
8699
@@ -134,28 +147,8 @@ const createChart = (dom, data, config = {}, zoom = false) => {
134147
// 多数据的时候,需要设置seriesField,单数据也可以设置,但是不希望出现label
135148
// seriesField,
136149
colorField,
137-
legend: {
138-
// flipPage: false,
139-
pageNavigator: {
140-
marker: {
141-
style: {
142-
// 非激活,不可点击态时的填充色设置
143-
inactiveFill: '#000',
144-
inactiveOpacity: 0.45,
145-
// 默认填充色设置
146-
fill: '#000',
147-
opacity: 0.8,
148-
size: 8
149-
}
150-
},
151-
text: {
152-
style: {
153-
fill: '#ccc',
154-
fontSize: 8
155-
}
156-
}
157-
}
158-
},
150+
// 自己写图例
151+
legend: false,
159152
// 多数据的时候颜色通过回调拿到,colors应该自带getSeries方法
160153
color: ({ series }) => {
161154
return colors.getSeriesColor(series, source.indexOf(series))
@@ -249,7 +242,7 @@ const createChart = (dom, data, config = {}, zoom = false) => {
249242
}
250243
}
251244
},
252-
// 图例相关
245+
// 悬浮提示相关
253246
tooltip: {
254247
// 在此处完成悬浮数据提示的格式化
255248
// 如果需要自定义浮窗,可以用下面的customContent
@@ -286,7 +279,9 @@ const createChart = (dom, data, config = {}, zoom = false) => {
286279
width: undefined,
287280
autoFit: true,
288281
// 开启一些交互
289-
interactions: [{ type: 'hover-cursor' }],
282+
// interactions: [{ type: 'element-active' }],
283+
// 图例相关
284+
290285
// 平滑曲线
291286
smooth: false,
292287
animation: false,
@@ -334,6 +329,7 @@ const createChart = (dom, data, config = {}, zoom = false) => {
334329
*/
335330
const format = (data) => {
336331
// 如果source的长度小于1,抛出错误
332+
console.log(data)
337333
if (source.length < 1) throw new Error('source length must be greater than 1')
338334
// 新的数据,遍历得到
339335
const d = []
@@ -352,7 +348,15 @@ const format = (data) => {
352348
d.sort((a, b) => a[xField] - b[xField])
353349
// console.log('d', d)
354350
// console.log('data', data)
355-
// 如果source的长度大于1,需要设置seriesField
351+
// 依据source生成legend,排序依据是source,数据结构:[{name, color}]
352+
const items = []
353+
for (const s of source) {
354+
if (props.chart.error && props.chart.error[s]) continue
355+
if (!data[s]) continue
356+
items.push({ name: s, color: colors.getSeriesColor(s, source.indexOf(s)), experiment_id: data[s].experiment_id })
357+
}
358+
legend.value = items
359+
// console.log('legend', legend.value)
356360
return { d, config: mutli ? { seriesField } : { color: colors[0] } }
357361
}
358362
@@ -627,6 +631,22 @@ const restoreByTagLinkage = (zoom, tag, color) => {
627631
restoreByTag(plot, zoom, tag, color)
628632
}
629633
634+
// ---------------------------------- 图例渲染,在这保存数据,传给legend组件 ----------------------------------
635+
636+
const legend = ref(null)
637+
638+
const legendHoverin = (item, zoom) => {
639+
// console.log('mouseenter', item)
640+
const plot = zoom ? zoomChartObj : chartObj
641+
thickenByTag(plot, zoom, item.name, item.color)
642+
}
643+
644+
const legendHoverout = (item, zoom) => {
645+
// console.log('mouseleave', item)
646+
const plot = zoom ? zoomChartObj : chartObj
647+
restoreByTag(plot, zoom, item.name, item.color)
648+
}
649+
630650
// ---------------------------------- 暴露api ----------------------------------
631651
defineExpose({
632652
render,
@@ -639,72 +659,4 @@ defineExpose({
639659
})
640660
</script>
641661
642-
<style lang="scss">
643-
.lc-tooltip {
644-
@apply py-2 px-3 absolute bg-default border rounded;
645-
box-shadow: rgba(21, 24, 31, 0.16) 0px 12px 24px 0px;
646-
visibility: visible;
647-
p {
648-
@apply text-xs text-default font-semibold;
649-
}
650-
.lc-tooltip-item-no-zoom,
651-
.lc-tooltip-item-zoom {
652-
@apply flex items-center gap-3;
653-
&:not(:last-child) {
654-
@apply mb-1.5;
655-
}
656-
.lc-tooltip-color {
657-
@apply w-5 flex items-center;
658-
}
659-
.lc-tooltip-color-rect {
660-
&::before {
661-
content: '';
662-
display: inline-block;
663-
width: 20px;
664-
height: 6px;
665-
border-radius: 2px;
666-
margin-right: 5px;
667-
background-color: currentColor;
668-
}
669-
}
670-
}
671-
.lc-tooltip-tip {
672-
@apply font-normal text-dimmest text-xs;
673-
}
674-
}
675-
676-
.lc-tooltip-item-no-zoom {
677-
.lc-tooltip-step {
678-
@apply font-semibold;
679-
&::after {
680-
content: ':';
681-
@apply font-semibold;
682-
}
683-
}
684-
.lc-tooltip-value {
685-
@apply w-10 text-left font-semibold;
686-
}
687-
.lc-tooltip-tag {
688-
@apply truncate;
689-
max-width: 128px;
690-
}
691-
}
692-
693-
.lc-tooltip-item-zoom {
694-
.lc-tooltip-step {
695-
@apply w-7;
696-
}
697-
.lc-tooltip-value {
698-
@apply col-span-1;
699-
@apply w-10 text-left;
700-
}
701-
.lc-tooltip-time {
702-
@apply w-28;
703-
}
704-
705-
.lc-tooltip-tag {
706-
@apply truncate;
707-
max-width: 160px;
708-
}
709-
}
710-
</style>
662+
<style lang="scss"></style>

0 commit comments

Comments
 (0)