从0开始的中后台管理系统-4(用图表工具展示线性,饼型图表,以及其他页面的动态展示)

上篇文章我们写完了welcome页面。现在新增两个dashboard和用户列表展示页面。

​编辑

1.dashboard页面

​编辑

首先作为layout子路由展示。我们切换到路径看效果图。

​编辑

​编辑

​编辑

首先图标使用我们要先安装工具npm install echarts然后引入 import * as echarts from 'echarts'然后就可以使用了。

使用步骤首先给包裹容器设置id或者ref拿到DOM节点,然后用节点去创建instace实例对象,然后进行配置项就可以了。我们直接上代码。这里有四个图表,首先获取实例以及DOM对象都是一致的,所以我们用自定义钩子设置。

​编辑

javascript 复制代码
import  * as echarts from 'echarts'
import { useEffect, useRef, useState } from 'react'
export const useCharts=():[
   React.RefObject<HTMLDivElement|null>,
   echarts.ECharts | undefined
]=>{
  const chartRef = useRef<HTMLDivElement>(null)
  const [chartInstance,setChartInstance] = useState<echarts.EChartsType>()
  useEffect(()=>{
   const chart = echarts.init(chartRef.current as HTMLElement)
    setChartInstance(chart)
  },[])
  return [chartRef,chartInstance]
}
/*
  封装useChart 解决了 需要用document.getElementById()获取dom元素
  然后 const instance = new echarts.init(dom节点 as HTMLELement) 获取实例
  省了这两步
*/

这里我们设置的钩子调用可以拿到DOM容器以及利用容器创建实例。

typescript 复制代码
import request from '@/utils/request'
import type { DashBoard, Login ,User} from '@/types/api'
//默认导出 需要用api引入 然后 api.login使用
export default {
  login(params: Login.params) {
    return request.post<Login.LoginResponse>('/user/login', params, {
      showLoading: false
    })
  },
  getUserInfo(){
    return request.get<User.UserItem>('/users/getUserInfo')
  },
  //获取工作台数据
  getReportData(){
    return request.get<DashBoard.ReportData>('/order/dashboard/getReportData')
  },
  //获取折线图数据
  getLineData(){
    return request.get<DashBoard.LineData>('/order/dashboard/getLineData')
  },
  //获取城市分布数据
   getPieCityData(){
    return request.get<DashBoard.PieData[]>('/order/dashboard/getPieCityData')
  },
  //年龄分布数据
   getPieAgeData(){
    return request.get<DashBoard.PieData[]>('/order/dashboard/getPieAgeData')
  },
  //雷达图
   getRadarData(){
    return request.get<DashBoard.RadarData>('/order/dashboard/getRadarData')
  }
}

这是我们图表获取数据调用的接口。我们可以写代码逻辑展示了。

javascript 复制代码
import React, { useEffect, useState } from 'react'
import styles from './index.module.less'
import { Button, Card, Descriptions } from 'antd'
import store, { useStore } from '@/store'
import { formatMoney, formatState } from '@/utils'
import api from '@/api/index'
import type { DashBoard } from '@/types/api'
import { useCharts } from '@/hook/useCharts'
/*
  创建好图表要用的容器div 节点设置id属性
  用线性图表首先安装 npm install echarts
*/
export default function DashBoard() {
  const userInfo = useStore(state => state.userInfo)
  const [report, setReport] = useState<DashBoard.ReportData>()
  //初始化折线图
  const [lineRef, lineChart] = useCharts()
  //饼图
  const [pieRef1, pieChart1] = useCharts()
  const [pieRef2, pieChart2] = useCharts()
  //雷达图
  const [radarRef, radarChart] = useCharts()
  useEffect(() => {
    //获取线图实例
    //配置线图
    renderLineChart()
    //获取饼图实例1
    renderPieChart1()
    //获取饼图实例2
    renderPieChart2()
    //获取雷达图
    renderRadarChart()
  }, [lineChart, pieChart1, pieChart2, radarChart])
  //加载折线图数据
  const renderLineChart = async () => {
    if (!lineChart) return
    const data = await api.getLineData()
    lineChart?.setOption({
      // title: {
      //   text: '订单和流水走势图'
      // },
      tooltip: {
        trigger: 'axis'
      },
      legend: {
        data: ['订单', '流水'],
        top: 30
      },
      grid: {
        left: 50,
        right: 50,
        bottom: 20
      },
      xAxis: {
        data: data.label
      },
      yAxis: {
        type: 'value' //数据轴
      },
      series: [
        {
          name: '订单',
          type: 'line',
          data: data.order
        },
        {
          name: '流水',
          type: 'line',
          data: data.money
        }
      ]
    })
  }
  //加载饼图数据1
  const renderPieChart1 = async () => {
    if (!pieChart1) return
    const data = await api.getPieCityData()
    pieChart1?.setOption({
      title: {
        text: '司机城市分布',
        left: 'center'
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical', //垂直方向剧中
        left: 'left' //左
      },
      series: [
        {
          name: '城市分布',
          type: 'pie',
          radius: '50%',
          data: data
        }
      ]
    })
  }
  //加载饼图数据2
  const renderPieChart2 = async () => {
    if (!pieChart2) return
    const data = await api.getPieAgeData()
    pieChart2?.setOption({
      title: {
        text: '司机年龄分布',
        left: 'center',
        top: 0
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical', //垂直方向剧中
        left: 'left' //左
      },
      series: [
        {
          name: '年龄分布',
          type: 'pie',
          radius: [50, 180],
          roseType: 'radious',
          data: data
        }
      ]
    })
  }
  //加载雷达图
  const renderRadarChart = async () => {
    if (!radarChart) return
    const data = await api.getRadarData()
    radarChart?.setOption({
      // title: {
      //   text: '司机模型诊断',
      //   left: 'center'
      // },
      legend: {
        data: ['司机模型诊断']
      },
      radar: {
        indicator: data.indicator
      },
      series: [
        {
          name: '司机模型诊断',
          type: 'radar',
          data: data.data
        }
      ]
    })
  }
  useEffect(() => {
    getReportData()
  }, [])
  const getReportData = async () => {
    const data = await api.getReportData()
    //console.log('工作台数据data ', data)
    setReport(data)
  }
  //饼图刷新
  const handleRefresh = () => {
    ;(renderPieChart1(), renderPieChart2())
  }
  return (
    <div className={styles.dashboard}>
      <div className={styles.userInfo}>
        <img
          src='https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
          alt=''
          className={styles.userImg}
        />
        <Descriptions title='欢迎新同学,每天都要起飞'>
          <Descriptions.Item label='用户ID'>
            {userInfo.userId}
          </Descriptions.Item>
          <Descriptions.Item label='邮箱'>
            {userInfo.userEmail}
          </Descriptions.Item>
          <Descriptions.Item label='状态'>
            {formatState(userInfo.state)}
          </Descriptions.Item>
          <Descriptions.Item label='手机号'>
            {userInfo.mobile}
          </Descriptions.Item>
          <Descriptions.Item label='岗位'>
            {store.userInfo.job}
          </Descriptions.Item>
        </Descriptions>
      </div>
      <div className={styles.report}>
        <div className={styles.card}>
          <div className={styles.title}>司机数量</div>
          <div className={styles.data}>{report?.driverCount}个</div>
        </div>
        <div className={styles.card}>
          <div className={styles.title}>总流水</div>
          <div className={styles.data}>{formatMoney(report?.totalmoney)}</div>
        </div>
        <div className={styles.card}>
          <div className={styles.tltle}>总订单</div>
          <div className={styles.data}>{report?.orderCount}单</div>
        </div>
        <div className={styles.card}>
          <div className={styles.title}>开通城市</div>
          <div className={styles.data}>{report?.cityNum}</div>
        </div>
      </div>
      <div className={styles.chart}>
        <Card
          title='订单流水走势图'
          extra={
            <Button type='primary' onClick={renderLineChart}>
              刷新
            </Button>
          }
        >
          <div ref={lineRef} className={styles.itemChart}></div>
        </Card>
      </div>
      <div className={styles.chart}>
        <Card
          title='司机分配'
          extra={
            <Button type='primary' onClick={handleRefresh}>
              刷新
            </Button>
          }
        >
          <div className={styles.pieChart}>
            <div ref={pieRef1} className={styles.itemChart}></div>
            <div ref={pieRef2} className={styles.itemChart}></div>
          </div>
        </Card>
      </div>
      <div className={styles.chart}>
        <Card
          title='模型诊断'
          extra={
            <Button type='primary' onClick={renderRadarChart}>
              刷新
            </Button>
          }
        >
          <div ref={radarRef} className={styles.itemChart}></div>
        </Card>
      </div>
    </div>
  )
}

这里配置项都是最基础的用的最多的,不同种类的图表都需要这些,只是series需要去导入不同的数据。

我们还封装了自定义方法去state转化以及金额转化。

typescript 复制代码
/*
  工具函数封装:格式化金额显示为人民币格式
*/

/**
 * 将数字格式化为中文人民币格式(例如:123456.78 => "¥123,456.78")
 * @param num - 要格式化的金额,可以是数字或字符串
 * @returns 格式化后的字符串,带有人民币符号(¥)
 */
export const formatMoney = (num?: number | string) => {
  if(!num) return 0
  // 先将 num 转为浮点数,确保是有效的数字
  const a = parseFloat(num.toString())

  // 使用 toLocaleString 进行本地化格式化,指定地区为中国,货币为人民币(CNY)
  return a.toLocaleString('zh-CN', {
    style: "currency",  // 格式化为货币类型
    currency: "CNY"     // 指定货币为人民币
  })
}
//格式化日期
export const toLocalDate=(date?:Date,rule?:string)=>{
  let curdate = new Date()
  if(date){
     curdate = date
  }
  if(rule === '1'){
    return  curdate.toLocaleDateString()
  }
  if(rule === 'HH::mm:sss') return curdate.toLocaleTimeString()
  return curdate.toLocaleString()
}
//用户状态转化
export const formatState=(state:number)=>{
  if(state===1)return '在职'
  if(state===2)return '试用期'
  if(state===3)return '离职'
}

2.userlist页面

​编辑

我们是通过去设置到了子路由展示的位置,所以我们只需要添加子路由就可以了。到时候通过点击导航栏切换就行了。

直接展示组件代码。只是实现了静态布局。

typescript 复制代码
import React from 'react'
import styles from './index.module.less'
import type { User } from '@/types/api'
import type { TableColumnsType, TableProps } from 'antd'
import { Button, Table, Form, Input, Select, Space } from 'antd'
export default function UserList() {
  const dataSource = [
    {
      create: 0,
      mobile: '',
      job: '',
      deptId: '',
      deptName: '',
      role: 0,
      roleLise: '',
      state: 0,
      userEmail: '',
      userId: 0,
      userImg: '',
      userName: '',
      _id: ''
    }
  ]
  const columns: TableColumnsType<User.UserItem> = [
    {
      title: '用户ID',
      dataIndex: 'userId',
      key: 'userId'
    },
    {
      title: '用户名称',
      dataIndex: 'userName',
      key: 'userName'
    },
    {
      title: '用户邮箱',
      dataIndex: 'userEmail',
      key: 'userEmail'
    },
    {
      title: '用户角色',
      dataIndex: 'role',
      key: 'role'
    },
    {
      title: '用户状态',
      dataIndex: 'state',
      key: 'state'
    },
    {
      title: '注册时间',
      dataIndex: 'createTime',
      key: 'createTime'
    },
    {
      title: '操作',
      dataIndex: 'address',
      key: 'address',
      render(record, values) {
        return (
          <Space>
            <Button type='text'>编辑</Button>
            <Button type='text' danger>
              删除
            </Button>
          </Space>
        )
      }
    }
  ]
  return (
    <div className='user-list'>
      <Form
        className='search-form'
        layout='inline'
        initialValues={{ state: 0 }}
      >
        <Form.Item name='userId' label='用户ID'>
          <Input placeholder='请输入用户ID' />
        </Form.Item>
        <Form.Item name='userName' label='用户名称'>
          <Input placeholder='请输入用户名称' />
        </Form.Item>
        <Form.Item name='state' label='状态'>
          <Select style={{ width: 120 }}>
            <Select.Option value={0}>所有</Select.Option>
            <Select.Option value={1}>在职</Select.Option>
            <Select.Option value={2}>试用</Select.Option>
            <Select.Option value={3}>离职</Select.Option>
          </Select>
        </Form.Item>
        <Form.Item name='state' label='状态'>
          <Space>
            <Button type='primary' className='mr10'>
              搜索
            </Button>
            <Button type='default'>重置</Button>
          </Space>
        </Form.Item>
      </Form>
      <div className='base-table'>
        <div className='header-wrapper'>
          <div className='title'>用户列表</div>
          <div className='action'>
            <Button type='primary'>新增</Button>
            <Button type='primary' danger>
              批量删除
            </Button>
          </div>
        </div>
        <Table dataSource={dataSource} columns={columns} />;
      </div>
    </div>
  )
}

相关推荐
PineappleCode19 分钟前
用 “私房钱” 类比闭包:为啥它能访问外部变量?
前端·面试·js
该用户已不存在25 分钟前
人人都爱的开发工具,但不一定合适自己
前端·后端
ZzMemory36 分钟前
JavaScript 类数组:披着数组外衣的 “伪装者”?
前端·javascript·面试
梁萌1 小时前
前端UI组件库
前端·ui
鲸渔1 小时前
CSS高频属性速查指南
前端·css·css3
小高0071 小时前
🌐AST(抽象语法树):前端开发的“代码编译器”
前端·javascript·面试
蓝易云1 小时前
Git stash命令的详细使用说明及案例分析。
前端·git·后端
GIS瞧葩菜1 小时前
Cesium 中拾取 3DTiles 交点坐标
前端·javascript·cesium
Allen Bright1 小时前
【JS-7-ajax】AJAX技术:现代Web开发的异步通信核心
前端·javascript·ajax
轻语呢喃1 小时前
Mock : 没有后端也能玩的虚拟数据
前端·javascript·react.js