|
@@ -1,10 +1,358 @@
|
|
|
-import React from "react";
|
|
|
+import React, {useEffect, useRef, useState} from "react";
|
|
|
import styles from "./index.module.scss";
|
|
|
+import * as echarts from "echarts";
|
|
|
+import { A9_APIgetData } from "@/store/action/A9board";
|
|
|
+
|
|
|
+function ChartTitle({text} : {text: string}) {
|
|
|
+ return (
|
|
|
+ <div className={styles.ChartTitle}>
|
|
|
+ <div className="decorator"></div>
|
|
|
+ <h3 className="chart-title">{text}</h3>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function DataBar({keyName1, value1, keyName2, value2}: {
|
|
|
+ keyName1: string,
|
|
|
+ value1: number,
|
|
|
+ keyName2: string,
|
|
|
+ value2: number,
|
|
|
+}) {
|
|
|
+ if ((value1 + value2) === 0) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ const v1Rate = value1 / (value1 + value2)
|
|
|
+ const v2Rate = value2 / (value1 + value2)
|
|
|
+ const bar1Width = `calc(${v1Rate * 100}% + 10px - 4px)`
|
|
|
+ const bar2Width = `calc(${v2Rate * 100}% + 10px - 4px)`
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={styles.DataBar}>
|
|
|
+ <div className="bar-left" style={{
|
|
|
+ width: bar1Width,
|
|
|
+ clipPath: `polygon(0 0, calc(100% - (10px - 4px) - 10px) 0, 100% 100%, 0 100%)`,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <div className="name" title={keyName1}>{keyName1}</div>
|
|
|
+ <div className="value">{(v1Rate * 100).toFixed(2) }%</div>
|
|
|
+ </div>
|
|
|
+ <div className="bar-right" style={{
|
|
|
+ width: bar2Width,
|
|
|
+ clipPath: `polygon( 0 0, 100% 0, 100% 100%, calc(10px - 4px + 10px) 100%)`,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <div className="value">{(v2Rate * 100).toFixed()}%</div>
|
|
|
+ <div className="name" title={keyName2}>{keyName2}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function DataList({className, list, reverseOrder}: {
|
|
|
+ className?: string,
|
|
|
+ list: {
|
|
|
+ key: string,
|
|
|
+ value: number
|
|
|
+ }[],
|
|
|
+ reverseOrder?: boolean,
|
|
|
+}) {
|
|
|
+ return (
|
|
|
+ <ul className={`${styles.DataList} ${className}`}>
|
|
|
+ {list.map((item: {
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }) => {
|
|
|
+ if (reverseOrder) {
|
|
|
+ return (
|
|
|
+ <li key={item.key}>
|
|
|
+ <div
|
|
|
+ className="value"
|
|
|
+ style={{
|
|
|
+ textAlign: 'right',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {item.value}
|
|
|
+ </div>
|
|
|
+ <div className="key">{item.key}</div>
|
|
|
+ </li>
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ return (
|
|
|
+ <li key={item.key}>
|
|
|
+ <div className="key"
|
|
|
+ style={{
|
|
|
+ textAlign: 'right',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {item.key}
|
|
|
+ </div>
|
|
|
+ <div className="value">{item.value}</div>
|
|
|
+ </li>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ })}
|
|
|
+ </ul>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function BinChart({className, list}: {
|
|
|
+ className?: string,
|
|
|
+ list: {
|
|
|
+ key: string,
|
|
|
+ value: number
|
|
|
+ }[],
|
|
|
+}) {
|
|
|
+ let maxValue = 0
|
|
|
+ for (const item of list) {
|
|
|
+ if (item.value > maxValue) {
|
|
|
+ maxValue = item.value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let trackValue = 0
|
|
|
+ if (maxValue === 0) {
|
|
|
+ trackValue = 1
|
|
|
+ } else {
|
|
|
+ trackValue = maxValue * 1.2
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className={`${styles.BinChart} ${className}`}>
|
|
|
+ {list.map((item: {
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }) => {
|
|
|
+ return (
|
|
|
+ <div className="bin-track" key={item.key}>
|
|
|
+ <div className="bin" style={{
|
|
|
+ width: `${item.value / trackValue * 100}%`
|
|
|
+ }}></div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
function A9board() {
|
|
|
+ const chartRefTotalVisit = useRef(null);
|
|
|
+ const chartRefLoveCoin = useRef(null);
|
|
|
+
|
|
|
+ const [pageVisitData, setPageVisitData] = useState([])
|
|
|
+ const [totalVisitData, setTotalVisitData] = useState(0)
|
|
|
+ const [browserVisitData, setBrowserVisitData] = useState({
|
|
|
+ keyName1: '小程序访客',
|
|
|
+ value1: 0,
|
|
|
+ keyName2: '网页端访客',
|
|
|
+ value2: 0,
|
|
|
+ })
|
|
|
+ const [totalVisitorData, setTotalVisitorData] = useState({
|
|
|
+ keyName1: '注册访客',
|
|
|
+ value1: 0,
|
|
|
+ keyName2: '非注册访客',
|
|
|
+ value2: 0,
|
|
|
+ })
|
|
|
+ const [totaltotalVisitorData, setTotaltotalVisitorData] = useState(0)
|
|
|
+ const [hotRelicData, setHotRelicData] = useState<{
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }[]>([])
|
|
|
+ const [loveCoinData, setLoveCoinData] = useState<{
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }[]>([])
|
|
|
+ const [totalLoveCoinData, setTotalLoveCoinData] = useState(0)
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ A9_APIgetData().then((res) => {
|
|
|
+ console.log('sdfsdf', res);
|
|
|
+
|
|
|
+ let totalVisitNum = 0
|
|
|
+ setPageVisitData(res.data['总访问量'].map((item: {
|
|
|
+ groupKey: string,
|
|
|
+ pcsPc: number,
|
|
|
+ pcsWx: number,
|
|
|
+ }) => {
|
|
|
+ totalVisitNum += (item.pcsPc + item.pcsWx)
|
|
|
+ return {
|
|
|
+ key: item.groupKey,
|
|
|
+ value: item.pcsPc + item.pcsWx,
|
|
|
+ }
|
|
|
+ }))
|
|
|
+ setTotalVisitData(totalVisitNum)
|
|
|
+
|
|
|
+
|
|
|
+ let wxVisitNum = 0
|
|
|
+ let pcVisitNum = 0
|
|
|
+ for (const iterator of res.data['总访问量']) {
|
|
|
+ wxVisitNum += iterator.pcsWx
|
|
|
+ pcVisitNum += iterator.pcsPc
|
|
|
+ }
|
|
|
+ setBrowserVisitData({
|
|
|
+ keyName1: '小程序访客',
|
|
|
+ value1: wxVisitNum,
|
|
|
+ keyName2: '网页端访客',
|
|
|
+ value2: pcVisitNum,
|
|
|
+ })
|
|
|
+
|
|
|
+ setTotalVisitorData({
|
|
|
+ keyName1: '注册访客',
|
|
|
+ value1: res.data['总访客数'].pcsWx, // 后端字段命名如此,并非错误
|
|
|
+ keyName2: '非注册访客',
|
|
|
+ value2: res.data['总访客数'].pcsPc, // 后端字段命名如此,并非错误
|
|
|
+ })
|
|
|
+ setTotaltotalVisitorData(res.data['总访客数'].pcsWx + res.data['总访客数'].pcsPc)
|
|
|
+
|
|
|
+ const hotRelicDataTemp: {
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }[] = []
|
|
|
+ for (const key in res.data['热门遗存']) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(res.data['热门遗存'], key)) {
|
|
|
+ const element = res.data['热门遗存'][key];
|
|
|
+ hotRelicDataTemp.push({
|
|
|
+ key,
|
|
|
+ value: element,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setHotRelicData(hotRelicDataTemp)
|
|
|
+
|
|
|
+ const loveCoinDataTemp: {
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }[] = []
|
|
|
+ let totalLoveCoinDataTemp = 0
|
|
|
+ for (const key in res.data['爱心币奖励']) {
|
|
|
+ if (Object.prototype.hasOwnProperty.call(res.data['爱心币奖励'], key)) {
|
|
|
+ const element = res.data['爱心币奖励'][key];
|
|
|
+ loveCoinDataTemp.push({
|
|
|
+ key,
|
|
|
+ value: element,
|
|
|
+ })
|
|
|
+ totalLoveCoinDataTemp += element
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setLoveCoinData(loveCoinDataTemp)
|
|
|
+ setTotalLoveCoinData(totalLoveCoinDataTemp)
|
|
|
+ })
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ const chartTotalVisitOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: 'Access From',
|
|
|
+ type: 'pie',
|
|
|
+ radius: '60%',
|
|
|
+ data: pageVisitData.map((item: {
|
|
|
+ key: string,
|
|
|
+ value: number,
|
|
|
+ }) => {
|
|
|
+ return {
|
|
|
+ name: item.key,
|
|
|
+ value: item.value
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 10,
|
|
|
+ shadowOffsetX: 0,
|
|
|
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ echarts.init(chartRefTotalVisit.current).setOption(chartTotalVisitOption);
|
|
|
+ }, [pageVisitData])
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ const chartLoveCoinOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: 'Access From',
|
|
|
+ type: 'pie',
|
|
|
+ radius: '60%',
|
|
|
+ data: loveCoinData.map(item => {
|
|
|
+ return {
|
|
|
+ name: item.key,
|
|
|
+ value: item.value,
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 10,
|
|
|
+ shadowOffsetX: 0,
|
|
|
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ echarts.init(chartRefLoveCoin.current).setOption(chartLoveCoinOption);
|
|
|
+ }, [loveCoinData])
|
|
|
+
|
|
|
return (
|
|
|
<div className={styles.A9board}>
|
|
|
<div className="pageTitle">数据看板</div>
|
|
|
- <h1>等待开发</h1>
|
|
|
+ <p className="update-time">统计数据更新于 2024-04-06 6:00</p>
|
|
|
+ <div className="chart-area">
|
|
|
+ <div className="column-1">
|
|
|
+ <div className="total-visit chart-card">
|
|
|
+ <ChartTitle text={`总访问量:${totalVisitData}`}></ChartTitle>
|
|
|
+ <div className="chart-1">
|
|
|
+ <div className="pie-chart" ref={chartRefTotalVisit}></div>
|
|
|
+ <DataList
|
|
|
+ className="data-list"
|
|
|
+ list={pageVisitData}
|
|
|
+ ></DataList>
|
|
|
+ </div>
|
|
|
+ <div className="chart-2">
|
|
|
+ <DataBar keyName1={browserVisitData.keyName1} value1={browserVisitData.value1} keyName2={browserVisitData.keyName2} value2={browserVisitData.value2}></DataBar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="total-visitor chart-card">
|
|
|
+ <ChartTitle text={`总访客数:${totaltotalVisitorData}`}></ChartTitle>
|
|
|
+ <div className="chart-area">
|
|
|
+ <DataBar keyName1={'注册访客'} value1={1} keyName2={'非注册访客'} value2={2}></DataBar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="column-2">
|
|
|
+ <div className="hot-remain chart-card">
|
|
|
+ <ChartTitle text={'热门遗存'}></ChartTitle>
|
|
|
+ <div className="chart-1">
|
|
|
+ <DataList
|
|
|
+ className="data-list"
|
|
|
+ list={hotRelicData}
|
|
|
+ reverseOrder={true}
|
|
|
+ ></DataList>
|
|
|
+ <div className="bin-chart">
|
|
|
+ <BinChart list={hotRelicData}></BinChart>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="love-coin chart-card">
|
|
|
+ <ChartTitle text={`爱心币奖励:${totalLoveCoinData}`}></ChartTitle>
|
|
|
+ <div className="chart-1">
|
|
|
+ <div className="pie-chart" ref={chartRefLoveCoin}></div>
|
|
|
+ <DataList
|
|
|
+ className="data-list"
|
|
|
+ list={loveCoinData}
|
|
|
+ ></DataList>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
);
|
|
|
}
|