二十(GIT3)、echarts(折线图、柱状图、饼图)、黑马就业数据平台(主页图表实现、闭包了解、学生信息渲染)

1. echarts

数据可视化:将数据转换为图形,数据特点更加突出

echarts:一个基于 JavaScript 的开源可视化图表库

echarts官网

1.1 echarts核心使用步骤

javascript 复制代码
// 1. 基于准备好的dom,初始化echarts实例
const myChart = echarts.init(dom元素);

// 2. 指定图表的配置项和数据
const option = {
  // ...
};

// 3. 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);

1.2 折线图

javascript 复制代码
// todo: 2022全学科薪资走势 折线图
// ? 折线粗细、点大小、x轴虚线、提示框(已解决)
function renderYear(year) {
  // 省略大括号 {} 和 return 关键字(适用于单行表达式)
  const months = year.map((item) => item.month);
  const salarys = year.map((item) => item.salary);
  // console.log(months, salarys);

  const myChart = echarts.init(document.querySelector("#line"));
  const option = {
    // 标题组件
    title: {
      text: "2022全学科薪资走势",
      // top、left..: 可设置title 组件离容器x侧的距离。
      top: 20,
      left: "2%",
    },
    // 直角坐标系内绘图网格
    grid: {
      top: "20%",
    },
    // 提示框组件
    tooltip: {
      // axis 坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。
      trigger: "axis",
    },
    // x轴
    xAxis: {
      // 坐标轴类型 value数值轴、category类目轴..
      type: "category",
      data: months,
      // 坐标轴轴线
      axisLine: {
        lineStyle: {
          color: "#9c9c9c",
          // 线的类型:实线solid、虚线、点线dotted
          type: "dashed",
        },
      },
      // x轴在 grid 区域中的分隔线
      /* splitLine: {
        show: true,
        lineStyle: {
          type: "dashed",
        },
      }, */
    },
    // y轴
    yAxis: {
      type: "value",
      // y轴在 grid 区域中的分隔线
      splitLine: {
        lineStyle: {
          // 分隔线的类型
          type: "dashed",
        },
      },
    },
    // 系列列表
    series: [
      {
        data: salarys,
        // 折线/面积图
        type: "line",
        smooth: true,
        // 区域填充样式。设置后显示成区域面积图。
        areaStyle: {
          // 线性渐变,前四个参数分别是 x0, y0, x2, y2, 范围从 0 - 1,相当于在图形包围盒中的百分比,
          // 如果 globalCoord 为 `true`,则该四个值是绝对的像素位置
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#88BDF1", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "rgba(255,255,255,0)", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
        // 线条样式
        lineStyle: {
          width: 7, // 线宽
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 1,
            y2: 0,
            colorStops: [
              {
                offset: 0,
                color: "#4897E7", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "#5975EC", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
        // 标记的大小
        symbolSize: 10,
      },
    ],
  };
  myChart.setOption(option);
}

1.3 双柱状图

javascript 复制代码
// todo:班级每组薪资 双柱状图
function renderGroupData(groupData) {
  // 班级平均薪资默认渲染第一组的 后面点击更换组别 多次使用 → 封装函数
  // console.log(groupData);
  // console.log(groupData instanceof Object); // true
  // console.log(groupData instanceof Array); // false
  // groupData是对象 键是数字(1到8)

  // ! 1. 直接封装函数后调用:这种方法简单直接,每次点击按钮时重新渲染整个图表。
  // ! 虽然实现起来比较方便,但可能会带来性能问题,特别是在数据量较大或图表比较复杂的情况下。

  // ! 2. 更改配置项数据,再次调用 myChart.setOption(option) 这种方法更高效,
  // ! 因为 ECharts 提供了一些 API 来更新图表的数据,而不需要完全重新初始化图表。这通常比重新创建图表要快得多,尤其是在数据量大的情况下。

  // * 对象的键可以是字符串或数字 groupData[1] 等价于 groupData["1"]
  // function renderGroup(group) {
  /* const getData = groupData[1].map((item) => {
    const { name, hope_salary, salary } = item;
    return [name, hope_salary, salary];
  }); */
  // console.log(getData);

  const myChart = echarts.init(document.querySelector("#lines"));
  const option = {
    // legend: {},
    tooltip: {},
    dataset: {
      source: [
        ["薪资", "期望薪资", "就业薪资"],
        ...groupData[1].map((item) => {
          const { name, hope_salary, salary } = item;
          return [name, hope_salary, salary];
        }),
      ],
    },
    xAxis: {
      type: "category",
      axisLine: {
        lineStyle: {
          color: "#9c9c9c",
          type: "dashed",
        },
      },
    },
    yAxis: {
      splitLine: {
        lineStyle: {
          type: "dashed",
        },
      },
    },
    series: [
      {
        type: "bar",
        itemStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#3CD59E", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "rgba(255,255,255,0.5)", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
      },
      {
        type: "bar",
        color: {
          type: "linear",
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            {
              offset: 0,
              color: "#56B1FF", // 0% 处的颜色
            },
            {
              offset: 1,
              color: "rgba(255,255,255,0.5)", // 100% 处的颜色
            },
          ],
          global: false, // 缺省为 false
        },
      },
    ],
    // color: ["#68DEB4", "#77B7F2"],
  };
  myChart.setOption(option);
  // }
  // renderGroup(1);

  document.querySelector("#btns").addEventListener("click", function (e) {
    if (e.target.nodeName === "BUTTON") {
      // console.log("button");
      // console.log(e.target.innerText);
      document.querySelector(".btn-blue").classList.remove("btn-blue");
      e.target.classList.add("btn-blue");
      // renderGroup(e.target.innerText);
      // 更改配置项数据
      option.dataset.source = [
        ["姓名", "期望薪资", "就业薪资"],
        ...groupData[e.target.innerText].map((item) => {
          const { name, hope_salary, salary } = item;
          return [name, hope_salary, salary];
        }),
      ];
      myChart.setOption(option);
    }
  });
}

1.4 饼状图

javascript 复制代码
// todo: 班级薪资分布 饼图
function renderSalaryDataClass(salaryData) {
  // 箭头函数省略return简写时,返回值若为对象{},需使用小括号()包裹
  const data = salaryData.map((item) => ({
    value: item.g_count + item.b_count,
    name: item.label,
  }));
  /* const data = salaryData.map((item) => {
    return { value: item.g_count + item.b_count, name: item.label };
  }); */
  // console.log(data);

  const myChart = echarts.init(document.querySelector("#salary"));
  const option = {
    title: {
      text: "班级薪资分布",
      top: 15,
      left: 15,
    },
    // 提示框
    tooltip: {
      trigger: "item",
    },
    // 图例组件
    legend: {
      bottom: "5%",
      left: "center",
    },
    // 系列列表
    series: [
      {
        name: "班级薪资分布",
        type: "pie",
        // 饼图大小
        radius: ["50%", "70%"],
        avoidLabelOverlap: false,
        // 饼图小扇区的边框设置 圆角颜色宽度等
        itemStyle: {
          borderRadius: 10,
          borderColor: "#fff",
          borderWidth: 2,
        },
        // 饼图标签文字 默认为true 显示
        label: {
          show: false,
          // 标签文字位置
          // position: "center",
        },
        // 饼图标签文字与饼图的连接线
        /* labelLine: {
          show: false,
        }, */
        // 高亮状态的扇区和标签样式 (悬停时所处数据中间高亮)
        /* emphasis: {
          label: {
            show: true,
            fontSize: 40,
            fontWeight: "bold",
          },
        }, */
        data,
      },
    ],
    color: ["#FDA224", "#5097FF", "#3ABCFA", "#34D39A"],
  };
  myChart.setOption(option);
}

1.5 双饼图

javascript 复制代码
// todo: 男女薪资分布 双饼图
function renderSalaryDataGender(salaryData) {
  const myChart = echarts.init(document.querySelector("#gender"));
  const option = {
    title: [
      {
        text: "男女薪资分布",
        left: 10,
        top: 10,
        textStyle: {
          fontsize: 16,
        },
      },
      {
        text: "男生",
        left: "45%",
        top: "45%",
        textStyle: {
          fontsize: 12,
        },
      },
      {
        text: "女生",
        left: "45%",
        top: "88%",
        textStyle: {
          fontsize: 12,
        },
      },
    ],
    tooltip: {
      trigger: "item",
    },
    series: [
      {
        type: "pie",
        radius: ["25%", "40%"],
        //  center 属性来控制每个饼图的位置
        center: ["50%", "30%"],
        avoidLabelOverlap: false,
        label: {
          show: true,
        },
        labelLine: {
          show: true,
        },
        data: salaryData.map((item) => ({
          value: item.b_count,
          name: item.label,
        })),
      },
      {
        type: "pie",
        radius: ["25%", "40%"],
        center: ["50%", "70%"],
        avoidLabelOverlap: false,
        label: {
          show: true,
        },
        labelLine: {
          show: true,
        },
        data: salaryData.map((item) => ({
          value: item.g_count,
          name: item.label,
        })),
      },
    ],
    color: ["#FDA224", "#5097FF", "#3ABCFA", "#34D39A"],
  };
  myChart.setOption(option);
}

2. 黑马就业数据平台

2.1 index.js

初始代码(不完整)

javascript 复制代码
// 调用函数 判断是否有token
checkLogin();

// 渲染用户名
renderName(1);

// 退出登录
logout();

// ? 可以使用 async await 从后台只获取一次数据吗? 图表直接使用
// ? 外面套函数?
// * getRes 是一个异步函数,返回一个 Promise 对象。
// * 调用 getRes 时,如果不使用 await,会得到一个未解决的 Promise 对象。
// * 使用 await 可以等待 Promise 被解决,并将解决后的值赋给变量。
// * await 关键字只能在 async 函数内部使用。不能直接在 return 语句中使用 await
// ! 为了实现每次调用 getRes 函数时只向网络请求一次数据,并且能够根据传入的参数获取解构出来的一条数据,
// ! 可以使用一个闭包来缓存第一次请求的数据。这样,后续的调用将直接从缓存中获取数据,而不需要再次进行网络请求。
// const { groupData, overview, provinceData, salaryData, year } = res.data;
function outer() {
  let cacheData = null;
  return async function inner(name) {
    if (!cacheData) {
      const res = await axios.get("/dashboard");
      cacheData = res.data;
    }
    return cacheData[name];
  };
}
// * getRes === outer() === inner === inner(){}
const getRes = outer();

// todo:统计数据区域
(async function () {
  // const token = localStorage.getItem("token");
  // * common.js 已添加请求(统一设置token)和响应拦截器(统一处理token失效问题) → catch部分代码移除,try catch注释
  // try {
  /* const res = await axios.get("/dashboard", {
      // * 请求头参数:校验是否登录
      headers: { Authorization: token },
    }); */
  // const res = await axios.get("/dashboard");
  // console.log(res);
  // const { groupData, overview, provinceData, salaryData, year } = res.data;
  // const { overview } = res.data;
  const overview = await getRes("overview");
  // console.log(overview);

  // console.log(overview);
  Object.keys(overview).forEach((key) => {
    document.querySelector(`.${key}`).innerHTML = overview[key];
  });
  // } catch (error) {
  // console.dir(error);
  // console.log(error.response.status); // 401

  // 401 token验证失败(token过期或被恶意篡改)
  // todo:跳转到登录页面重新登陆,本地存储数据清除
  /* if (error.response.status === 401) {
      showToast("登陆失败,请重新登录");
      localStorage.removeItem("username");
      localStorage.removeItem("token");
      // 延迟跳转登陆页面
      setTimeout(() => {
        location.href = "./login.html";
      }, 1500);
    } */
  // }
})();

// * 多个图表需写入页面 → 立即执行函数,解决可能存在的变量名冲突问题
// todo: 柱状图 班级每组薪资
(async function () {
  // const res = await axios.get("/dashboard");
  // console.log(res.data);
  // const { groupData } = res.data;
  const groupData = await getRes("groupData");

  const myChart = echarts.init(document.querySelector("#lines"));
  const option = {
    tooltip: {},
    dataset: {
      source: [
        ["薪资", "期望薪资", "就业薪资"],
        ...groupData[1].map((item) => {
          const { name, hope_salary, salary } = item;
          return [name, hope_salary, salary];
        }),
      ],
    },
    xAxis: {
      type: "category",
      axisLine: {
        lineStyle: {
          color: "#9c9c9c",
          type: "dashed",
        },
      },
    },
    yAxis: {
      splitLine: {
        lineStyle: {
          type: "dashed",
        },
      },
    },
    series: [
      {
        type: "bar",
        itemStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#3CD59E", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "rgba(255,255,255,0.5)", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
      },
      {
        type: "bar",
        color: {
          type: "linear",
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            {
              offset: 0,
              color: "#56B1FF", // 0% 处的颜色
            },
            {
              offset: 1,
              color: "rgba(255,255,255,0.5)", // 100% 处的颜色
            },
          ],
          global: false, // 缺省为 false
        },
      },
    ],
  };
  myChart.setOption(option);

  document.querySelector("#btns").addEventListener("click", function (e) {
    if (e.target.nodeName === "BUTTON") {
      document.querySelector(".btn-blue").classList.remove("btn-blue");
      e.target.classList.add("btn-blue");
      // 更改配置项数据
      option.dataset.source = [
        ["姓名", "期望薪资", "就业薪资"],
        ...groupData[e.target.innerText].map((item) => {
          const { name, hope_salary, salary } = item;
          return [name, hope_salary, salary];
        }),
      ];
      myChart.setOption(option);
    }
  });
})();

2.2 getData

向服务器请求数据 解构 调用函数 传参 (后四个函数在本章1.2-1.5)

javascript 复制代码
// todo:统计数据区域
async function getData() {
  const res = await axios.get("/dashboard");
  const { groupData, overview, provinceData, salaryData, year } = res.data;

  // console.log(salaryData);

  renderOverview(overview);
  renderGroupData(groupData);
  renderYear(year);
  renderSalaryDataClass(salaryData);
  renderSalaryDataGender(salaryData);
}

getData();

function renderOverview(overview) {
  Object.keys(overview).forEach((key) => {
    document.querySelector(`.${key}`).innerHTML = overview[key];
  });
}

2.3 student.js

学生信息渲染

javascript 复制代码
// 调用函数 判断是否有token(登录校验)
checkLogin();

// 渲染用户名
renderName(1);

// 退出登录
logout();

// * 渲染学生信息 增删改之后需再次渲染 → 封装函数
// * 获取服务器学生列表 并渲染到页面
async function render() {
  // 已设置请求拦截器
  const res = await axios.get("/students");
  const str = res.data
    .map((item) => {
      const {
        id,
        name,
        age,
        gender,
        group,
        hope_salary,
        salary,
        province,
        city,
        area,
      } = item;
      return `
        <tr>
          <td>${name}</td>
          <td>${age}</td>
          <td>${gender === 0 ? "男" : "女"}</td>
          <td>第${group}组</td>
          <td>${hope_salary}</td>
          <td>${salary}</td>
          <td>${province + city + area}</td>
          <td>
            <a href="javascript:;" class="text-success mr-3"><i class="bi bi-pen" data-id="${id}"></i></a>
            <a href="javascript:;" class="text-danger"><i class="bi bi-trash" data-id="${id}"></i></a>
          </td>
        </tr>
      `;
    })
    .join("");
  document.querySelector(".list").innerHTML = str;
  document.querySelector(".total").innerHTML = res.data.length;
}
render();
相关推荐
落日弥漫的橘_1 小时前
npm run 运行项目报错:Cannot resolve the ‘pnmp‘ package manager
前端·vue.js·npm·node.js
梦里小白龙1 小时前
npm发布流程说明
前端·npm·node.js
No Silver Bullet1 小时前
Vue进阶(贰幺贰)npm run build多环境编译
前端·vue.js·npm
破浪前行·吴2 小时前
【初体验】【学习】Web Component
前端·javascript·css·学习·html
泷羽Sec-pp2 小时前
基于Centos 7系统的安全加固方案
java·服务器·前端
IT 古月方源2 小时前
GRE技术的详细解释
运维·前端·网络·tcp/ip·华为·智能路由器
myepicure8882 小时前
Windows下调试Dify相关组件(1)--前端Web
前端·llm
用户59594399272192 小时前
大牛工程师告诉你:开关电源“Y电容”都是这样计算的!
前端
用户59594399272193 小时前
松下功率继电器HE-A全新登场
前端
JosieBook3 小时前
【ASP.NET学习】Web Pages 最简单的网页编程开发模型
前端·asp.net·菜鸟教程