首页 > 其他分享 >学生成绩预测与分析可视化平台

学生成绩预测与分析可视化平台

时间:2024-07-17 19:27:54浏览次数:12  
标签:const 平台 value item score 可视化 push let 成绩

一.创作思路

在平时办公中,我们往往需要对数据进行各种数据分析与图形可视化成图表,这些操作我们可以采用wps,word等等办公软件,于是我想自己尝试写一个线上的平台,专门实现上传文件,勾选相应的数据,采用Echarts生成图表,将Echars图表生成图表的全部步骤让用户自行选择生成图表,用户可以将生成的图表保存下来进行使用。目前开发中主要分析与预测学生数据为主,同时搭配上自主选择生成图表的工作台模块,以便后续的升级。

二.技术思路

1.技术路线:使用Vue3+pinia+router+Echarts+datav

2.功能模块:

1、 数据管理模块: 用于数据的收集、存储和管理,其中包括学生成绩数据、个人信息 等。 2、 成绩可视化模块: 能够根据用户需求,将成绩数据以柱状图、折线图、饼状图等形 式进行可视化展示。 3、 个性化分析模块: 根据用户的选择和参数设定,对成绩数据进行个性化分析。 4、 用户界面: 提供给用户的操作界面,包括数据输入、参数设置、图表展示等功能。

三.界面展示

1、登录注册页面:用户通过邮箱号和密码登录进入学生成绩可视化平台

1.1.实现思路:背景的动态粒子效果采用tsparticles,大家如果感兴趣的话,可以点击连接去官网查看。邮箱登录以及验证码方面,我主要是采用node里面的一个邮箱模块nodemailer,采用mysql存储用户信息,其实我感觉用mongdb可能更方便一些,但是当时没考虑这么多。

2.数据分析界面:用户可以在操作台勾选数据,选择自己想要生成的图标,定制化设计图表,提供各种可视化图表,如折线图、柱状图,饼状图等。

2.1.生成饼状图:用户可以点击鼠标左键,在平台上勾选相应的数据进行分析,用户可以自己定义图表的样式,布局,实时更改。然后图片中展示数据是采用mockjs随机生成。

2.2.勾选数据实现原理:使用xlsx模块将用户上传的文件数据解析出来存储在pinia,使用表格渲染到界面给每一个格子添加一个点击,移入,移出事件,方便用户勾选,主要数据代码如下

<template>
    <!-- 表格模板,使用 v-for 指令遍历 datatables.tables 中的数据 -->
    <table class="csv-table" ref="tablesbox">
        <tr v-for="(row, index) in datatables.tables" :key="index">
            <!-- 为每个单元格绑定点击和鼠标悬停事件,传递行列信息 -->
            <td v-for="(value, key) in row" :key="key" @click="draw" @mouseover="xuanran" :data-coloum="index"
                :data-row="key" :data-postion="index * 10 + key">{{ value }}</td>
        </tr>
    </table>
</template>

<script setup>
import { ref, onMounted, watch, toRefs } from "vue";
import { useCounterStore } from "@/stores/counter"

// 使用 Pinia store
const datatables = useCounterStore()

// 将 store 中的属性转为 refs
const { Mouseselected, tables } = toRefs(datatables)

// 定义 ref 变量
let isMouseDown = false;
const tablesbox = ref()
let num = []

// 在组件挂载时添加事件监听器
onMounted(() => {
    document.addEventListener('mousedown', () => {
        isMouseDown = true;
        tablesbox.value.classList.add('cursor')
    });
    document.addEventListener('mouseup', () => {
        isMouseDown = false;
        tablesbox.value.classList.remove('cursor')
    });
})

// 定义起点和父节点坐标
let start = { x: 0, y: 0 }
let parent = { x: 0, y: 0 }

// 绘制选中的单元格
const draw = (e) => {
    guiling()  // 清除所有单元格的样式
    e.target.style.backgroundColor = 'rgb(180, 178, 178,0.7)';
    e.target.style.border = '2px dotted rgba(205, 208, 20)';
    start.y = e.target.getAttribute('data-coloum')
    start.x = e.target.getAttribute('data-row')
    datatables.dataprencet = [e.target.innerHTML]
}

// 渲染鼠标悬停时的效果
const xuanran = (e) => {
    if (isMouseDown) {
        guiling()  // 清除所有单元格的样式
        parent.y = e.target.getAttribute('data-coloum')
        parent.x = e.target.getAttribute('data-row')
        num = getSurroundedCoordinates(start.x, start.y, parent.x, parent.y)
        for (let index = 0; index < num.length; index++) {
            let data = document.querySelector(`[data-postion="${num[index][1]}${num[index][0]}"]`)
            data.style.backgroundColor = 'rgb(180, 178, 178,0.2)';
            data.style.border = '2px dotted rgba(0, 0, 0)';
        }
        datatables.dataprencet = caiji(num)
    }
}

// 清空全部样式
function guiling() {
    const childNodes = document.querySelectorAll('td')
    childNodes.forEach(element => {
        element.style = 'border: 2px solid #dddddd;width: 50px;text-align: center;'
    });
}

// 获取被矩形包围的所有坐标
function getSurroundedCoordinates(x1, y1, x2, y2) {
    const grid = Array.from({ length: datatables.tables.length }, () => Array.from({ length: datatables.tables[0].length }, () => 0));
    grid[x1][y1] = 1;
    grid[x2][y2] = 1;

    const minX = Math.min(x1, x2);
    const maxX = Math.max(x1, x2);
    const minY = Math.min(y1, y2);
    const maxY = Math.max(y1, y2);

    const surroundedCoordinates = [];
    for (let i = minX; i <= maxX; i++) {
        for (let j = minY; j <= maxY; j++) {
            surroundedCoordinates.push([i, j]);
        }
    }
    return surroundedCoordinates;
}

// 采集选中区域的数据
function caiji(params) {
    let numdata = []
    for (let index = 0; index < params.length; index++) {
        let data = document.querySelector(`[data-postion="${params[index][1]}${params[index][0]}"]`)
        numdata.push(data.innerHTML)
    }
    num = []
    console.log(numdata);
    return numdata
}

// 监听 Mouseselected 的变化,清空样式并重置 dataprencet
watch(Mouseselected, (newValue) => {
    guiling()
    datatables.dataprencet = []
}, { deep: true });

// 监听 tables 的变化,更新 tables 的值
watch(tables, (newValue) => {
    tables.value = newValue
}, { deep: true });

</script>

<style scoped>
.csv-table {
    border-collapse: collapse;
    width: 100%;
    user-select: none;
    color: #ffffff;
}

.csv-table td {
    border: 2px solid #90b8ce;
    width: 50px;
    text-align: center;
    text-wrap: wrap;
}


.csv-table th {
    background-color: #f2f2f2;
}

.cursor {
    cursor: crosshair;
}

</style>

2.3生成图表的原理: 根据勾选的数据生成图表,灵感来自于Echars官方网站有在线运行生成图表的一个功能,以我这个饼状图举例,连接如下:点击跳转官方给我们提供的代码样式如下:

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)'
        }
      }
    }
  ]
};

可以看出来是一个对象,那么我们只需将对象里面每一个属性对应的值,使用v-model绑定或者使用watch监听,实现更新,在生成图表时,将对象传入进行更新即可。折线图与饼状图原理与其相同,这里不做过多介绍,感兴趣的朋友可以下载文件亲自上手使用。

2.4.  mockjs随机生成数据功能部分代码如下:

// npm install
import Mock from 'mockjs'

const dataTemplate = {
    'list|100': [
        ['@natural(1, 10)', '@id', '@cname', '@integer(0, 100)', '@integer(0, 100)', '@integer(0, 100)', '@integer(0, 100)', '@integer(0, 100)', '@integer(0, 100)']
    ]
};

// 生成模拟数据
const mockData = Mock.mock(dataTemplate).list;

// 添加表头
const we = [
    ['班级', '学号', '姓名', '语文', '数学', '英语', '物理', '化学', '生物'],
    ...mockData
];



// 输出模拟数据

export default we

3.上传与下载模板:这里的模板主要是为学生成绩可视化分析做准备,这里上传与下载使用了el-upload也就是element plus官方的组件库,下载模板主要是使用XLSX库.

3.1. 上传与下载模板文件的代码展示:

// 文件上传前的处理函数
const beforeUpload = (file) => {
    if (!file) return false;
    const fileName = file.name;
    const fileExtension = fileName.split('.').pop().toLowerCase();
    if (fileExtension !== 'csv' && fileExtension !== 'xlsx') {
        alert('只能上传 CSV 文件和 SQL 文件');
        return;
    }
    const reader = new FileReader();
    reader.onload = (e) => {
        const data = new Uint8Array(e.target.result);
        const workbook = XLSX.read(data, { type: 'array' });
        const sheet = workbook.Sheets[workbook.SheetNames[0]];
        const csvDataArray = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: '' }); // 添加 defval: '' 选项来保留空值
        csvData.value = csvDataArray;
        let a = JSON.stringify(csvData.value)
        student.value = JSON.parse(a)
        datawe.jiazai()
        router.push("/Maininterface/PersonalCenter/2")
    };
    reader.readAsArrayBuffer(file);

    // 返回 false 阻止默认上传行为
    return false;
};

// 准备 CSV 数据
const csvDatas = ref('班级,学号,姓名,语文,英语,数学,生物,化学,物理');
// 下载文件名
const filename = ref('可视化模板.csv');

// 下载 CSV 文件的函数
const downloadCSV = () => {
    // 创建 CSV 数据的 Blob 对象
    const blob = new Blob([csvDatas.value], { type: 'text/csv;charset=utf-8;' });
    // 创建下载链接
    const downloadLink = document.createElement('a');
    const url = URL.createObjectURL(blob);
    // 设置下载链接的属性
    downloadLink.setAttribute('href', url);
    downloadLink.setAttribute('download', filename.value);

    // 添加下载链接到文档中
    document.body.appendChild(downloadLink);

    // 模拟用户点击下载链接来触发下载
    downloadLink.click();

    // 清理创建的对象和链接
    URL.revokeObjectURL(url);
    document.body.removeChild(downloadLink);
};

4.学生成绩的可视化分析展示:

4.1. 在用户上传了模板文件后,我们使用遍历的方式将数据提取出来,由于我们模板文件前列的标题固定为 班级,学号,姓名  先使用XLSX库将数据提取为二维数组,让对学生数据进行求和,取平均值,排名,将对应的数据更新完毕后,传入对应的图表组件中,更新,代码如下。

import { ref } from 'vue'
import { defineStore } from 'pinia'
import { echarsdata } from '@/api/user.js'
export const usenamePoechars = defineStore('Poechars', () => {
   //存储学生信息
   const student = ref([])
   //存储当前选中查看的学生的基本信息
   const studentchoose = ref([])
   //存储学生成绩的排名
   const studentpaiming = ref([])
   //存储表头列
   const header = ref([])
   //存储排名信息(只有姓名与总分)
   const studenpaiming = ref([])
   //存储排名信息(只有学号与总分)
   const studenpaiming2 = ref([])
   //成绩平均值
   const pingjun = ref([])
   //获取用户上传的表单数据
   async function jiazai() {
      //模拟用户上传的数据
      // student.value = await echarsdata();
      // console.log(student.value);
      // const arrayToCSV = (arr) => {
      //    return arr.map(row => row.join(',')).join('\n');
      // };

      // // 准备 CSV 数据
      // const csvData = arrayToCSV(student.value);

      // // 下载文件名
      // const filename = 'student_data.csv';

      // // 下载 CSV 文件的函数
      // const downloadCSV = () => {
      //    // 创建 CSV 数据的 Blob 对象
      //    const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
      //    // 创建下载链接
      //    const downloadLink = document.createElement('a');
      //    const url = URL.createObjectURL(blob);
      //    // 设置下载链接的属性
      //    downloadLink.setAttribute('href', url);
      //    downloadLink.setAttribute('download', filename);

      //    // 添加下载链接到文档中
      //    document.body.appendChild(downloadLink);

      //    // 模拟用户点击下载链接来触发下载
      //    downloadLink.click();

      //    // 清理创建的对象和链接
      //    URL.revokeObjectURL(url);
      //    document.body.removeChild(downloadLink);
      // };

      // // 调用下载函数
      // downloadCSV();
      header.value = student.value[0]
      //存储学生数据信息
      const studentnum = student.value.slice(1)
      for (let index = 3; index < studentnum[0].length; index++) {
         pingjun.value.push(parseInt(studentnum.reduce((totil, per) => totil + per[index], 0) / studentnum.length))
      }
      studentchoose.value = student.value[1]
      studentnum.forEach(item => {
         studenpaiming.value.push({ name: item[2], value: item.slice(3).reduce((accumulator, currentValue) => accumulator + currentValue, 0) })
         studenpaiming2.value.push({ id: item[1], value: item.slice(3).reduce((accumulator, currentValue) => accumulator + currentValue, 0) })
      })
      studenpaiming.value.sort((a, b) => b.value - a.value);
      studenpaiming2.value.sort((a, b) => b.value - a.value);
   }
   //判断用户是否上传了标准文件

   //存储用户上传的需要预测的数据
   const updatawenjian = ref([])
   //记录需要预测的数据数量
   const numwenjian = ref([])

   //存储当前需要预测的学生的总分
   const numscore = ref([])
   //存储预测文件的开头标题
   let headeryuecei = ref([])
   const updatafangfa = (value) => {
      updatawenjian.value.push(value.splice(1))
      headeryuecei.value = value
      console.log(headeryuecei.value);
      numwenjian.value.push(numwenjian.value.length + 1)
   }
   //存储文件的预测名
   const wenjianname = ref([])
   function appname(value) {
      wenjianname.value.push(value)
   }
   function yuecei(qwedata) {
      //存储数量
      let numty = []
      //存储第一科的分数
      let frist = []
      //存储第2科的分数
      let frist2 = []
      //存储第3科的分数
      let frist3 = []
      //存储第4科的分数
      let frist4 = []
      //存储第5科的分数
      let frist5 = []
      //存储第6科的分数
      let frist6 = []
      //存储总科科分数
      let scores = []
      let num = []
      numscore.value = []
      let studentname = ""
      for (let index = 0; index < updatawenjian.value.length; index++) {
         let number = updatawenjian.value[index].filter(item => item[1] === qwedata)
         num.push(number)
         numty.push(index)
      }
      console.log(num);
      num.forEach(item => {
         studentname = item[0][2]
         let score = item[0].slice(3).reduce((pr, cr) => pr + cr, 0);
         frist.push(item[0].slice(3)[0])
         frist2.push(item[0].slice(3)[1])
         frist3.push(item[0].slice(3)[2])
         frist4.push(item[0].slice(3)[3])
         frist5.push(item[0].slice(3)[4])
         frist6.push(item[0].slice(3)[5])
         scores.push(score)
         numscore.value.push(score)
      })
      console.log(frist2);
      numty.push(numty.length + 1)
      // 计算总分平均分
      const averageScore = scores.reduce((acc, score) => acc + score, 0) / scores.length;
      //第一科平均分
      const fristScore = frist.reduce((acc, score) => acc + score, 0) / frist.length
      //第二科平均分
      const frist2Score = frist2.reduce((acc, score) => acc + score, 0) / frist2.length
      //第三科平均分
      const frist3Score = frist3.reduce((acc, score) => acc + score, 0) / frist3.length
      //第四科平均分
      const frist4Score = frist4.reduce((acc, score) => acc + score, 0) / frist4.length
      //第五科平均分
      const frist5Score = frist5.reduce((acc, score) => acc + score, 0) / frist5.length
      //第六科平均分
      const frist6Score = frist6.reduce((acc, score) => acc + score, 0) / frist6.length
      scores.push(huigui(numscore.value, numwenjian.value, averageScore))
      frist.push(huigui(frist, numwenjian.value, fristScore))
      frist2.push(huigui(frist2, numwenjian.value, frist2Score))
      frist3.push(huigui(frist3, numwenjian.value, frist3Score))
      frist4.push(huigui(frist4, numwenjian.value, frist4Score))
      frist5.push(huigui(frist5, numwenjian.value, frist5Score))
      frist6.push(huigui(frist6, numwenjian.value, frist6Score))
      return [{ score: scores, value: numty, name: "总分" },
      { score: frist, value: numty, name: headeryuecei.value[0][3] },
      { score: frist2, value: numty, name: headeryuecei.value[0][4] },
      { score: frist3, value: numty, name: headeryuecei.value[0][5] },
      { score: frist4, value: numty, name: headeryuecei.value[0][6] },
      { score: frist5, value: numty, name: headeryuecei.value[0][7] },
      { score: frist6, value: numty, name: headeryuecei.value[0][8] }]
   }
   function huigui(scores, exams, averageScore) {
      // 使用线性回归拟合模型
      function linearRegression(x, y) {
         const n = x.length;
         let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;

         for (let i = 0; i < n; i++) {
            sumX += x[i];
            sumY += y[i];
            sumXY += x[i] * y[i];
            sumX2 += x[i] * x[i];
         }
         //计算斜率和截距
         const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
         const intercept = (sumY - slope * sumX) / n;

         return { slope, intercept };
      }
      // 计算回归方程
      const regressionEquation = linearRegression(exams, scores);
      // 预测下次考试的分数
      const nextExamNumber = exams.length + 1;
      let predictedScore = regressionEquation.slope * nextExamNumber + regressionEquation.intercept;

      // 确保预测到的分数不为负数,如果为负数,统一返回0
      predictedScore = predictedScore < 0 ? 0 : predictedScore;

      return predictedScore;
   }
   return { student, studentchoose, pingjun, jiazai, header, studenpaiming, studenpaiming2, updatawenjian, numwenjian, yuecei, updatafangfa, numscore, appname, wenjianname }
}, {
   persist: true
})

4.2:Echar图表更新步骤,以上图学生个人可视化分析的里面的雷达图举例。

<template>
  <div ref="myEchartss" :style="{ width: '100%', height: '100%' }"></div>
</template>

<script setup>
import { usenamePoechars } from '@/stores/Poechars.js'
import * as echarts from 'echarts';
import { ref, onMounted, watch, toRefs } from 'vue';

const datawe = usenamePoechars();
const { student, pingjun, studentchoose, header } = toRefs(datawe);

let datas = [
  { Name: '语文', TotalScore: 150, Score: 76, AvgScore: 72 },
  { Name: '数学', TotalScore: 150, Score: 71, AvgScore: 63 },
  { Name: '英语', TotalScore: 150, Score: 56, AvgScore: 58 },
  { Name: '物理', TotalScore: 100, Score: 81, AvgScore: 68 },
  { Name: '化学', TotalScore: 100, Score: 77, AvgScore: 65 },
  { Name: '生物', TotalScore: 100, Score: 77, AvgScore: 65 }
];

let colorList = ['#36A87A', '#3f76f2'];
let aveList = datas.map((n) => { return n.AvgScore; });
let uList = datas.map((n) => { return n.Score; });
let nameList = [];

datas.forEach((item) => {
  nameList.push({
    name: item.Name,
    max: 150,
    AvgScore: item.AvgScore,
    Score: item.Score
  });
});

let option = {
  title: {
    text: `综合得分:${datas.reduce((er, per) => er + per.Score, 0)}分`,
    left: 'center',
    textStyle: {
      // 图例文字的样式
      fontSize: 18,
      color: '#fff'
    }
  },
  legend: {
    data: ['你的得分', '平均得分'],
    left: 'center',
    top: 'bottom',
    itemGap: 50,
    textStyle: {
      // 图例文字的样式
      fontSize: 14,
      color: '#fff'
    }
  },
  radar: {
    center: ['50%', '55%'], // 图表位置
    radius: '50%', // 图表大小
    // 设置雷达图中间射线的颜色
    axisLine: {
      lineStyle: {
        color: '#999',
        fontSize: 30
      }
    },
    indicator: nameList,
    // 雷达图背景的颜色,在这儿随便设置了一个颜色,完全不透明度为0,就实现了透明背景
    splitArea: {
      areaStyle: {
        color: '#a0cfff' // 图表背景的颜色
      }
    },
    name: {
      lineHeight: 18,
      formatter: (labelName, raw) => {
        const { AvgScore, Score } = raw;
        return (
          labelName + '\n' + `{score|${Score}}` + '/' + `{avg|${AvgScore}}`
        );
      },
      rich: {
        score: {
          color: colorList[0],
          fontSize: 16
        },
        avg: {
          color: colorList[1],
          fontSize: 16
        }
      }
    }
  },
  series: [
    {
      type: 'radar',
      data: [
        {
          value: uList,
          name: '你的得分',
          // 设置区域边框和区域的颜色
          itemStyle: {
            color: colorList[0]
          },
          label: {
            show: false,
            fontSize: 16,
            position: 'right',
            color: colorList[0],
            formatter: function (params) {
              return params.value;
            }
          },
          areaStyle: {
            color: colorList[0],
            opacity: 0.2
          }
        },
        {
          value: aveList,
          name: '平均得分',
          // 设置区域边框和区域的颜色
          itemStyle: {
            color: colorList[1]
          },
          label: {
            show: false,
            fontSize: 16,
            position: 'left',
            color: colorList[1],
            formatter: function (params) {
              return params.value;
            }
          },
          areaStyle: {
            color: colorList[1],
            opacity: 0.2
          }
        }
      ]
    }
  ]
}

const myEchartss = ref(null);

onMounted(() => {
  initChart(option);
});

function initChart(options) {
  const chartDom = myEchartss.value;
  if (!chartDom) return;

  const chart = echarts.init(chartDom);
  chart.setOption(options);

  window.onresize = () => {
    chart.resize();
  };
}

watch(studentchoose, (newvalue) => {
  datas = []
  for (let index = 3; index < header.value.length; index++) {
    datas.push({ Name: header.value[index], TotalScore: 150, Score: studentchoose.value[index], AvgScore: pingjun.value[index - 3] })
  }
  aveList = datas.map((n) => { return n.AvgScore; });
  uList = datas.map((n) => { return n.Score; });
  nameList = [];
  datas.forEach((item) => {
    nameList.push({
      name: item.Name,
      max: 150,
      AvgScore: item.AvgScore,
      Score: item.Score
    });
  });
  option = {
    title: {
      text: `综合得分:${datas.reduce((er, per) => er + per.Score, 0)}分`,
      left: 'center'
    },
    legend: {
      data: ['你的得分', '平均得分'],
      left: 'center',
      top: 'bottom',
      itemGap: 50,
      textStyle: {
        // 图例文字的样式
        fontSize: 14
      }
    },
    radar: {
      center: ['50%', '55%'], // 图表位置
      radius: '50%', // 图表大小
      // 设置雷达图中间射线的颜色
      axisLine: {
        lineStyle: {
          color: '#999',
          fontSize: 30
        }
      },
      indicator: nameList,
      // 雷达图背景的颜色,在这儿随便设置了一个颜色,完全不透明度为0,就实现了透明背景
      splitArea: {
        areaStyle: {
          color: '#d7cece' // 图表背景的颜色
        }
      },
      name: {
        lineHeight: 18,
        formatter: (labelName, raw) => {
          const { AvgScore, Score } = raw;
          return (
            labelName + '\n' + `{score|${Score}}` + '/' + `{avg|${AvgScore}}`
          );
        },
        rich: {
          score: {
            color: colorList[0],
            fontSize: 16
          },
          avg: {
            color: colorList[1],
            fontSize: 16
          }
        }
      }
    },
    series: [
      {
        type: 'radar',
        data: [
          {
            value: uList,
            name: '你的得分',
            // 设置区域边框和区域的颜色
            itemStyle: {
              color: colorList[0]
            },
            label: {
              show: false,
              fontSize: 16,
              position: 'right',
              color: colorList[0],
              formatter: function (params) {
                return params.value;
              }
            },
            areaStyle: {
              color: colorList[0],
              opacity: 0.2
            }
          },
          {
            value: aveList,
            name: '平均得分',
            // 设置区域边框和区域的颜色
            itemStyle: {
              color: colorList[1]
            },
            label: {
              show: false,
              fontSize: 16,
              position: 'left',
              color: colorList[1],
              formatter: function (params) {
                return params.value;
              }
            },
            areaStyle: {
              color: colorList[1],
              opacity: 0.2
            }
          }
        ]
      }
    ]
  }
  initChart(option);
}, {
  deep: true
})

</script>

<style></style>

及通过watch监听studentchoose的数据变化,来刷新界面。

5.学生成绩预测界面:

将学生每一期的数据上传后(至少上传两个文件)然后基于最基础的公式y=kx+b来进行预测,后续会考虑采用更先进的方式进行综合的预测。

四.总结:

1 .这个项目整体上来说只能算的上勉强实现基础功能,由于是一个人开发,在界面美化,以及功能的完善还有很长的一段路需要走,有更好的想法欢迎各位探讨,也请各位,如果觉得不错请点赞加收藏,谢谢了。

标签:const,平台,value,item,score,可视化,push,let,成绩
From: https://blog.csdn.net/m0_73831440/article/details/140499748

相关文章

  • 大屏可视化看板变形问题和分辨率问题的解决办法
    关于大屏可视化,好看的画面总是千篇一律,但是遇到的问题,可谓是五花八门,就比如画面变形问题,模糊问题。 做一个可视化画面,电脑上显示好好的↓ 实际投出来↓画面拉长了压扁了扭曲变形…… 回到电脑上一顿调,没啥用,还是变形……那种无力感,经历过画面工程师懂的都懂。 ......
  • ssm+vue 社区生鲜电商平台
    本社区生鲜电商平台采用SSM框架和MYSQL数据库技术开发,实现了房用户社区生鲜的科学化管理,大大的提高了管理效率,使得用户社区生鲜相关信息的管理系统化、高效化、科学化。通过对系统的需求分析,设计出了本社区生鲜电商平台,主要的研究内容有:(1)在使用中了解系统的工作流程,撰写关于......
  • 以电商、消费行业为例,详解火山引擎数智平台如何应用湖仓一体架构
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群。 随着互联网的不断发展,企业数据的使用场景也发生巨大变化,湖仓一体逐渐成为一种被广泛应用的底层数据架构。 详细来说,湖仓一体架构是一种将数据湖和数据仓库的优势结合起来的新型数据架......
  • 跨境电商平台自养号测评防关联技巧!
    不管是亚马逊、ebay、Lazada、shopee、速卖通、阿里国际、美客多、敦煌等跨境电商平台都是禁止卖家去补单(测评),为此每年都有一段风控期来大量检测,为什么还有这么多的卖家冒着封号封店铺的风险做这个呢?测评(补单)推广产品的一种手段,一个好的评价对店铺的影响很重要,如果评价里恰好......
  • 亚马逊、Shopee、Lazada等跨境电商平台怎么测评?
    在跨境电商的激烈竞争中,产品的评价是连接潜在买家与卖家的桥梁,其重要性不言而喻,它不仅直接影响平台对卖家店铺的评估,还深刻影响着产品页面的曝光率、流量以及最终的销量,所以如何高效、安全地获取高质量的评价,成为了每位跨境电商卖家必须面对的问题测评市场的现状与挑战当前,......
  • 想编辑GIS场景该怎么办?快来试试这款免费可视化工具
    想编辑GIS场景该怎么办?别急,山海鲸可视化这款免费可视化工具能帮你轻松搞定。作为一款免费可视化工具,山海鲸可视化不仅功能强大,而且使用便捷,简直是GIS从业者的福音。 首先,山海鲸可视化的GIS场景编辑功能极为强大。它支持多种常见的GIS文件格式,并且内置了丰富的编辑工具,让你可以......
  • 基于SSM的校园志愿者管理系统小程序+99213(免费领源码)可做计算机毕业设计JAVA、PHP、爬
    小程序+springboot校园志愿者管理系统摘 要随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,校园志愿者管理系统被用户普遍使用,为方便用户能够可以随时进行在线查看校园志愿......
  • 好用的开源移动端低代码平台有哪些
    移动APP、H5、小程序曾风靡一时,结合当前无代码/低代码开发技术,有哪些免费开源的移动端H5/小程序软件,不用写代码即可发布H5页面,笔者对市场上主流的开源H5低代码/无代码工具/框架/组件进行了研究和验证,找到了几款比较好用的移动端H5无代码/低代码设计器,供大家选型参考。1、h5-doori......
  • windows11 使用pytorch transformers运行Qwen2-0.5B-Instruct模型 (基于anaconda pyth
    吾名爱妃,性好静亦好动。好编程,常沉浸于代码之世界,思维纵横,力求逻辑之严密,算法之精妙。亦爱篮球,驰骋球场,尽享挥洒汗水之乐。且喜跑步,尤钟马拉松,长途奔袭,考验耐力与毅力,每有所进,心甚喜之。 吾以为,编程似布阵,算法如谋略,需精心筹谋,方可成就佳作。篮球乃团队之艺,协作共进,方显力......
  • 海外短剧系统(h5+APP)搭建源码部署,海外短剧看剧平台搭建
    一、海外短剧看剧小程序//#ifdefAPP-PLUS constrequest=require('./common/request/index').default constutils=require('./common/utils/index.js').default importcheckappupdatefrom'@/common/utils/checkappupdate.js' importapifr......