Skip to content

Commit e1f4687

Browse files
committed
Refactor charts of background page
1 parent d9fb9b7 commit e1f4687

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1895
-1247
lines changed

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"countup",
1717
"daterange",
1818
"echarts",
19+
"emsp",
20+
"ensp",
1921
"filemanager",
2022
"Hengyang",
2123
"Kanban",
@@ -38,4 +40,4 @@
3840
// Ignore i18n resources
3941
"src/i18n/message/**/*.json",
4042
],
41-
}
43+
}

src/app/components/Analysis/components/Trend/Dimension/Wrapper.ts

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ import { use } from "echarts/core"
1818
import { LineChart } from "echarts/charts"
1919
import { SVGRenderer } from "echarts/renderers"
2020
import { TitleComponent, TooltipComponent, GridComponent } from "echarts/components"
21-
2221
import { ValueFormatter } from "@app/components/Analysis/util"
2322
import { getSecondaryTextColor } from "@util/style"
2423
import { EchartsWrapper } from "@hooks"
25-
import { ZRColor } from "echarts/types/dist/shared"
24+
import { getLineSeriesPalette, tooltipDot, tooltipFlexLine } from "@app/util/echarts"
25+
import { TopLevelFormatterParams } from "echarts/types/dist/shared"
2626

2727
use([
2828
LineChart,
@@ -50,67 +50,25 @@ type ValueItem = LineSeriesOption["data"][0] & {
5050
_data: DimensionEntry
5151
}
5252

53-
const THIS_COLOR: ZRColor = {
54-
type: "linear",
55-
x: 0, y: 0,
56-
x2: 0, y2: 1,
57-
colorStops: [
58-
{ offset: 0, color: 'rgb(55, 162, 255)' },
59-
{ offset: 1, color: 'rgb(116, 21, 219)' },
60-
],
61-
}
62-
const PREV_COLOR: ZRColor = {
63-
type: "linear",
64-
x: 0, y: 0,
65-
x2: 0, y2: 1,
66-
colorStops: [
67-
{ offset: 0, color: 'rgb(255, 0, 135)' },
68-
{ offset: 1, color: 'rgb(135, 0, 157)' },
69-
],
70-
}
53+
const [THIS_COLOR, PREV_COLOR] = getLineSeriesPalette()
7154

72-
const createTooltipLine = (param: any, valueFormatter: ValueFormatter) => {
55+
const createTooltipLine = (param: any, valueFormatter: ValueFormatter): string => {
7356
const data = param.data as ValueItem
7457
const { _data: { value, date } } = data
75-
const color = param.color as string
76-
const p = document.createElement('p')
77-
p.style.margin = "0"
78-
p.style.padding = "0"
79-
p.style.alignItems = "center"
80-
p.style.display = "flex"
81-
82-
const dotEl = document.createElement('div')
83-
dotEl.style.width = '8px'
84-
dotEl.style.height = '8px'
85-
dotEl.style.display = 'inline-flex'
86-
dotEl.style.borderRadius = '4px'
87-
dotEl.style.backgroundColor = color
88-
dotEl.style.marginRight = '7px'
89-
p.append(dotEl)
90-
91-
const dateEl = document.createElement('span')
92-
dateEl.innerText = date
93-
dateEl.style.marginRight = "7px"
94-
p.appendChild(dateEl)
9558

9659
const valStr = valueFormatter?.(value) || value?.toString() || "NaN"
97-
const valEL = document.createElement('span')
98-
valEL.innerText = valStr
99-
valEL.style.fontWeight = "500"
100-
p.appendChild(valEL)
101-
return p
60+
61+
return tooltipFlexLine(
62+
`${tooltipDot(param.color)} ${date}`,
63+
`<b>${valStr}</b>`
64+
)
10265
}
10366

104-
const formatTooltip = (params: any[], valueFormatter: ValueFormatter) => {
105-
const container = document.createElement('div')
106-
container.style.height = "50px"
107-
container.style.display = "flex"
108-
container.style.flexDirection = "column"
109-
container.style.justifyContent = "space-around"
67+
const formatTooltip = (params: TopLevelFormatterParams, valueFormatter: ValueFormatter) => {
68+
if (!Array.isArray(params)) return ''
11069

11170
const lines = params.map(param => createTooltipLine(param, valueFormatter))
112-
lines.forEach(l => container.append(l))
113-
return container
71+
return lines.join('')
11472
}
11573

11674
const generateOption = ({ entries, preEntries, title, valueFormatter }: BizOption) => {
@@ -141,7 +99,7 @@ const generateOption = ({ entries, preEntries, title, valueFormatter }: BizOptio
14199
tooltip: {
142100
trigger: 'axis',
143101
axisPointer: { type: "line" },
144-
formatter: (params: any[]) => formatTooltip(params, valueFormatter),
102+
formatter: (params: TopLevelFormatterParams) => formatTooltip(params, valueFormatter),
145103
},
146104
xAxis: {
147105
type: 'category',

src/app/components/Analysis/components/Trend/Filter.tsx

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,25 @@
66
*/
77

88
import type { ElementDatePickerShortcut } from "@src/element-ui/date"
9-
import type { CalendarMessage } from "@i18n/message/common/calendar"
109

1110
import { t } from "@app/locale"
1211
import { ElDatePicker } from "element-plus"
1312
import { defineComponent, ref, type PropType } from "vue"
1413
import { daysAgo } from "@util/time"
14+
import { EL_DATE_FORMAT } from "@i18n/element"
1515

16-
function datePickerShortcut(msgKey: keyof CalendarMessage['range'], agoOfStart?: number, agoOfEnd?: number): ElementDatePickerShortcut {
16+
function datePickerShortcut(agoOfStart?: number, agoOfEnd?: number): ElementDatePickerShortcut {
1717
return {
18-
text: t(msg => msg.calendar.range[msgKey]),
19-
value: daysAgo(agoOfStart - 1 || 0, agoOfEnd || 0)
18+
text: t(msg => msg.calendar.range.lastDays, { n: agoOfStart }),
19+
value: daysAgo(agoOfStart - 1 || 0, agoOfEnd || 0),
2020
}
2121
}
2222

23-
const DATE_FORMAT = t(msg => msg.calendar.dateFormat, {
24-
y: 'YYYY',
25-
m: 'MM',
26-
d: 'DD'
27-
})
28-
2923
const SHORTCUTS = [
30-
datePickerShortcut('last7Days', 7),
31-
datePickerShortcut('last15Days', 15),
32-
datePickerShortcut('last30Days', 30),
33-
datePickerShortcut("last90Days", 90)
24+
datePickerShortcut(7),
25+
datePickerShortcut(15),
26+
datePickerShortcut(30),
27+
datePickerShortcut(90),
3428
]
3529

3630
const _default = defineComponent({
@@ -47,7 +41,7 @@ const _default = defineComponent({
4741
<ElDatePicker
4842
modelValue={dateRange.value}
4943
disabledDate={(date: Date) => date.getTime() > new Date().getTime()}
50-
format={DATE_FORMAT}
44+
format={EL_DATE_FORMAT}
5145
type="daterange"
5246
shortcuts={SHORTCUTS}
5347
rangeSeparator="-"

src/app/components/Dashboard/DashboardCard.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88
import { ElCard, ElCol } from "element-plus"
99
import { defineComponent } from "vue"
1010

11+
const clzName = (noPadding: boolean) => {
12+
const names = ['dashboard-card']
13+
noPadding && names.push('no-padding')
14+
return names.join(' ')
15+
}
16+
1117
const _default = defineComponent({
1218
props: {
19+
noPadding: Boolean,
1320
span: {
1421
type: Number,
1522
required: true
@@ -18,7 +25,7 @@ const _default = defineComponent({
1825
setup(props, ctx) {
1926
return () => (
2027
<ElCol span={props.span}>
21-
<ElCard style={{ height: "100%" }} v-slots={ctx.slots} />
28+
<ElCard class={clzName(props.noPadding)} style={{ height: "100%" }} v-slots={ctx.slots} />
2229
</ElCol>
2330
)
2431
}

src/app/components/Dashboard/components/Calendar/Wrapper.ts

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
*/
77
import type { TitleComponentOption, TooltipComponentOption, GridComponentOption, VisualMapComponentOption } from "echarts/components"
88
import { TitleComponent, TooltipComponent, GridComponent, VisualMapComponent } from "echarts/components"
9-
import { HeatmapChart, type HeatmapSeriesOption } from "echarts/charts"
9+
import { ScatterChart, ScatterSeriesOption, type HeatmapSeriesOption } from "echarts/charts"
1010
import { use, type ComposeOption } from "echarts/core"
1111
import { SVGRenderer } from "echarts/renderers"
1212

1313
// Register echarts
1414
use([
1515
SVGRenderer,
16-
HeatmapChart,
16+
ScatterChart,
1717
TooltipComponent,
1818
GridComponent,
1919
VisualMapComponent,
@@ -30,6 +30,7 @@ import { BASE_TITLE_OPTION } from "../../common"
3030
import { getAppPageUrl } from "@util/constant/url"
3131
import { REPORT_ROUTE } from "@app/router/constants"
3232
import { createTabAfterCurrent } from "@api/chrome/tab"
33+
import { getStepColors } from "@app/util/echarts"
3334

3435
type _Value = [
3536
x: number,
@@ -39,7 +40,7 @@ type _Value = [
3940
]
4041

4142
type EcOption = ComposeOption<
42-
| HeatmapSeriesOption
43+
| ScatterSeriesOption
4344
| TitleComponentOption
4445
| TooltipComponentOption
4546
| GridComponentOption
@@ -58,11 +59,7 @@ function formatTooltip(mills: number, date: string): string {
5859
const d = date.substring(6, 8)
5960
const dateStr = t(msg => msg.calendar.dateFormat, { y, m, d })
6061
const timeStr = formatPeriodCommon(mills)
61-
return `${dateStr}</br>${timeStr}`
62-
}
63-
64-
function getGridColors() {
65-
return ['#9be9a8', '#40c263', '#30a04e', '#216039']
62+
return `${dateStr}</br><b>${timeStr}</b>`
6663
}
6764

6865
function getXAxisLabelMap(data: _Value[]): { [x: string]: string } {
@@ -105,11 +102,48 @@ const cvtHeatmapItem = (d: _Value): HeatmapItem => {
105102
return item
106103
}
107104

108-
function optionOf(data: _Value[], weekDays: string[]): EcOption {
105+
type Piece = {
106+
label: string
107+
min: number
108+
max: number
109+
color?: string
110+
}
111+
112+
const minOf = (min: number) => min * 60 * 1000
113+
const hourOf = (hour: number) => hour * 60 * 60 * 1000
114+
115+
const ALL_PIECES: Piece[] = [
116+
{ min: 1, max: minOf(10), label: "<10m" },
117+
{ min: minOf(10), max: minOf(30), label: "<30m" },
118+
{ min: minOf(30), max: hourOf(1), label: "<1h" },
119+
{ min: hourOf(1), max: hourOf(2), label: "<2h" },
120+
{ min: hourOf(2), max: hourOf(4), label: "<4h" },
121+
{ min: hourOf(4), max: hourOf(7), label: "<7h" },
122+
{ min: hourOf(7), max: hourOf(12), label: "<12h" },
123+
{ min: hourOf(12), max: hourOf(18), label: "<18h" },
124+
{ min: hourOf(18), max: hourOf(24), label: ">=18h" },
125+
]
126+
127+
const computePieces = (min: number, max: number): Piece[] => {
128+
let pieces = ALL_PIECES.filter((p, i) => i === 0 || p.min <= max)
129+
pieces = pieces.filter((p, i) => p.max > min || i === pieces.length - 1)
130+
131+
const colors = getStepColors(pieces.length)
132+
return pieces.map((p, idx) => ({ ...p, color: colors[idx] }))
133+
}
134+
135+
function optionOf(data: _Value[], weekDays: string[], dom: HTMLElement): EcOption {
109136
const totalMills = sum(data?.map(d => d[2] ?? 0))
110137
const totalHours = Math.floor(totalMills / MILL_PER_HOUR)
111138
const xAxisLabelMap = getXAxisLabelMap(data)
112139
const textColor = getPrimaryTextColor()
140+
const w = dom?.getBoundingClientRect?.()?.width
141+
const gridWidth = 0.85
142+
const colCount = new Set(data.map(v => v[0])).size
143+
const gridCellSize = colCount ? w * gridWidth / colCount * 0.75 : 0
144+
145+
const maxVal = Math.max(...data.map(a => a[2]))
146+
const minVal = Math.min(...data.map(a => a[2]).filter(v => v))
113147
return {
114148
title: {
115149
...BASE_TITLE_OPTION,
@@ -125,7 +159,7 @@ function optionOf(data: _Value[], weekDays: string[]): EcOption {
125159
return mills ? formatTooltip(mills as number, date) : undefined
126160
},
127161
},
128-
grid: { height: '70%', width: '82%', left: '8%', top: '18%', },
162+
grid: { height: '70%', left: '7%', width: `${gridWidth * 100}%`, top: '18%', },
129163
xAxis: {
130164
type: 'category',
131165
axisLine: { show: false },
@@ -145,22 +179,23 @@ function optionOf(data: _Value[], weekDays: string[]): EcOption {
145179
axisTick: { show: false, alignWithLabel: true },
146180
},
147181
visualMap: {
148-
min: 0,
149-
max: Math.max(...data.map(a => a[2])),
150-
inRange: { color: getGridColors() },
182+
type: 'piecewise',
151183
realtime: true,
152184
calculable: true,
153185
orient: 'vertical',
154186
right: '2%',
155187
top: 'center',
156188
dimension: 2,
157-
textStyle: { color: textColor },
189+
splitNumber: 6,
190+
showLabel: true,
191+
pieces: computePieces(minVal, maxVal),
192+
textStyle: { color: getPrimaryTextColor() },
158193
},
159194
series: {
160-
type: 'heatmap',
195+
type: 'scatter',
161196
data: data.map(cvtHeatmapItem),
162-
progressive: 5,
163-
progressiveThreshold: 10,
197+
symbol: 'circle',
198+
symbolSize: gridCellSize,
164199
},
165200
}
166201
}
@@ -203,7 +238,7 @@ class Wrapper extends EchartsWrapper<BizOption, EcOption> {
203238
// Saturday to Sunday
204239
rotate(weekDays, 1)
205240
}
206-
return optionOf(data, weekDays)
241+
return optionOf(data, weekDays, this.getDom())
207242
}
208243

209244
protected afterInit(): void {

0 commit comments

Comments
 (0)