Skip to content

Commit

Permalink
Refactor charts of background page
Browse files Browse the repository at this point in the history
Refactor charts of background page
  • Loading branch information
sheepzh committed Aug 8, 2024
2 parents d9fb9b7 + e1f4687 commit 88ac3f6
Show file tree
Hide file tree
Showing 73 changed files with 1,895 additions and 1,247 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"countup",
"daterange",
"echarts",
"emsp",
"ensp",
"filemanager",
"Hengyang",
"Kanban",
Expand All @@ -38,4 +40,4 @@
// Ignore i18n resources
"src/i18n/message/**/*.json",
],
}
}
68 changes: 13 additions & 55 deletions src/app/components/Analysis/components/Trend/Dimension/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import { use } from "echarts/core"
import { LineChart } from "echarts/charts"
import { SVGRenderer } from "echarts/renderers"
import { TitleComponent, TooltipComponent, GridComponent } from "echarts/components"

import { ValueFormatter } from "@app/components/Analysis/util"
import { getSecondaryTextColor } from "@util/style"
import { EchartsWrapper } from "@hooks"
import { ZRColor } from "echarts/types/dist/shared"
import { getLineSeriesPalette, tooltipDot, tooltipFlexLine } from "@app/util/echarts"
import { TopLevelFormatterParams } from "echarts/types/dist/shared"

use([
LineChart,
Expand Down Expand Up @@ -50,67 +50,25 @@ type ValueItem = LineSeriesOption["data"][0] & {
_data: DimensionEntry
}

const THIS_COLOR: ZRColor = {
type: "linear",
x: 0, y: 0,
x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgb(55, 162, 255)' },
{ offset: 1, color: 'rgb(116, 21, 219)' },
],
}
const PREV_COLOR: ZRColor = {
type: "linear",
x: 0, y: 0,
x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgb(255, 0, 135)' },
{ offset: 1, color: 'rgb(135, 0, 157)' },
],
}
const [THIS_COLOR, PREV_COLOR] = getLineSeriesPalette()

const createTooltipLine = (param: any, valueFormatter: ValueFormatter) => {
const createTooltipLine = (param: any, valueFormatter: ValueFormatter): string => {
const data = param.data as ValueItem
const { _data: { value, date } } = data
const color = param.color as string
const p = document.createElement('p')
p.style.margin = "0"
p.style.padding = "0"
p.style.alignItems = "center"
p.style.display = "flex"

const dotEl = document.createElement('div')
dotEl.style.width = '8px'
dotEl.style.height = '8px'
dotEl.style.display = 'inline-flex'
dotEl.style.borderRadius = '4px'
dotEl.style.backgroundColor = color
dotEl.style.marginRight = '7px'
p.append(dotEl)

const dateEl = document.createElement('span')
dateEl.innerText = date
dateEl.style.marginRight = "7px"
p.appendChild(dateEl)

const valStr = valueFormatter?.(value) || value?.toString() || "NaN"
const valEL = document.createElement('span')
valEL.innerText = valStr
valEL.style.fontWeight = "500"
p.appendChild(valEL)
return p

return tooltipFlexLine(
`${tooltipDot(param.color)} ${date}`,
`<b>${valStr}</b>`
)
}

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

const lines = params.map(param => createTooltipLine(param, valueFormatter))
lines.forEach(l => container.append(l))
return container
return lines.join('')
}

const generateOption = ({ entries, preEntries, title, valueFormatter }: BizOption) => {
Expand Down Expand Up @@ -141,7 +99,7 @@ const generateOption = ({ entries, preEntries, title, valueFormatter }: BizOptio
tooltip: {
trigger: 'axis',
axisPointer: { type: "line" },
formatter: (params: any[]) => formatTooltip(params, valueFormatter),
formatter: (params: TopLevelFormatterParams) => formatTooltip(params, valueFormatter),
},
xAxis: {
type: 'category',
Expand Down
24 changes: 9 additions & 15 deletions src/app/components/Analysis/components/Trend/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,25 @@
*/

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

import { t } from "@app/locale"
import { ElDatePicker } from "element-plus"
import { defineComponent, ref, type PropType } from "vue"
import { daysAgo } from "@util/time"
import { EL_DATE_FORMAT } from "@i18n/element"

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

const DATE_FORMAT = t(msg => msg.calendar.dateFormat, {
y: 'YYYY',
m: 'MM',
d: 'DD'
})

const SHORTCUTS = [
datePickerShortcut('last7Days', 7),
datePickerShortcut('last15Days', 15),
datePickerShortcut('last30Days', 30),
datePickerShortcut("last90Days", 90)
datePickerShortcut(7),
datePickerShortcut(15),
datePickerShortcut(30),
datePickerShortcut(90),
]

const _default = defineComponent({
Expand All @@ -47,7 +41,7 @@ const _default = defineComponent({
<ElDatePicker
modelValue={dateRange.value}
disabledDate={(date: Date) => date.getTime() > new Date().getTime()}
format={DATE_FORMAT}
format={EL_DATE_FORMAT}
type="daterange"
shortcuts={SHORTCUTS}
rangeSeparator="-"
Expand Down
9 changes: 8 additions & 1 deletion src/app/components/Dashboard/DashboardCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@
import { ElCard, ElCol } from "element-plus"
import { defineComponent } from "vue"

const clzName = (noPadding: boolean) => {
const names = ['dashboard-card']
noPadding && names.push('no-padding')
return names.join(' ')
}

const _default = defineComponent({
props: {
noPadding: Boolean,
span: {
type: Number,
required: true
Expand All @@ -18,7 +25,7 @@ const _default = defineComponent({
setup(props, ctx) {
return () => (
<ElCol span={props.span}>
<ElCard style={{ height: "100%" }} v-slots={ctx.slots} />
<ElCard class={clzName(props.noPadding)} style={{ height: "100%" }} v-slots={ctx.slots} />
</ElCol>
)
}
Expand Down
71 changes: 53 additions & 18 deletions src/app/components/Dashboard/components/Calendar/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
*/
import type { TitleComponentOption, TooltipComponentOption, GridComponentOption, VisualMapComponentOption } from "echarts/components"
import { TitleComponent, TooltipComponent, GridComponent, VisualMapComponent } from "echarts/components"
import { HeatmapChart, type HeatmapSeriesOption } from "echarts/charts"
import { ScatterChart, ScatterSeriesOption, type HeatmapSeriesOption } from "echarts/charts"
import { use, type ComposeOption } from "echarts/core"
import { SVGRenderer } from "echarts/renderers"

// Register echarts
use([
SVGRenderer,
HeatmapChart,
ScatterChart,
TooltipComponent,
GridComponent,
VisualMapComponent,
Expand All @@ -30,6 +30,7 @@ import { BASE_TITLE_OPTION } from "../../common"
import { getAppPageUrl } from "@util/constant/url"
import { REPORT_ROUTE } from "@app/router/constants"
import { createTabAfterCurrent } from "@api/chrome/tab"
import { getStepColors } from "@app/util/echarts"

type _Value = [
x: number,
Expand All @@ -39,7 +40,7 @@ type _Value = [
]

type EcOption = ComposeOption<
| HeatmapSeriesOption
| ScatterSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
Expand All @@ -58,11 +59,7 @@ function formatTooltip(mills: number, date: string): string {
const d = date.substring(6, 8)
const dateStr = t(msg => msg.calendar.dateFormat, { y, m, d })
const timeStr = formatPeriodCommon(mills)
return `${dateStr}</br>${timeStr}`
}

function getGridColors() {
return ['#9be9a8', '#40c263', '#30a04e', '#216039']
return `${dateStr}</br><b>${timeStr}</b>`
}

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

function optionOf(data: _Value[], weekDays: string[]): EcOption {
type Piece = {
label: string
min: number
max: number
color?: string
}

const minOf = (min: number) => min * 60 * 1000
const hourOf = (hour: number) => hour * 60 * 60 * 1000

const ALL_PIECES: Piece[] = [
{ min: 1, max: minOf(10), label: "<10m" },
{ min: minOf(10), max: minOf(30), label: "<30m" },
{ min: minOf(30), max: hourOf(1), label: "<1h" },
{ min: hourOf(1), max: hourOf(2), label: "<2h" },
{ min: hourOf(2), max: hourOf(4), label: "<4h" },
{ min: hourOf(4), max: hourOf(7), label: "<7h" },
{ min: hourOf(7), max: hourOf(12), label: "<12h" },
{ min: hourOf(12), max: hourOf(18), label: "<18h" },
{ min: hourOf(18), max: hourOf(24), label: ">=18h" },
]

const computePieces = (min: number, max: number): Piece[] => {
let pieces = ALL_PIECES.filter((p, i) => i === 0 || p.min <= max)
pieces = pieces.filter((p, i) => p.max > min || i === pieces.length - 1)

const colors = getStepColors(pieces.length)
return pieces.map((p, idx) => ({ ...p, color: colors[idx] }))
}

function optionOf(data: _Value[], weekDays: string[], dom: HTMLElement): EcOption {
const totalMills = sum(data?.map(d => d[2] ?? 0))
const totalHours = Math.floor(totalMills / MILL_PER_HOUR)
const xAxisLabelMap = getXAxisLabelMap(data)
const textColor = getPrimaryTextColor()
const w = dom?.getBoundingClientRect?.()?.width
const gridWidth = 0.85
const colCount = new Set(data.map(v => v[0])).size
const gridCellSize = colCount ? w * gridWidth / colCount * 0.75 : 0

const maxVal = Math.max(...data.map(a => a[2]))
const minVal = Math.min(...data.map(a => a[2]).filter(v => v))
return {
title: {
...BASE_TITLE_OPTION,
Expand All @@ -125,7 +159,7 @@ function optionOf(data: _Value[], weekDays: string[]): EcOption {
return mills ? formatTooltip(mills as number, date) : undefined
},
},
grid: { height: '70%', width: '82%', left: '8%', top: '18%', },
grid: { height: '70%', left: '7%', width: `${gridWidth * 100}%`, top: '18%', },
xAxis: {
type: 'category',
axisLine: { show: false },
Expand All @@ -145,22 +179,23 @@ function optionOf(data: _Value[], weekDays: string[]): EcOption {
axisTick: { show: false, alignWithLabel: true },
},
visualMap: {
min: 0,
max: Math.max(...data.map(a => a[2])),
inRange: { color: getGridColors() },
type: 'piecewise',
realtime: true,
calculable: true,
orient: 'vertical',
right: '2%',
top: 'center',
dimension: 2,
textStyle: { color: textColor },
splitNumber: 6,
showLabel: true,
pieces: computePieces(minVal, maxVal),
textStyle: { color: getPrimaryTextColor() },
},
series: {
type: 'heatmap',
type: 'scatter',
data: data.map(cvtHeatmapItem),
progressive: 5,
progressiveThreshold: 10,
symbol: 'circle',
symbolSize: gridCellSize,
},
}
}
Expand Down Expand Up @@ -203,7 +238,7 @@ class Wrapper extends EchartsWrapper<BizOption, EcOption> {
// Saturday to Sunday
rotate(weekDays, 1)
}
return optionOf(data, weekDays)
return optionOf(data, weekDays, this.getDom())
}

protected afterInit(): void {
Expand Down
Loading

0 comments on commit 88ac3f6

Please sign in to comment.