首页数据展示

排版

现在做首页的排版,依旧是偷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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值