改动
This commit is contained in:
@@ -1,356 +0,0 @@
|
||||
import { ApexOptions } from 'apexcharts';
|
||||
import { useEffect, useState } from 'react';
|
||||
import ReactApexChart from 'react-apexcharts';
|
||||
import dayjs from 'dayjs'
|
||||
import { MonthlySums, Result } from './chart';
|
||||
|
||||
interface ChartOneState {
|
||||
series: {
|
||||
name: string;
|
||||
data: number[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const ChartOne = () => {
|
||||
const [result, setResult] = useState<Result>({} as Result)
|
||||
const [scope, setScope] = useState<"day" | "month" | "year">("day")
|
||||
const [startDate, setStartDate] = useState(dayjs(new Date()).subtract(7, "day").format("YYYY/MM/DD"))
|
||||
const [endDate, setEndDate] = useState(dayjs(new Date()).format("YYYY/MM/DD"))
|
||||
|
||||
const [options, setOptions] = useState<ApexOptions>({
|
||||
legend: {
|
||||
show: false,
|
||||
position: 'top',
|
||||
horizontalAlign: 'left',
|
||||
},
|
||||
colors: ['#3C50E0', '#80CAEE'],
|
||||
chart: {
|
||||
fontFamily: 'Satoshi, sans-serif',
|
||||
height: 335,
|
||||
type: 'area',
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
color: '#623CEA14',
|
||||
top: 10,
|
||||
blur: 4,
|
||||
left: 0,
|
||||
opacity: 0.1,
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1024,
|
||||
options: {
|
||||
chart: {
|
||||
height: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 1366,
|
||||
options: {
|
||||
chart: {
|
||||
height: 350,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
stroke: {
|
||||
width: [2, 2],
|
||||
curve: 'straight',
|
||||
},
|
||||
grid: {
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
markers: {
|
||||
size: 4,
|
||||
colors: '#fff',
|
||||
strokeColors: ['#3056D3', '#80CAEE'],
|
||||
strokeWidth: 3,
|
||||
strokeOpacity: 0.9,
|
||||
strokeDashArray: 0,
|
||||
fillOpacity: 1,
|
||||
discrete: [],
|
||||
hover: {
|
||||
size: undefined,
|
||||
sizeOffset: 5,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'category',
|
||||
categories: [],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
style: {
|
||||
fontSize: '0px',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const [state, setState] = useState<ChartOneState>({
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: [],
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const siteId = import.meta.env.VITE_BAIDU_TONGJI_SITE_ID
|
||||
const token = import.meta.env.VITE_BAIDU_TONGJI_ACCESS_TOKEN
|
||||
|
||||
fetch(`/api/rest/2.0/tongji/report/getData?access_token=${token}&site_id=${siteId}&start_date=${startDate}&end_date=${endDate}&metrics=pv_count%2Cip_count&method=overview%2FgetTimeTrendRpt`).then(async res => {
|
||||
const { result } = await res.json()
|
||||
console.log({ result });
|
||||
setResult(result)
|
||||
})
|
||||
}, [scope])
|
||||
|
||||
useEffect(() => {
|
||||
if (!result?.items?.length) return
|
||||
|
||||
switch (scope) {
|
||||
case "day":
|
||||
const categories_weeks = result.items[0].map((item: string[]) => {
|
||||
const year = new Date().getFullYear() + "/"
|
||||
return item[0].replace(year, "")
|
||||
})
|
||||
|
||||
setOptions((data) => (
|
||||
{
|
||||
...data,
|
||||
xaxis: { ...options.xaxis, categories: categories_weeks }
|
||||
}
|
||||
))
|
||||
|
||||
const pvList_weeks = result.items[1].map((item: number[]) => item[0])
|
||||
const ipList_weeks = result.items[1].map((item: number[]) => item[1])
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: pvList_weeks,
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: ipList_weeks,
|
||||
},
|
||||
],
|
||||
}));
|
||||
break
|
||||
case "month":
|
||||
const datesArray: string[][] = result.items[0];
|
||||
const valuesArray: (string | number)[][] = result.items[1];
|
||||
|
||||
const monthlySums: MonthlySums = {};
|
||||
|
||||
datesArray.forEach((dateArray, index) => {
|
||||
const date: string = dateArray[0];
|
||||
const [year, month, day] = date.split('/');
|
||||
|
||||
if (!monthlySums[month]) {
|
||||
monthlySums[month] = { pv: 0, ip: 0 };
|
||||
}
|
||||
|
||||
const pair = valuesArray[index];
|
||||
|
||||
if (pair.length === 2) {
|
||||
const firstValue = parseFloat(pair[0] as string);
|
||||
const secondValue = parseFloat(pair[1] as string);
|
||||
|
||||
if (!isNaN(firstValue) && !isNaN(secondValue)) {
|
||||
monthlySums[month].pv += firstValue;
|
||||
monthlySums[month].ip += secondValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setOptions((data) => (
|
||||
{
|
||||
...data,
|
||||
xaxis: { ...options.xaxis, categories: Object.keys(monthlySums) }
|
||||
}
|
||||
))
|
||||
|
||||
const list = Object.values(monthlySums)
|
||||
const pvList_month = list.map(item => item.pv)
|
||||
const ipList_month = list.map(item => item.ip)
|
||||
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: pvList_month,
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: ipList_month,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
break
|
||||
case "year":
|
||||
const yearlySums: { [year: string]: { pv: number, ip: number } } = {};
|
||||
|
||||
result.items[0].forEach((dateArray: string[], index: number) => {
|
||||
const date: string = dateArray[0];
|
||||
const [year, month, day] = date.split('/');
|
||||
|
||||
if (!yearlySums[year]) {
|
||||
yearlySums[year] = { pv: 0, ip: 0 };
|
||||
}
|
||||
|
||||
const pair = result.items[1][index];
|
||||
console.log(pair);
|
||||
|
||||
if (pair.length === 2) {
|
||||
const firstValue = parseFloat(pair[0] as string);
|
||||
const secondValue = parseFloat(pair[1] as string);
|
||||
|
||||
if (!isNaN(firstValue) && !isNaN(secondValue)) {
|
||||
yearlySums[year].pv += firstValue;
|
||||
yearlySums[year].ip += secondValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 删除没有数据的年份
|
||||
Object.keys(yearlySums).forEach(year => {
|
||||
if (yearlySums[year].pv === 0 && yearlySums[year].ip === 0) {
|
||||
delete yearlySums[year];
|
||||
}
|
||||
});
|
||||
|
||||
setOptions((data) => (
|
||||
{
|
||||
...data,
|
||||
xaxis: { ...options.xaxis, categories: Object.keys(yearlySums) }
|
||||
}
|
||||
));
|
||||
|
||||
const yearlyList = Object.values(yearlySums);
|
||||
const pvList_year = yearlyList.map(item => item.pv);
|
||||
const ipList_year = yearlyList.map(item => item.ip);
|
||||
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: pvList_year,
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: ipList_year,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
break
|
||||
}
|
||||
}, [result])
|
||||
|
||||
const handleReset = () => {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
}));
|
||||
};
|
||||
handleReset;
|
||||
|
||||
return (
|
||||
<div className="col-span-12 rounded-sm border border-stroke bg-white px-5 pt-7.5 pb-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-8">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3 sm:flex-nowrap">
|
||||
<div className="flex w-full flex-wrap gap-3 sm:gap-5">
|
||||
<div className="flex min-w-47.5">
|
||||
<span className="mt-1 mr-2 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-primary">
|
||||
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-primary"></span>
|
||||
</span>
|
||||
|
||||
<div className="w-full">
|
||||
<p className="font-semibold text-primary">访客(UV)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-47.5">
|
||||
<span className="mt-1 mr-2 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-secondary">
|
||||
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-secondary"></span>
|
||||
</span>
|
||||
|
||||
<div className="w-full">
|
||||
<p className="font-semibold text-secondary">IP</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full max-w-45 justify-end">
|
||||
<div className="inline-flex items-center rounded-md bg-whiter p-1.5 dark:bg-meta-4">
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "day" ? "bg-white shadow-card" : ""}`} onClick={() => {
|
||||
setScope("day")
|
||||
setStartDate(dayjs(new Date()).subtract(7, "day").format("YYYY/MM/DD"))
|
||||
}}>
|
||||
天
|
||||
</button>
|
||||
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "month" ? "bg-white shadow-card" : ""}`} onClick={() => {
|
||||
setScope("month")
|
||||
const year = new Date().getFullYear() + ""
|
||||
setStartDate(year + "/01/01")
|
||||
}}>
|
||||
月
|
||||
</button>
|
||||
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "year" ? "bg-white shadow-card" : ""}`} onClick={() => {
|
||||
setScope("year")
|
||||
setStartDate(dayjs(new Date()).subtract(5, "year").format("YYYY/MM/DD"))
|
||||
}}>
|
||||
年
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chartOne" className="-ml-5">
|
||||
<ReactApexChart
|
||||
options={options}
|
||||
series={state.series}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChartOne;
|
||||
7
src/components/Charts/chart.d.ts
vendored
7
src/components/Charts/chart.d.ts
vendored
@@ -15,3 +15,10 @@ export interface MonthlySums {
|
||||
ip: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChartOneState {
|
||||
series: {
|
||||
name: string;
|
||||
data: number[];
|
||||
}[];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ChartOne from '../../components/Charts/ChartOne';
|
||||
import VisitorsStatisChat from './components/VisitorsStatisChat';
|
||||
import ChartThree from '../../components/Charts/ChartThree';
|
||||
import ChartTwo from '../../components/Charts/ChartTwo';
|
||||
import ChatCard from '../../components/Chat/ChatCard';
|
||||
@@ -30,7 +30,7 @@ const ECommerce: React.FC = () => {
|
||||
</Card>
|
||||
|
||||
<div className="mt-4 grid grid-cols-12 gap-4 md:mt-6 md:gap-6 2xl:mt-7.5 2xl:gap-7.5">
|
||||
<ChartOne />
|
||||
<VisitorsStatisChat />
|
||||
<ChartTwo />
|
||||
<ChartThree />
|
||||
<MapOne />
|
||||
|
||||
326
src/pages/Dashboard/components/VisitorsStatisChat/index.tsx
Normal file
326
src/pages/Dashboard/components/VisitorsStatisChat/index.tsx
Normal file
@@ -0,0 +1,326 @@
|
||||
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import { ApexOptions } from 'apexcharts';
|
||||
import ReactApexChart from 'react-apexcharts';
|
||||
import { MonthlySums, Result, ChartOneState } from './type';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const VisitorsStatisChat = () => {
|
||||
const [result, setResult] = useState<Result | null>(null);
|
||||
const [scope, setScope] = useState<"day" | "month" | "year">("day");
|
||||
const [startDate, setStartDate] = useState(dayjs(new Date()).subtract(7, "day").format("YYYY/MM/DD"));
|
||||
const [endDate, setEndDate] = useState(dayjs(new Date()).format("YYYY/MM/DD"));
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
// Echarts相关配置
|
||||
const [options, setOptions] = useState<ApexOptions>({
|
||||
legend: {
|
||||
show: false,
|
||||
position: 'top',
|
||||
horizontalAlign: 'left',
|
||||
},
|
||||
colors: ['#3C50E0', '#80CAEE'],
|
||||
chart: {
|
||||
fontFamily: 'Satoshi, sans-serif',
|
||||
height: 335,
|
||||
type: 'area',
|
||||
dropShadow: {
|
||||
enabled: true,
|
||||
color: '#623CEA14',
|
||||
top: 10,
|
||||
blur: 4,
|
||||
left: 0,
|
||||
opacity: 0.1,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1024,
|
||||
options: {
|
||||
chart: {
|
||||
height: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 1366,
|
||||
options: {
|
||||
chart: {
|
||||
height: 350,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
stroke: {
|
||||
width: [2, 2],
|
||||
curve: 'straight',
|
||||
},
|
||||
grid: {
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
markers: {
|
||||
size: 4,
|
||||
colors: '#fff',
|
||||
strokeColors: ['#3056D3', '#80CAEE'],
|
||||
strokeWidth: 3,
|
||||
strokeOpacity: 0.9,
|
||||
strokeDashArray: 0,
|
||||
fillOpacity: 1,
|
||||
discrete: [],
|
||||
hover: {
|
||||
size: undefined,
|
||||
sizeOffset: 5,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'category',
|
||||
categories: [],
|
||||
axisBorder: {
|
||||
show: false,
|
||||
},
|
||||
axisTicks: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
style: {
|
||||
fontSize: '0px',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 核心数据
|
||||
const [state, setState] = useState<ChartOneState>({
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: [],
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 获取统计数据
|
||||
const getDataList = useCallback(async () => {
|
||||
setLoading(true);
|
||||
|
||||
const siteId = import.meta.env.VITE_BAIDU_TONGJI_SITE_ID;
|
||||
const token = import.meta.env.VITE_BAIDU_TONGJI_ACCESS_TOKEN;
|
||||
|
||||
const response = await fetch(`/api/rest/2.0/tongji/report/getData?access_token=${token}&site_id=${siteId}&start_date=${startDate}&end_date=${endDate}&metrics=pv_count%2Cip_count&method=overview%2FgetTimeTrendRpt`);
|
||||
const data = await response.json();
|
||||
const { result } = data;
|
||||
setResult(result);
|
||||
|
||||
setLoading(false);
|
||||
}, [startDate, endDate]);
|
||||
|
||||
useEffect(() => {
|
||||
getDataList();
|
||||
}, [getDataList]);
|
||||
|
||||
// 切换不同范围的数据
|
||||
const scopeData = useMemo(() => {
|
||||
if (!result?.items?.length) return { categories: [], series: [[], []] };
|
||||
|
||||
let categories = [];
|
||||
let pvList = [];
|
||||
let ipList = [];
|
||||
|
||||
switch (scope) {
|
||||
case "day":
|
||||
categories = result.items[0].map((item: string[]) => {
|
||||
const year = new Date().getFullYear() + "/";
|
||||
return item[0].replace(year, "");
|
||||
});
|
||||
|
||||
pvList = result.items[1].map((item: number[]) => item[0]);
|
||||
ipList = result.items[1].map((item: number[]) => item[1]);
|
||||
break;
|
||||
case "month":
|
||||
const datesArray: string[][] = result.items[0];
|
||||
const valuesArray: (string | number)[][] = result.items[1];
|
||||
|
||||
const monthlySums: MonthlySums = {};
|
||||
|
||||
datesArray.forEach((dateArray, index) => {
|
||||
const date: string = dateArray[0];
|
||||
const [year, month, day] = date.split('/');
|
||||
|
||||
if (!monthlySums[month]) {
|
||||
monthlySums[month] = { pv: 0, ip: 0 };
|
||||
}
|
||||
|
||||
const pair = valuesArray[index];
|
||||
|
||||
if (pair.length === 2) {
|
||||
const firstValue = parseFloat(pair[0] as string);
|
||||
const secondValue = parseFloat(pair[1] as string);
|
||||
|
||||
if (!isNaN(firstValue) && !isNaN(secondValue)) {
|
||||
monthlySums[month].pv += firstValue;
|
||||
monthlySums[month].ip += secondValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
categories = Object.keys(monthlySums);
|
||||
pvList = Object.values(monthlySums).map(item => item.pv);
|
||||
ipList = Object.values(monthlySums).map(item => item.ip);
|
||||
break;
|
||||
case "year":
|
||||
const yearlySums: { [year: string]: { pv: number, ip: number } } = {};
|
||||
|
||||
result.items[0].forEach((dateArray: string[], index: number) => {
|
||||
const date: string = dateArray[0];
|
||||
const [year, month, day] = date.split('/');
|
||||
|
||||
if (!yearlySums[year]) {
|
||||
yearlySums[year] = { pv: 0, ip: 0 };
|
||||
}
|
||||
|
||||
const pair = result.items[1][index];
|
||||
|
||||
if (pair.length === 2) {
|
||||
const firstValue = parseFloat(pair[0] + '');
|
||||
const secondValue = parseFloat(pair[1] + '');
|
||||
|
||||
if (!isNaN(firstValue) && !isNaN(secondValue)) {
|
||||
yearlySums[year].pv += firstValue;
|
||||
yearlySums[year].ip += secondValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 删除没有数据的年份
|
||||
Object.keys(yearlySums).forEach(year => {
|
||||
if (yearlySums[year].pv === 0 && yearlySums[year].ip === 0) {
|
||||
delete yearlySums[year];
|
||||
}
|
||||
});
|
||||
|
||||
categories = Object.keys(yearlySums);
|
||||
pvList = Object.values(yearlySums).map(item => item.pv);
|
||||
ipList = Object.values(yearlySums).map(item => item.ip);
|
||||
break;
|
||||
}
|
||||
|
||||
return { categories, series: [pvList, ipList] };
|
||||
}, [result, scope]);
|
||||
|
||||
// 当数据发生变化时,更新图表选项和状态
|
||||
useEffect(() => {
|
||||
setOptions((data) => ({
|
||||
...data,
|
||||
xaxis: { ...options.xaxis, categories: scopeData.categories }
|
||||
}));
|
||||
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
series: [
|
||||
{
|
||||
name: '访客数量',
|
||||
data: scopeData.series[0],
|
||||
},
|
||||
{
|
||||
name: 'IP数量',
|
||||
data: scopeData.series[1],
|
||||
},
|
||||
],
|
||||
}));
|
||||
}, [scopeData]);
|
||||
|
||||
// 处理范围变更并相应地更新日期范围
|
||||
const handleScopeChange = (newScope: "day" | "month" | "year") => {
|
||||
setScope(newScope);
|
||||
switch (newScope) {
|
||||
case "day":
|
||||
setStartDate(dayjs(new Date()).subtract(7, "day").format("YYYY/MM/DD"));
|
||||
break;
|
||||
case "month":
|
||||
const year = new Date().getFullYear() + "";
|
||||
setStartDate(year + "/01/01");
|
||||
break;
|
||||
case "year":
|
||||
setStartDate(dayjs(new Date()).subtract(5, "year").format("YYYY/MM/DD"));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="col-span-12 rounded-sm border border-stroke bg-white px-5 pt-7.5 pb-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-8">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3 sm:flex-nowrap">
|
||||
<div className="flex w-full flex-wrap gap-3 sm:gap-5">
|
||||
<div className="flex min-w-47.5">
|
||||
<span className="mt-1 mr-2 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-primary">
|
||||
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-primary"></span>
|
||||
</span>
|
||||
|
||||
<div className="w-full">
|
||||
<p className="font-semibold text-primary">访客(UV)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-47.5">
|
||||
<span className="mt-1 mr-2 flex h-4 w-full max-w-4 items-center justify-center rounded-full border border-secondary">
|
||||
<span className="block h-2.5 w-full max-w-2.5 rounded-full bg-secondary"></span>
|
||||
</span>
|
||||
|
||||
<div className="w-full">
|
||||
<p className="font-semibold text-secondary">IP</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full max-w-45 justify-end">
|
||||
<div className="inline-flex items-center rounded-md bg-whiter p-1.5 dark:bg-meta-4">
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "day" ? "bg-white shadow-card" : ""}`} onClick={() => handleScopeChange("day")}>
|
||||
天
|
||||
</button>
|
||||
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "month" ? "bg-white shadow-card" : ""}`} onClick={() => handleScopeChange("month")}>
|
||||
月
|
||||
</button>
|
||||
|
||||
<button className={`rounded py-1 px-3 text-xs font-medium text-black hover:bg-white hover:shadow-card dark:bg-boxdark dark:text-white dark:hover:bg-boxdark ${scope === "year" ? "bg-white shadow-card" : ""}`} onClick={() => handleScopeChange("year")}>
|
||||
年
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Spin spinning={loading}>
|
||||
<div id="chartOne" className="-ml-5">
|
||||
<ReactApexChart
|
||||
options={options}
|
||||
series={state.series}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VisitorsStatisChat;
|
||||
24
src/pages/Dashboard/components/VisitorsStatisChat/type.d.ts
vendored
Normal file
24
src/pages/Dashboard/components/VisitorsStatisChat/type.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
export interface Result {
|
||||
timeSpan: string[];
|
||||
fields: string[];
|
||||
items: [
|
||||
string[][],
|
||||
number[][],
|
||||
any[],
|
||||
any[]
|
||||
];
|
||||
}
|
||||
|
||||
export interface MonthlySums {
|
||||
[key: string]: {
|
||||
pv: number;
|
||||
ip: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChartOneState {
|
||||
series: {
|
||||
name: string;
|
||||
data: number[];
|
||||
}[];
|
||||
}
|
||||
Reference in New Issue
Block a user