mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
update history charts
This commit is contained in:
@@ -5,14 +5,51 @@ import { HISTORY_API_URL } from '@/data/constants'
|
|||||||
import { Info24Filled } from '@vicons/fluent'
|
import { Info24Filled } from '@vicons/fluent'
|
||||||
import { addDays, addHours, format, isSameDay, isSameHour, startOfDay, startOfHour } from 'date-fns'
|
import { addDays, addHours, format, isSameDay, isSameHour, startOfDay, startOfHour } from 'date-fns'
|
||||||
import { BarChart, LineChart } from 'echarts/charts'
|
import { BarChart, LineChart } from 'echarts/charts'
|
||||||
import { DataZoomComponent, GridComponent, LegendComponent, TitleComponent, ToolboxComponent, TooltipComponent } from 'echarts/components'
|
import {
|
||||||
|
DataZoomComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
TitleComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
} from 'echarts/components'
|
||||||
import { use } from 'echarts/core'
|
import { use } from 'echarts/core'
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
import { NAlert, NButton, NCard, NIcon, NSpace, NSpin, NText, NTooltip, useMessage } from 'naive-ui'
|
import { NAlert, NButton, NCard, NDivider, NIcon, NSpace, NSpin, NText, NTime, NTooltip, useMessage } from 'naive-ui'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import VChart from 'vue-echarts'
|
import VChart from 'vue-echarts'
|
||||||
|
|
||||||
use([CanvasRenderer, LineChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent, DataZoomComponent, LineChart, ToolboxComponent, BarChart])
|
use([
|
||||||
|
CanvasRenderer,
|
||||||
|
LineChart,
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
LegendComponent,
|
||||||
|
GridComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
LineChart,
|
||||||
|
ToolboxComponent,
|
||||||
|
BarChart,
|
||||||
|
])
|
||||||
|
type HistoryModel = {
|
||||||
|
fan: {
|
||||||
|
records: HistoryRecordModel[]
|
||||||
|
updateAt: number
|
||||||
|
}
|
||||||
|
guard: {
|
||||||
|
records: HistoryRecordModel[]
|
||||||
|
updateAt: number
|
||||||
|
}
|
||||||
|
upstat: {
|
||||||
|
records: HistoryUpstatRecordModel[]
|
||||||
|
updateAt: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type HistoryRecordModel = {
|
||||||
|
time: number
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
type HistoryUpstatRecordModel = { time: number; stats: { views: number; likes: number } }
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
@@ -20,6 +57,9 @@ const message = useMessage()
|
|||||||
const fansHistory = ref<{ time: number; count: number }[]>()
|
const fansHistory = ref<{ time: number; count: number }[]>()
|
||||||
const guardHistory = ref<{ time: number; count: number }[]>()
|
const guardHistory = ref<{ time: number; count: number }[]>()
|
||||||
const upstatHistory = ref<{ time: number; stats: { views: number; likes: number } }[]>()
|
const upstatHistory = ref<{ time: number; stats: { views: number; likes: number } }[]>()
|
||||||
|
const fansUpdateAt = ref(0)
|
||||||
|
const guardUpdateAt = ref(0)
|
||||||
|
const upstatUpdateAt = ref(0)
|
||||||
const fansOption = ref()
|
const fansOption = ref()
|
||||||
const guardsOption = ref()
|
const guardsOption = ref()
|
||||||
const upstatViewOption = ref()
|
const upstatViewOption = ref()
|
||||||
@@ -27,55 +67,16 @@ const upstatLikeOption = ref()
|
|||||||
|
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
|
|
||||||
async function getFansHistory() {
|
async function getHistory() {
|
||||||
await QueryGetAPI<
|
await QueryGetAPI<HistoryModel>(HISTORY_API_URL + 'get-all')
|
||||||
{
|
|
||||||
time: number
|
|
||||||
count: number
|
|
||||||
}[]
|
|
||||||
>(HISTORY_API_URL + 'fans')
|
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
fansHistory.value = data.data
|
fansHistory.value = data.data.fan.records
|
||||||
} else {
|
guardHistory.value = data.data.guard.records
|
||||||
message.error('加载失败: ' + data.message)
|
upstatHistory.value = data.data.upstat.records
|
||||||
}
|
fansUpdateAt.value = data.data.fan.updateAt
|
||||||
})
|
guardUpdateAt.value = data.data.guard.updateAt
|
||||||
.catch((err) => {
|
upstatUpdateAt.value = data.data.upstat.updateAt
|
||||||
message.error('加载失败')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
async function getGuardsHistory() {
|
|
||||||
await QueryGetAPI<
|
|
||||||
{
|
|
||||||
time: number
|
|
||||||
count: number
|
|
||||||
}[]
|
|
||||||
>(HISTORY_API_URL + 'guards')
|
|
||||||
.then((data) => {
|
|
||||||
if (data.code == 200) {
|
|
||||||
guardHistory.value = data.data
|
|
||||||
} else {
|
|
||||||
message.error('加载失败: ' + data.message)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
message.error('加载失败')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
async function getUpstatHistory() {
|
|
||||||
await QueryGetAPI<
|
|
||||||
{
|
|
||||||
time: number
|
|
||||||
stats: {
|
|
||||||
views: number
|
|
||||||
likes: number
|
|
||||||
}
|
|
||||||
}[]
|
|
||||||
>(HISTORY_API_URL + 'upstat')
|
|
||||||
.then((data) => {
|
|
||||||
if (data.code == 200) {
|
|
||||||
upstatHistory.value = data.data
|
|
||||||
} else {
|
} else {
|
||||||
message.error('加载失败: ' + data.message)
|
message.error('加载失败: ' + data.message)
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,11 @@ async function getUpstatHistory() {
|
|||||||
function isSameDaySimple(time1: number, time2: number) {
|
function isSameDaySimple(time1: number, time2: number) {
|
||||||
const time1Date = new Date(time1)
|
const time1Date = new Date(time1)
|
||||||
const time2Date = new Date(time2)
|
const time2Date = new Date(time2)
|
||||||
return time1Date.getFullYear() === time2Date.getFullYear() && time1Date.getMonth() === time2Date.getMonth() && time1Date.getDate() === time2Date.getDate()
|
return (
|
||||||
|
time1Date.getFullYear() === time2Date.getFullYear() &&
|
||||||
|
time1Date.getMonth() === time2Date.getMonth() &&
|
||||||
|
time1Date.getDate() === time2Date.getDate()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const statisticStartDate = new Date(2023, 10, 4)
|
const statisticStartDate = new Date(2023, 10, 4)
|
||||||
const statisticStartDateTime = statisticStartDate.getTime()
|
const statisticStartDateTime = statisticStartDate.getTime()
|
||||||
@@ -105,7 +110,12 @@ function getOptions() {
|
|||||||
|
|
||||||
if (fansHistory.value) {
|
if (fansHistory.value) {
|
||||||
let currentTime = startTime
|
let currentTime = startTime
|
||||||
let lastFansTimeIndex = fansHistory.value.length > 0 ? (fansHistory.value[0].time >= statisticStartDateTime ? 0 : fansHistory.value.findIndex((entry) => entry.time >= statisticStartDateTime)) : -1
|
let lastFansTimeIndex =
|
||||||
|
fansHistory.value.length > 0
|
||||||
|
? fansHistory.value[0].time >= statisticStartDateTime
|
||||||
|
? 0
|
||||||
|
: fansHistory.value.findIndex((entry) => entry.time >= statisticStartDateTime)
|
||||||
|
: -1
|
||||||
let lastDayCount = lastFansTimeIndex >= 0 ? fansHistory.value[lastFansTimeIndex].count : 0
|
let lastDayCount = lastFansTimeIndex >= 0 ? fansHistory.value[lastFansTimeIndex].count : 0
|
||||||
// 生成完整的小时序列
|
// 生成完整的小时序列
|
||||||
while (currentTime <= endTime) {
|
while (currentTime <= endTime) {
|
||||||
@@ -228,10 +238,6 @@ function getOptions() {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
fansOption.value = {
|
fansOption.value = {
|
||||||
title: {
|
|
||||||
text: '粉丝数',
|
|
||||||
left: 'left',
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
@@ -240,6 +246,17 @@ function getOptions() {
|
|||||||
color: '#999',
|
color: '#999',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
formatter: (param: any) => {
|
||||||
|
let name = param[0].name + '<br>'
|
||||||
|
let str = ''
|
||||||
|
for (var i = 0; i < param.length; i++) {
|
||||||
|
const status =
|
||||||
|
param[i].seriesName == '粉丝数' ? (completeTimeSeries[param[i].dataIndex].change ? '' : '(未获取)') : ''
|
||||||
|
const statusHtml = status == '' ? '' : ' <span style="color:gray">' + status + '</span>'
|
||||||
|
str += param[i].marker + param[i].seriesName + ':' + param[i].data + statusHtml + '<br>'
|
||||||
|
}
|
||||||
|
return name + str
|
||||||
|
},
|
||||||
},
|
},
|
||||||
toolbox: {
|
toolbox: {
|
||||||
feature: {
|
feature: {
|
||||||
@@ -274,9 +291,6 @@ function getOptions() {
|
|||||||
},
|
},
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
data: chartData.xAxisData,
|
data: chartData.xAxisData,
|
||||||
formatter: (value: number, index: number) => {
|
|
||||||
return `${value}${completeTimeSeries[index].change ? '' : '(无变化)'}`
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -303,6 +317,17 @@ function getOptions() {
|
|||||||
focus: 'series',
|
focus: 'series',
|
||||||
},
|
},
|
||||||
data: chartData.hourlyCounts,
|
data: chartData.hourlyCounts,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: function (data: any) {
|
||||||
|
if (completeTimeSeries[data.dataIndex].change) {
|
||||||
|
return '#18a058'
|
||||||
|
} else {
|
||||||
|
return '#5470C6'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '增量 /日',
|
name: '增量 /日',
|
||||||
@@ -332,10 +357,10 @@ function getOptions() {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
guardsOption.value = {
|
guardsOption.value = {
|
||||||
title: {
|
/*title: {
|
||||||
text: '舰长数',
|
text: '舰长数',
|
||||||
left: 'left',
|
left: 'left',
|
||||||
},
|
},*/
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
@@ -401,10 +426,10 @@ function getOptions() {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
upstatViewOption.value = {
|
upstatViewOption.value = {
|
||||||
title: {
|
/*title: {
|
||||||
text: '投稿播放数',
|
text: '投稿播放数',
|
||||||
left: 'left',
|
left: 'left',
|
||||||
},
|
},*/
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
@@ -463,10 +488,10 @@ function getOptions() {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
upstatLikeOption.value = {
|
upstatLikeOption.value = {
|
||||||
title: {
|
/*title: {
|
||||||
text: '投稿点赞数',
|
text: '投稿点赞数',
|
||||||
left: 'left',
|
left: 'left',
|
||||||
},
|
},*/
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
},
|
},
|
||||||
@@ -528,9 +553,7 @@ function getOptions() {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (accountInfo.value?.isBiliVerified == true) {
|
if (accountInfo.value?.isBiliVerified == true) {
|
||||||
await getFansHistory()
|
await getHistory()
|
||||||
await getGuardsHistory()
|
|
||||||
await getUpstatHistory()
|
|
||||||
getOptions()
|
getOptions()
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -552,7 +575,9 @@ onMounted(async () => {
|
|||||||
</template>
|
</template>
|
||||||
更新速度:
|
更新速度:
|
||||||
<NSpace vertical>
|
<NSpace vertical>
|
||||||
<span> 粉丝数: 1000粉以下: 每24小时一次, 1000-10000粉: 每6小时一次, 10000粉以上: 每小时一次 </span>
|
<span>
|
||||||
|
粉丝数: 200粉以下: 每3天一次, 200-1000粉: 每24小时一次, 1000-10000粉: 每6小时一次, 10000粉以上: 每小时一次
|
||||||
|
</span>
|
||||||
<span> 舰长数: 10舰以下: 每24小时一次, 10-50舰: 每12小时一次, 50舰以上: 每6小时一次 </span>
|
<span> 舰长数: 10舰以下: 每24小时一次, 10-50舰: 每12小时一次, 50舰以上: 每6小时一次 </span>
|
||||||
<span> 投稿数据: 每天一次 </span>
|
<span> 投稿数据: 每天一次 </span>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
@@ -560,9 +585,63 @@ onMounted(async () => {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<NSpace vertical>
|
<NSpace vertical>
|
||||||
|
<NDivider>
|
||||||
|
粉丝
|
||||||
|
<NDivider vertical />
|
||||||
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<span>
|
||||||
|
<NTime :time="fansUpdateAt" type="relative" />
|
||||||
|
更新
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<NTime :time="fansUpdateAt" />
|
||||||
|
</NTooltip>
|
||||||
|
</NDivider>
|
||||||
<VChart :option="fansOption" style="height: 200px" />
|
<VChart :option="fansOption" style="height: 200px" />
|
||||||
|
<NDivider>
|
||||||
|
舰长
|
||||||
|
<NDivider vertical />
|
||||||
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<span>
|
||||||
|
<NTime :time="guardUpdateAt" type="relative" />
|
||||||
|
更新
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<NTime :time="guardUpdateAt" />
|
||||||
|
</NTooltip>
|
||||||
|
</NDivider>
|
||||||
<VChart :option="guardsOption" style="height: 200px" />
|
<VChart :option="guardsOption" style="height: 200px" />
|
||||||
|
|
||||||
|
<NDivider>
|
||||||
|
投稿播放量
|
||||||
|
<NDivider vertical />
|
||||||
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<span>
|
||||||
|
<NTime :time="upstatUpdateAt" type="relative" />
|
||||||
|
更新
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<NTime :time="upstatUpdateAt" />
|
||||||
|
</NTooltip>
|
||||||
|
</NDivider>
|
||||||
<VChart :option="upstatViewOption" style="height: 200px" />
|
<VChart :option="upstatViewOption" style="height: 200px" />
|
||||||
|
|
||||||
|
<NDivider>
|
||||||
|
投稿点赞量
|
||||||
|
<NDivider vertical />
|
||||||
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<span>
|
||||||
|
<NTime :time="upstatUpdateAt" type="relative" />
|
||||||
|
更新
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<NTime :time="upstatUpdateAt" />
|
||||||
|
</NTooltip>
|
||||||
|
</NDivider>
|
||||||
<VChart :option="upstatLikeOption" style="height: 200px" />
|
<VChart :option="upstatLikeOption" style="height: 200px" />
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
|||||||
Reference in New Issue
Block a user