排版
现在做首页的排版,依旧是偷antd里面的东西
使用card包裹list的样式
import React from 'react'
import 'axios'
import { Card, Col, Row, List } from 'antd'
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar } from 'antd';
const { Meta } = Card;
function Home() {
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={['qq', 'wx', 'dy']}
renderItem={(item) => <List.Item>{item}</List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={['qq', 'wx', 'dy']}
renderItem={(item) => <List.Item>{item}</List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title="Card title"
description="This is the description"
/>
</Card>
</Col>
</Row>
</div>
)
}
export default Home
数据
接下来就是写数据请求之类的了
做后端数据请求+显示:
import React, { useEffect } from 'react'
import 'axios'
import { Card, Col, Row, List } from 'antd'
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar } from 'antd';
import axios from 'axios';
import { useState } from'react'
const { Meta } = Card;
function Home() {
const [viewList, setviewList] = useState([])
const [starList, setstarList] = useState([])
useEffect(()=>{
axios.get("http://localhost:3000/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6").then(res=>{
setviewList(res.data)
}).catch(err => {
console.error('Request failed', err)
})
},[])
useEffect(()=>{
axios.get("http://localhost:3000/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6").then(res=>{
setstarList(res.data)
}).catch(err => {
console.error('Request failed', err)
})
},[])
const {username,region,role:{roleName}} = JSON.parse(localStorage.getItem('token'))
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={viewList}
renderItem={(item) => <List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={starList}
renderItem={(item) => <List.Item><a href={`#/news-manage/preview/${item.id}`}>{item.title}</a></List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title={username}
description={
<div>
<b>{region?region:'全球'}</b>
<span style={{
paddingLeft:"30px"
}}>{roleName}</span>
</div>
}
/>
</Card>
</Col>
</Row>
</div>
)
}
export default Home
柱状图
要做柱状图,需要用到一个开源的可视化的库:Apache EChartshttps://echarts.apache.org/zh/index.html
echarts也是需要下载的:
npm i --save echarts
使用相对来说简单,先做好模块的初始化,然后进行导入,复制粘贴就好了:
import React, { useEffect } from 'react'
import 'axios'
import { Card, Col, Row, List } from 'antd'
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { Avatar } from 'antd';
import axios from 'axios';
import * as Echarts from 'echarts';
// 把所有东西都导进打模块中
import { useState } from'react'
const { Meta } = Card;
function Home() {
const [viewList, setviewList] = useState([])
const [starList, setstarList] = useState([])
useEffect(()=>{
axios.get("http://localhost:3000/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6").then(res=>{
setviewList(res.data)
}).catch(err => {
console.error('Request failed', err)
})
},[])
useEffect(()=>{
axios.get("http://localhost:3000/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6").then(res=>{
setstarList(res.data)
}).catch(err => {
console.error('Request failed', err)
})
},[])
useEffect(()=>{
var myChart = Echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
},[])
const {username,region,role:{roleName}} = JSON.parse(localStorage.getItem('token'))
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={viewList}
renderItem={(item) => <List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={starList}
renderItem={(item) => <List.Item><a href={`#/news-manage/preview/${item.id}`}>{item.title}</a></List.Item>}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title={username}
description={
<div>
<b>{region?region:'全球'}</b>
<span style={{
paddingLeft:"30px"
}}>{roleName}</span>
</div>
}
/>
</Card>
</Col>
</Row>
<div id="main" style={{
width:"100%",
height:"400px",
marginTop:"30px",
}}></div>
</div>
)
}
export default Home
接下来做样式的统一处理,引入lodash库
import React, { useEffect, useRef } from 'react'
import 'axios'
import { Card, Col, Row, List } from 'antd'
import {
EditOutlined,
EllipsisOutlined,
SettingOutlined,
} from '@ant-design/icons'
import { Avatar } from 'antd'
import axios from 'axios'
import * as Echarts from 'echarts'
// 把所有东西都导进打模块中
import { useState } from 'react'
import _ from 'lodash'
const { Meta } = Card
function Home() {
const [viewList, setviewList] = useState([])
const [starList, setstarList] = useState([])
const barRef = useRef(null)
useEffect(() => {
axios
.get(
'http://localhost:3000/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6'
)
.then((res) => {
setviewList(res.data)
})
.catch((err) => {
console.error('Request failed', err)
})
}, [])
useEffect(() => {
axios
.get(
'http://localhost:3000/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6'
)
.then((res) => {
setstarList(res.data)
})
.catch((err) => {
console.error('Request failed', err)
})
}, [])
useEffect(() => {
axios
.get('/news?publishState=2&_expand=category')
.then((res) => {
console.log(res.data)
renderBarView(_.groupBy(res.data, (item) => item.category.title))
})
.catch((err) => {
console.error('Request failed', err)
})
const renderBarView = (obj) => {
var myChart = Echarts.init(barRef.current)
// 指定图表的配置项和数据
var option = {
title: {
text: '新闻分类图示',
},
tooltip: {},
legend: {
data: ['数量'],
},
xAxis: {
data: Object.keys(obj),
},
yAxis: {},
series: [
{
name: '数量',
type: 'bar',
// 把数组映射成长度
data: Object.values(obj).map((item) => item.length),
},
],
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
return () => {
myChart.dispose()
}
}
}, [])
const {
username,
region,
role: { roleName },
} = JSON.parse(localStorage.getItem('token'))
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={viewList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={starList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title={username}
description={
<div>
<b>{region ? region : '全球'}</b>
<span
style={{
paddingLeft: '30px',
}}
>
{roleName}
</span>
</div>
}
/>
</Card>
</Col>
</Row>
<div
ref={barRef}
style={{
width: '100%',
height: '400px',
marginTop: '30px',
}}
></div>
</div>
)
}
export default Home
现在的分类做出来了,但是没有做响应式,所以优化一下
响应式
进行响应式的优化:
import React, { useEffect, useRef } from 'react'
import { Card, Col, Row, List } from 'antd'
import {
EditOutlined,
EllipsisOutlined,
SettingOutlined,
} from '@ant-design/icons'
import { Avatar } from 'antd'
import axios from 'axios'
import * as Echarts from 'echarts'
// 把所有东西都导进打模块中
import { useState } from 'react'
import _ from 'lodash'
const { Meta } = Card
function Home() {
const [viewList, setviewList] = useState([])
const [starList, setstarList] = useState([])
const barRef = useRef(null)
useEffect(() => {
axios
.get(
'http://localhost:3000/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6'
)
.then((res) => {
setviewList(res.data)
})
.catch((err) => {
console.error('Request failed', err)
})
}, [])
useEffect(() => {
axios
.get(
'http://localhost:3000/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6'
)
.then((res) => {
setstarList(res.data)
})
.catch((err) => {
console.error('Request failed', err)
})
}, [])
useEffect(() => {
axios
.get('/news?publishState=2&_expand=category')
.then((res) => {
renderBarView(_.groupBy(res.data, (item) => item.category.title))
})
return ()=>{
window.onresize = null
}
}, [])
const renderBarView = (obj) => {
var myChart = Echarts.init(barRef.current)
// 指定图表的配置项和数据
var option = {
title: {
text: '新闻分类图示',
},
tooltip: {},
legend: {
data: ['数量'],
},
xAxis: {
data: Object.keys(obj),
axisLabel: {
rotate: "60",
//强制显示
interval: 0,
},
},
yAxis: {
// 让显示全是整数
minInterval: 1
},
series: [
{
name: '数量',
type: 'bar',
// 把数组映射成长度
data: Object.values(obj).map((item) => item.length),
},
],
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
window.onresize =()=>{
//每次自动触发
myChart.resize()
}
return () => {
myChart.dispose()
}
}
const {
username,
region,
role: { roleName },
} = JSON.parse(localStorage.getItem('token'))
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={viewList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={starList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title={username}
description={
<div>
<b>{region ? region : '全球'}</b>
<span
style={{
paddingLeft: '30px',
}}
>
{roleName}
</span>
</div>
}
/>
</Card>
</Col>
</Row>
<div
ref={barRef}
style={{
width: '100%',
height: '400px',
marginTop: '30px',
}}
></div>
</div>
)
}
export default Home
用onresize即可
饼状图
最开始写的代码有问题,会出现显示不及时或者显示出来的格式是错的的问题:
// import React, { useEffect, useRef, useState } from 'react';
// import { Card, Col, Row, List, Drawer } from 'antd';
// import {
// EditOutlined,
// EllipsisOutlined,
// SettingOutlined,
// } from '@ant-design/icons';
// import { Avatar } from 'antd';
// import axios from 'axios';
// import * as Echarts from 'echarts';
// import _ from 'lodash';
// const { Meta } = Card;
// function Home() {
// const [viewList, setviewList] = useState([]);
// const [starList, setstarList] = useState([]);
// const [visible, setvisible] = useState(false);
// const barRef = useRef(null);
// const pieRef = useRef(null);
// useEffect(() => {
// axios
// .get(
// 'http://localhost:3000/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6'
// )
// .then((res) => {
// setviewList(res.data);
// })
// .catch((err) => {
// console.error('Request failed', err);
// });
// }, []);
// useEffect(() => {
// axios
// .get(
// 'http://localhost:3000/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6'
// )
// .then((res) => {
// setstarList(res.data);
// })
// .catch((err) => {
// console.error('Request failed', err);
// });
// }, []);
// useEffect(() => {
// let barCleanup;
// let barResizeHandler;
// let pieCleanup;
// let pieResizeHandler;
// const fetchDataAndRender = async () => {
// try {
// const res = await axios.get('/news?publishState=2&_expand=category');
// if (barRef.current) {
// barCleanup = renderBarView(
// _.groupBy(res.data, (item) => item.category.title)
// );
// barResizeHandler = () => {
// if (barCleanup) {
// const myChart = Echarts.getInstanceByDom(barRef.current);
// if (myChart) {
// myChart.resize();
// }
// }
// };
// window.addEventListener('resize', barResizeHandler);
// }
// } catch (err) {
// console.error('Request failed', err);
// }
// };
// fetchDataAndRender();
// return () => {
// if (typeof barCleanup === 'function') {
// barCleanup();
// }
// if (barResizeHandler) {
// window.removeEventListener('resize', barResizeHandler);
// }
// if (typeof pieCleanup === 'function') {
// pieCleanup();
// }
// if (pieResizeHandler) {
// window.removeEventListener('resize', pieResizeHandler);
// }
// };
// }, []);
// const renderBarView = (obj) => {
// if (!barRef.current) return;
// var myChart = Echarts.init(barRef.current);
// // 指定图表的配置项和数据
// var option = {
// title: {
// text: '新闻分类图示',
// },
// tooltip: {},
// legend: {
// data: ['数量'],
// },
// xAxis: {
// data: Object.keys(obj),
// axisLabel: {
// rotate: 60,
// // 强制显示
// interval: 0,
// },
// },
// yAxis: {
// // 让显示全是整数
// minInterval: 1,
// },
// series: [
// {
// name: '数量',
// type: 'bar',
// // 把数组映射成长度
// data: Object.values(obj).map((item) => item.length),
// },
// ],
// };
// // 使用刚指定的配置项和数据显示图表。
// myChart.setOption(option);
// return () => {
// myChart.dispose();
// };
// };
// const renderPieView = (obj) => {
// if (!pieRef.current) return;
// var myChart = Echarts.init(pieRef.current);
// var option = {
// title: {
// text: 'Referer of a Website',
// subtext: 'Fake Data',
// left: 'center',
// },
// tooltip: {
// trigger: 'item',
// },
// legend: {
// orient: 'vertical',
// left: 'left',
// },
// series: [
// {
// name: 'Access From',
// type: 'pie',
// radius: '50%',
// data: [
// { value: 1048, name: 'Search Engine' },
// { value: 735, name: 'Direct' },
// { value: 580, name: 'Email' },
// { value: 484, name: 'Union Ads' },
// { value: 300, name: 'Video Ads' },
// ],
// emphasis: {
// itemStyle: {
// shadowBlur: 10,
// shadowOffsetX: 0,
// shadowColor: 'rgba(0, 0, 0, 0.5)',
// },
// },
// },
// ],
// };
// option && myChart.setOption(option);
// const resizeHandler = () => {
// myChart.resize();
// };
// window.addEventListener('resize', resizeHandler);
// return () => {
// window.removeEventListener('resize', resizeHandler);
// myChart.dispose();
// };
// };
// const tokenData = localStorage.getItem('token');
// const {
// username = '',
// region = '',
// role: { roleName = '' } = {},
// } = tokenData ? JSON.parse(tokenData) : {};
// return (
// <div>
// <Row gutter={16}>
// <Col span={8}>
// <Card title="用户最常浏览" variant="border">
// <List
// size="small"
// dataSource={viewList}
// renderItem={(item) => (
// <List.Item>
// <a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
// </List.Item>
// )}
// />
// </Card>
// </Col>
// <Col span={8}>
// <Card title="用户点赞最多" variant="border">
// <List
// size="small"
// dataSource={starList}
// renderItem={(item) => (
// <List.Item>
// <a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
// </List.Item>
// )}
// />
// </Card>
// </Col>
// <Col span={8}>
// <Card
// cover={
// <img
// alt="example"
// src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
// />
// }
// actions={[
// <SettingOutlined
// key="setting"
// onClick={() => {
// setTimeout(() => {
// setvisible(true);
// //init初始化
// renderPieView();
// }, 0);
// }}
// />,
// <EditOutlined key="edit" />,
// <EllipsisOutlined key="ellipsis" />,
// ]}
// >
// <Meta
// avatar={
// <Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
// }
// title={username}
// description={
// <div>
// <b>{region ? region : '全球'}</b>
// <span
// style={{
// paddingLeft: '30px',
// }}
// >
// {roleName}
// </span>
// </div>
// }
// />
// </Card>
// </Col>
// </Row>
// <Drawer
// width="500px"
// title="个人新闻分类"
// placement="right"
// closable={true}
// onClose={() => {
// setvisible(false);
// }}
// visible={visible}
// >
// <div
// ref={pieRef}
// style={{
// width: '100%',
// height: '400px',
// marginTop: '30px',
// }}
// ></div>
// </Drawer>
// <div
// ref={barRef}
// style={{
// width: '100%',
// height: '400px',
// marginTop: '30px',
// }}
// ></div>
// </div>
// );
// }
// export default Home;
拆特鸡皮替告诉我是两个问题导致的,一是我没有传数据给renderPieView(先获取数据再分组),二是DOM没有准备好(所以要延时):
<SettingOutlined
key="setting"
onClick={async () => {
setvisible(true);
// 延迟一点,等 Drawer 动画打开再渲染图表
setTimeout(async () => {
const token = JSON.parse(localStorage.getItem('token'));
const username = token?.username;
try {
const res = await axios.get(
`/news?author=${username}&publishState=2&_expand=category`
);
const groupedData = _.groupBy(res.data, (item) => item.category.title);
const pieData = Object.keys(groupedData).map((key) => ({
name: key,
value: groupedData[key].length,
}));
renderPieView(pieData);
} catch (err) {
console.error('Failed to fetch pie data', err);
}
}, 300); // 等 Drawer 动画展开后再绘制图表
}}
/>
点击图标时触发这个异步函数(async
),它将打开抽屉并加载图表数据。打开 Drawer
,这是控制抽屉显示的 visible
状态。
Drawer 打开时有动画,如果立即渲染图表,容器尺寸可能为 0,Echarts 会渲染失败或图表不显示。
用 setTimeout(..., 300)
延迟 300ms 再渲染图表,确保 Drawer 打开完成、DOM 有尺寸
从本地存储获取当前用户 token 并解析出用户名,用于之后的请求过滤(只看当前用户发布的新闻)
使用 lodash 的 groupBy
对新闻按分类标题进行分组,便于做饼图
修改 renderPieView
函数接收参数为 data
(数组形式)
const renderPieView = (data) => {
if (!pieRef.current) return;
const myChart = Echarts.init(pieRef.current);
const option = {
title: {
text: '个人新闻分类',
subtext: '按分类统计',
left: 'center',
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '新闻数量',
type: 'pie',
radius: '50%',
data: data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
};
myChart.setOption(option);
const resizeHandler = () => {
myChart.resize();
};
window.addEventListener('resize', resizeHandler);
return () => {
window.removeEventListener('resize', resizeHandler);
myChart.dispose();
};
};
完整代码:
import React, { useEffect, useRef, useState } from 'react';
import { Card, Col, Row, List, Drawer } from 'antd';
import {
EditOutlined,
EllipsisOutlined,
SettingOutlined,
} from '@ant-design/icons';
import { Avatar } from 'antd';
import axios from 'axios';
import * as Echarts from 'echarts';
import _ from 'lodash';
const { Meta } = Card;
function Home() {
const [viewList, setviewList] = useState([]);
const [starList, setstarList] = useState([]);
const [visible, setvisible] = useState(false);
const barRef = useRef(null);
const pieRef = useRef(null);
useEffect(() => {
axios
.get(
'/news?publishState=2&_expand=category&_sort=view&_order=desc&_limit=6'
)
.then((res) => {
setviewList(res.data);
})
.catch((err) => {
console.error('Request failed', err);
});
}, []);
useEffect(() => {
axios
.get(
'/news?publishState=2&_expand=category&_sort=star&_order=desc&_limit=6'
)
.then((res) => {
setstarList(res.data);
})
.catch((err) => {
console.error('Request failed', err);
});
}, []);
useEffect(() => {
let barCleanup;
let barResizeHandler;
const fetchDataAndRender = async () => {
try {
const res = await axios.get('/news?publishState=2&_expand=category');
if (barRef.current) {
barCleanup = renderBarView(
_.groupBy(res.data, (item) => item.category.title)
);
barResizeHandler = () => {
if (barCleanup) {
const myChart = Echarts.getInstanceByDom(barRef.current);
if (myChart) {
myChart.resize();
}
}
};
window.addEventListener('resize', barResizeHandler);
}
} catch (err) {
console.error('Request failed', err);
}
};
fetchDataAndRender();
return () => {
if (typeof barCleanup === 'function') {
barCleanup();
}
if (barResizeHandler) {
window.removeEventListener('resize', barResizeHandler);
}
};
}, []);
const renderBarView = (obj) => {
if (!barRef.current) return;
var myChart = Echarts.init(barRef.current);
var option = {
title: {
text: '新闻分类图示',
},
tooltip: {},
legend: {
data: ['数量'],
},
xAxis: {
data: Object.keys(obj),
axisLabel: {
rotate: 60,
interval: 0,
},
},
yAxis: {
minInterval: 1,
},
series: [
{
name: '数量',
type: 'bar',
data: Object.values(obj).map((item) => item.length),
},
],
};
myChart.setOption(option);
return () => {
myChart.dispose();
};
};
const renderPieView = (data) => {
if (!pieRef.current) return;
const myChart = Echarts.init(pieRef.current);
const option = {
title: {
text: '个人新闻分类',
subtext: '按分类统计',
left: 'center',
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
name: '新闻数量',
type: 'pie',
radius: '50%',
data: data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
};
myChart.setOption(option);
const resizeHandler = () => {
myChart.resize();
};
window.addEventListener('resize', resizeHandler);
return () => {
window.removeEventListener('resize', resizeHandler);
myChart.dispose();
};
};
const tokenData = localStorage.getItem('token');
const {
username = '',
region = '',
role: { roleName = '' } = {},
} = tokenData ? JSON.parse(tokenData) : {};
return (
<div>
<Row gutter={16}>
<Col span={8}>
<Card title="用户最常浏览" variant="border">
<List
size="small"
dataSource={viewList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card title="用户点赞最多" variant="border">
<List
size="small"
dataSource={starList}
renderItem={(item) => (
<List.Item>
<a href={`#/news-manage/preview/${item.id}`}>{item.title}</a>
</List.Item>
)}
/>
</Card>
</Col>
<Col span={8}>
<Card
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined
key="setting"
onClick={async () => {
setvisible(true);
setTimeout(async () => {
const token = JSON.parse(localStorage.getItem('token'));
const username = token?.username;
try {
const res = await axios.get(
`/news?author=${username}&publishState=2&_expand=category`
);
const groupedData = _.groupBy(
res.data,
(item) => item.category.title
);
const pieData = Object.keys(groupedData).map((key) => ({
name: key,
value: groupedData[key].length,
}));
renderPieView(pieData);
} catch (err) {
console.error('Failed to fetch pie data', err);
}
}, 300);
}}
/>,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=8" />
}
title={username}
description={
<div>
<b>{region ? region : '全球'}</b>
<span
style={{
paddingLeft: '30px',
}}
>
{roleName}
</span>
</div>
}
/>
</Card>
</Col>
</Row>
<Drawer
width="500px"
title="个人新闻分类"
placement="right"
closable={true}
onClose={() => {
setvisible(false);
}}
visible={visible}
>
<div
ref={pieRef}
style={{
width: '100%',
height: '400px',
marginTop: '30px',
}}
></div>
</Drawer>
<div
ref={barRef}
style={{
width: '100%',
height: '400px',
marginTop: '30px',
}}
></div>
</div>
);
}
export default Home;