# 数据可视化155 **Repository Path**: flyird/data-visualization-155 ## Basic Information - **Project Name**: 数据可视化155 - **Description**: 数据可视化项目,黑马顺义前端就业155期专属 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 8 - **Forks**: 3 - **Created**: 2023-04-29 - **Last Updated**: 2023-11-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # echarts使用步骤 - 必须有一个具有宽高的div - 引入echarts.min.js - JS代码 - 初始化echarts(写法固定)`let 变量 = echarts.init(盒子)` - 配置echarts图表(配置项决定了图表的样子) `let 配置项 = {}` - 根据配置项,创建图表(写法固定)`变量.setOption(配置项)` ```html
``` # echarts常用的配置项  title - 标题 xAxis - X轴配置 yAxis - Y轴配置 series - 系列数据 - 格式是 [{ 一个图形 }, { 一个图形 }, { 一个图形 }] color - 颜色配置 - 格式 ['red', 'green', 'blue'] - 格式 ['red', 'green', '渐变色'] ----- [渐变色传送门](https://www.makeapie.cn/doc/echarts/zh/option.html#color) grid - 坐标轴区域的配置,比如配置距离顶部 100 像素 legend - 图例配置 - 需要设置series中每一项的 name 属性才行。 tooltip - 鼠标移入提示 - 默认是鼠标移入图形才能提示 - 将 trigger: 'axis' 之后,鼠标输到轴上即可提示。 ```js let option = { // 标题配置 title: { text: '我的第1个图表', // 主标题文本 left: 'center', // 主标题的位置 textStyle: { color: 'red', // 要求 标题的字体大小是 24px fontSize: 24 }, }, // X轴配置 xAxis: { type: 'category', data: ['星期一', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], axisLabel: { color: 'blue' // 坐标轴文字的颜色 } }, // Y轴配置 yAxis: { type: 'value' }, // 下面是构成图表的必须的配置,叫做系列数据 series: [ // 数组中的每一个小对象,就是一个图形 { // data 构成图表的数据 data: [150, 230, 224, 218, 135, 147, 260], // type 表示图表的类型:(line-折线)(bar-柱状图)(pie-饼图)(map-地图) type: 'line', name: '订单量' }, { data: [250, 130, 124, 118, 235, 247, 160], type: 'line', name: '销售额' }, { data: [50, 30, 24, 18, 35, 47, 60], type: 'bar', barWidth: '60%', // 控制柱子的宽度 name: '纯收入' } ], // 颜色配置 color: ['red', 'green', { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'red' // 0% 处的颜色 }, { offset: 1, color: 'blue' // 100% 处的颜色 }], global: false // 缺省为 false }], // 网格配置(坐标轴这个区域) grid: { top: 100 // 调整坐标轴区域的位置 }, // 图例(用于表达 每个图形的意义) // 需要series中,每项数据必须定义name名字 legend: { top: 60 }, // 提示框组件 tooltip: { // 提示的触发方式 // 默认是 item,鼠标放到每一项上才能提示 // 可选 axis ,鼠标放到轴上即可提示 trigger: 'axis', } } ``` # 折线图 ## 写基础的步骤 ```js // ------------------- 折线图 -------------------------------- function lineChart () { let myChart = echarts.init(document.querySelector('#line')) let option = {} myChart.setOption(option) // 第1个O是大写的 } lineChart() ``` ## 套用官方示例 官方示例代码如下: ```js let option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [ { data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', smooth: true } ] } ``` ## 修改配置 1. 增加标题(title) 1. 1. 标题文本(text):2022全学科薪资走势 2. 距离顶部(top):15 3. 距离左侧(left):10 4. 文字大小(textStyle > fontSize):16 1. X 轴(xAxis) 1. 1. 标签文字(data):['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] 2. 标签文字颜色(axisLabel > color):#999 3. 轴线颜色(axisLine > lineStyle > color):#ccc 4. 轴线类型(axisLine > lineStyle > type):点划线 1. Y 轴(yAxis) 1. 1. Y 轴分割线类型((splitLine > lineStyle > type)):点划线 1. 鼠标移入提示(tooltip) 1. 1. (trigger: 'axis')设置为鼠标移入轴线提示 1. 网格(grid) 1. 1. 距离顶部:20% 1. 颜色(color) 1. 1. 渐变颜色,参考链接:https://echarts.apache.org/zh/option.html#color 2. 0%处的颜色:#499FEE 3. 100%处的颜色:#5D75F0 1. 系列数据(series) 1. 1. 数据:[9000, 12000, 15000, 13000, 10000, 18000, 14000, 10000, 12000, 13000, 15000, 19000] 2. 平滑曲线 3. 线条粗细:6 4. 拐点空心圆:10 5. 区域面积渐变: 1. 1. 1. 0处颜色:#499FEE 2. 0.8处颜色:rgba(255,255,255,0.2) 3. 1处颜色:rgba(255,255,255,0) 4. **渐变色设置参考**:https://echarts.apache.org/zh/option.html#color # 饼图(右上角) ## 写基础的步骤 ```js function classSalaryChart () { let myChart = echarts.init(document.querySelector('#salary')) let option = {} myChart.setOption(option) // 第1个O是大写的 } classSalaryChart() ``` ## 套用官方示例 [传送门](https://echarts.apache.org/examples/zh/editor.html?c=pie-borderRadius) ## 修改配置 1. 增加标题(title) 1. 1. 标题文本:班级薪资分布 2. 距离顶部:15 3. 距离左侧:10 4. 文字大小:16 1. 系列数据(series) 1. 1. 去掉了 avoidLabelOverlap 和 emphasis 两个配置 2. 系列数据名 name:班级薪资分布 3. 环形内外圈半径 radius:['50%', '64%'] 4. 每一项样式 itemStyle(不需要改): 1. 1. 1. 边框颜色:#fff 2. 边框大小:2 3. 扇形内外圆角半径:10 1. 1. 文字标签 label(不需要改):不显示 2. 视觉引导线 labelLine(不需要改):不显示 3. 数据: ```javascript [ { value: 1048, name: '1万以下' }, { value: 235, name: '1万-2万' }, { value: 580, name: '1.5万-2万' }, { value: 484, name: '2万以上' } ] ``` 1. 图例(legend) 1. 1. 左右居中 2. 距离**底**部:6% 1. 鼠标移入提示(tooltip)(不需要改) 1. 1. 鼠标移入每一项上提示 1. 颜色(color) 1. 1. ['#FDA224', '#5097FF', '#3ABCFA', '#34D39A'] # 饼图(左下角) ## 写基础的步骤 ```js function sexSalaryChart () { let myChart = echarts.init(document.querySelector('#gender')) let option = {} myChart.setOption(option) } sexSalaryChart() ``` ## 复制上一个饼图并修改 复制之后 - 删除 series中的 itemStyle - 删除 series中的 label - 删除 series中的 baleLine - 复制 series 中的一个小对象,并修改圆心点 - center: ['50%', '30%'] center: ['50%', '70%'] - 删除图例 legend 标题设置: - 一个标题,使用一个 对象 `{}` 表示 - 多个标题,使用一个 数组 `[{标题1}, {标题2}, {标题3}]` 表示 ```js title: [ { text: '男女生薪资分布', top: 15, left: 10, textStyle: { fontSize: 16 } }, { text: '男生', top: '50%', left: '45%', textStyle: { fontSize: 12 } }, { text: '女生', top: '85%', left: '45%', textStyle: { fontSize: 12 } } ], ``` # 地图 ## 准备工作 要做地图,除了引入 echarts.min.js 之外,还需要地区的配置文件。 比如做中国地图,需要引入 china.js 才可以。 更多地图做法,参考群里的 7 分钟视频。 ```js // ------------------- 地图 ---------------------------------- function mapChart () { let myChart = echarts.init(document.querySelector('#map')) let option = { // 从 0 开始,自己写配置 } myChart.setOption(option) } mapChart() ``` ## 配置 ## 修改配置 1. 标题(title) 1. 1. 标题文字:籍贯分布 2. 距离顶部:10 3. 距离左边:10 4. 文字大小:16 1. 系列数据(series) 1. 1. 系列数据名(name):籍贯分布 2. 类型(type: 'map') -- 表示地图 3. map: 'china' --- 表示使用中国地图(到这一步,就可以显示中国地图了) 4. 通过 label 设置每个区域(每个省)的名字 1. 1. 1. (show: true)显示省的名字 2. (fontSize)文字大小:10 3. (color)字体颜色:rgba(0,0,0,0.7) 1. 1. 通过 itemStyle 设置每个区域(每个省)默认的颜色 1. 1. 1. borderColor 区域边界线(省界线)颜色:rgba(0, 0, 0, 0.2) 2. areaColor 区域颜色:#E0FFFF 1. 1. 通过 emphasis 设置高亮状态(鼠标移入)的样式 1. 1. 1. areaColor 区域颜色:#34D39A 2. borderWidth 区域边框(省界线)大小:0 3. shadowBlur 阴影模糊大小:20 4. shadowOffsetX shadowOffsetY 阴影偏移:0 5. shadowColor 阴影颜色:rgba(0, 0, 0, 0.5) 1. 1. 使用data设置每个省的数据 ```javascript // 因为用地图表示每个省有几名同学 // 所以可以提前准备好数据 const mapData = [ { name: '南海诸岛', value: 0 }, { name: '北京', value: 3 }, { name: '天津', value: 2 }, { name: '上海', value: 4 }, { name: '重庆', value: 1 }, { name: '河北', value: 20 }, { name: '河南', value: 23 }, { name: '云南', value: 0 }, { name: '辽宁', value: 15 }, { name: '黑龙江', value: 12 }, { name: '湖南', value: 2 }, { name: '安徽', value: 5 }, { name: '山东', value: 18 }, { name: '新疆', value: 0 }, { name: '江苏', value: 5 }, { name: '浙江', value: 1 }, { name: '江西', value: 4 }, { name: '湖北', value: 3 }, { name: '广西', value: 2 }, { name: '甘肃', value: 9 }, { name: '山西', value: 11 }, { name: '内蒙古', value: 14 }, { name: '陕西', value: 14 }, { name: '吉林', value: 10 }, { name: '福建', value: 0 }, { name: '贵州', value: 0 }, { name: '广东', value: 0 }, { name: '青海', value: 3 }, { name: '西藏', value: 0 }, { name: '四川', value: 1 }, { name: '宁夏', value: 1 }, { name: '海南', value: 0 }, { name: '台湾', value: 0 }, { name: '香港', value: 0 }, { name: '澳门', value: 0 } ] ``` 然后series中,通过 data 指定使用这些数据 ```javascript series: [ { type: 'map', ... ... 其他配置略 data: mapData // 指定数据 } ] ``` 1. 鼠标移入提示(tooltip) 1. 1. 鼠标放到每个省的时候,进行提示 2. 自定义提示的格式为:`河北:20 位学员` 这样的格式 3. 提示框边框透明 4. 提示框背景色:rgba(0,0,0,0.5) 5. 文字颜色:#fff 1. 视觉映射组件(visualMap) 1. 1. 视觉映射组件,根据每个区域数据的大小来修改每个区域的颜色 2. 显示视觉映射组件 3. max min 约定最大值20,最小值0 4. text: [20, 0] 视觉映射组件开头和结尾文字也是 20和0 5. 组件距离左边40,距离下边20 6. inRange: { color: ['', ''] } 从小到大的颜色分别为:#fff 和 #0075F0 # 作业:柱状图要求 ## 修改配置 网格(grid) 1. 1. 距离左边:70 2. 距离顶部:30 3. 距离右边:30 4. 距离下边:50 X 轴(xAxis) 1. 1. 轴线颜色:#ccc 2. 轴线类型:点划线 3. 文字颜色:#999 Y 轴(yAxis) 1. 1. Y轴分割线:点划线 鼠标移入提示(tooltip) 1. 1. 鼠标移入柱子提示 系列数据(series) 1. 1. 因为有两个柱子,所以需要两份数据 ```javascript series: [ { data: [12200, 17932, 13901, 13934, 21290, 23300, 13300, 13320], type: 'bar', name: '期望薪资' // 这个数据的名字,可以在鼠标移入的提示上显示 }, { data: [22820, 19932, 16901, 15934, 31290, 13300, 14300, 18320], type: 'bar', name: '就业薪资' // 这个数据的名字,可以在鼠标移入的提示上显示 } ] ``` 颜色(color) 1. 1. 因为有两个柱子,所以颜色也使用**数组**,并且每一个柱子使用渐变色 2. 第1个柱子:0处的颜色#34D39A,1处的颜色rgba(52,211,154,0.2) 3. 第2个柱子:0处的颜色#499FEE,1处的颜色rgba(73,159,238,0.2) 作业:自己做一个自己家乡的地图(做市级地图,参考群里的视频) # 创建并切换到login  # 插件 ## val() 作用一:获取表单各项的值,语法: `let data = val(表单)` 作用二:修改的时候,可以做数据回填,语法: `val(表单, 数据)` ## message - message.success('成功的提示') - message.error('失败的提示') - message.confirm('标题', '提示内容', 函数) ---- 删除学员的时候用 # 注册代码 ```js // 注册业务:获取输入框的账号、密码,提交给接口即可; // 如果有问题,则提示一下(比如账号已存在、比如账号太长了、.......) // 如果注册成功,则跳转到登录页去登录 document.querySelector('#btn-login').addEventListener('click', function (e) { e.preventDefault() // 使用插件收集表单各项的值 // let data = val(表单) // val() 函数就会把表单各项的值收集到(要求表单各项必须有name属性) let data = val(document.querySelector('form')) // serialize(表单, { hash: true }) // console.log(data) // {username: 'aaa', password: 'bbb'} // 简单的验证一下数据格式 if (data.username.length < 2 || data.username.length > 30) { message.error('用户名长度应该是2~30位') return } if (data.password.length < 6 || data.password.length > 30) { message.error('密码长度应该是6~30位') return } // Ajax把数据提交给接口 axios({ method: 'POST', url: 'http://ajax-api.itheima.net/register', data: data }).then(result => { // 成功后提示 message.success(result.data.message) // 提示注册成功 // 清空输入框的值 document.querySelector('form').reset() // 重置表单 // 跳转到登录页 setTimeout(() => { // 等一下,等提示隐藏了再跳转 location.href = './login.html' }, 1500) }).catch(err => { // console.dir(err) message.error(err.response.data.message) // 提示账号已存在 }) }) ``` # 登录功能 因为登录的业务逻辑和注册差不多。 所以,复制上述注册的代码 到 login.html 中。 并修改以下内容: - 接口地址修改为: `http://ajax-api.itheima.net/login` - 登录成功后,跳转到:`location.href = './index.html'` # token的使用  具体到代码: 1. 登录成功之后,存储token ```js localStorage.setItem('token', result.data.data.token) ``` 2. 首页请求接口数据的时候,带请求头 ```js // ==================================================================================== // 发送请求,获取接口数据,把数据渲染到页面中,把数据用到图表中 axios({ url: 'http://ajax-api.itheima.net/dashboard', headers: { Authorization: localStorage.getItem('token') } }).then(result => { console.log(result) }) ``` # axios配置请求根路径和请求头 axios配置好请求根路径之后,后续所有的请求,都不用写请求根路径了。 配置语法:`axios.defaults.baseURL = 'http://ajax-api.itheima.net'` 只要有上述这行代码,后面所有的请求,都不用加 请求根路径了。 项目中,由于每个html文件,都引入了 common.js 的文件,所以,我们可以选择把 统一的配置,放到 common.js 中。 同理,也可以全局统一配置请求头,代码如下。 common.js 中的代码: ```js // 上面这个代码处理过度动画(默认加上不用管) document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { document.body.classList.add('sidenav-pinned') document.body.classList.add('ready') }, 200) }) // 加入axios的配置 // 配置请求根路径 axios.defaults.baseURL = 'http://ajax-api.itheima.net' // 配置请求头 axios.defaults.headers.common['Authorization'] = localStorage.getItem('token') ``` # 响应拦截器处理401错误 拦截器分为 - 请求拦截器 - 响应拦截器 参考地址:https://www.axios-http.cn/docs/interceptors 项目中,如果是token 的问题(不是忘记携带了,就是token过期了),接口都会响应 401 状态码。 所以,添加响应拦截器,判断响应状态码是否是401,如果是,则跳转到登录。 在common.js中,**添加**如下代码: ```js // 添加响应拦截器 axios.interceptors.response.use( function (response) { // 2xx 范围内的状态码都会触发该函数。成功进入这个函数 // 对响应数据做点什么 return response; }, function (error) { // 超出 2xx 范围的状态码都会触发该函数。失败进入这个函数 // 对响应错误做点什么 // console.dir(error) if (error.response.status === 401) { // 说明 token 有问题了(不是忘记携带了,就是token过期了) location.href = './login.html' } return Promise.reject(error); } ); ``` # 首页数据处理 ## 概览区处理 略 ## 折线图数据处理 - 原来 那次 调用函数,注释掉 - 等接口响应数据之后,再调用函数,并且传递实参 `lineChart(year)` - lineChart 函数里面: - X轴数据: data: a.map(item => item.month) - series数据:data: a.map(item => item.salary) ## 饼图数据处理 - 原来 那次 调用函数,注释掉 - 等接口响应数据之后,再调用函数,并且传递实参 - - `classSalaryChart(salaryData)` -- 右上角饼图 - `sexSalaryChart(salaryData)` -- 左下角 - classSalaryChart(右上角函数) - series中, data: a.map(item => { // return { value: 20, name: '一万以下' } return { value: item.g_count + item.b_count, name: item.label } }) - sexSalaryChart(左下角函数) - series中,data: a.map(item => { // return { value: 20, name: '一万以下' } return { value: item.g_count, name: item.label } }) ## 柱状图数据处理 - 原来 那次 调用函数,注释掉 - 等接口响应数据之后,再调用函数,并且传递实参 `groupSalaryChart(groupData)` - 默认展示1组数据 - X轴的人名 `data: a[1].map(item => item.name)` - series中的两组数据 `a[1].map(item => item.hope_salary)` 和 `a[1].map(item => item.salary)` - 点击之后,做排他效果 - 点击之后,设置对应组的数据,并重新创建图表 ```js // 给柱状图上面的 8 个按钮,注册click事件 document.querySelector('#btns').addEventListener('click', function (e) { if (e.target.tagName === 'BUTTON') { // 排他效果 document.querySelector('#btns .btn-blue').classList.remove('btn-blue') e.target.classList.add('btn-blue') // echarts更换图表数据的步骤【1. 更换图表中x轴、series数据 2. myChart.setOption(option)重新创建图表即可】 // 获取组号 let i = e.target.innerHTML.trim() // trim()是去掉字符串两边的空白 // console.log(i) // 组号有了,每组的数据就是 a[i] // 换图表配置项中的数据 option.xAxis.data = a[i].map(item => item.name) option.series[0].data = a[i].map(item => item.hope_salary) option.series[1].data = a[i].map(item => item.salary) myChart.setOption(option) } }) ``` ## 地图数据处理 - 原来 那次 调用函数,注释掉 - 等接口响应数据之后,再调用函数,并且传递实参 `mapChart(provinceData)` - 函数中,在模板数据(mapData)之后,加入下面的代码即可 ```js // 在模板数据、接口返回的数据 之后。融合两部分数据 mapData.forEach(item => { // 判断接口返回的数据有没有北京、河北的、内蒙古的 let 结果 = a.find(v => { return v.name.includes(item.name) }) // console.log(结果) if (结果) { // item.value = 接口返回的value item.value = 结果.value } }) ``` # 合并,创建新分支 上述完成 - 将当前 login 分支的代码都提交了 (git add . git commit -m 'xxxxx') - 切换到master分支 (git checkout master) - 合并login分支 (git merge login) 创建新分支,准备开发学生列表页 - 创建新分支 (git branch student) - 切换到新分支 (git checkout student) # 学生列表 略 # 添加学员 ## 模态框的使用 模态框的使用: ```js let modal = new bootstrap.Modal(盒子) // 让模态框显示 modal.show() // 让模态框隐藏 modal.hide() ``` 具体到我们的项目代码: ```js // --------------------------- 添加学员 ----------------------------------- // 模态框盒子 在 student.html 第 205~285 行 let addModal = new bootstrap.Modal(document.querySelector('#modal')) // 点击 + 的时候,让模态框显示 document.querySelector('#openModal').addEventListener('click', function () { addModal.show() // 让模态框显示 }) // 点击模态框中的 确认 按钮 document.querySelector('#submit').addEventListener('click', function () { addModal.hide() // 让模态框隐藏 }) ``` ## 省市县联动 ```js // --------------------------- 省市县联动 --------------------------------- let sheng = '' let shi = '' let xian = '' let province = document.querySelector('[name=province]') // 下拉框 let city = document.querySelector('[name=city]') let area = document.querySelector('[name=area]') // 获取省,并渲染 axios({ url: '/api/province' }).then(result => { // console.log(result) let newArr = result.data.data.map(item => ``) province.innerHTML = sheng + newArr.join('') }) // 省切换的时候,获取市,并渲染 province.addEventListener('change', function () { // console.log(province.value) area.innerHTML = xian // 切换省的时候,重置区县 axios({ url: '/api/city', params: { pname: province.value } }).then(result => { let newArr = result.data.data.map(item => ``) city.innerHTML = shi + newArr.join('') }) }) // 市切换的时候,获取区县,并渲染 city.addEventListener('change', function () { axios({ url: '/api/area', params: { pname: province.value, cname: city.value } }).then(result => { let newArr = result.data.data.map(item => ``) area.innerHTML = xian + newArr.join('') }) }) ``` ## 完成添加功能 ```js // 点击模态框中的 确认 按钮 document.querySelector('#submit').addEventListener('click', function () { // 获取表单各项的值 // let data = val(表单) let data = val(document.querySelector('#form')) // 检查是否是接口需要的数据 // console.log(data) data.age = +data.age data.group = +data.group data.gender = +data.gender data.hope_salary = +data.hope_salary data.salary = +data.salary // console.log(data) // Ajax提交 axios({ method: 'POST', url: '/students', data: data }).then(result => { // console.log(result) message.success(result.data.message) // 使用插件提示消息 document.querySelector('#form').reset() // 重置表单 renderStudent() // 更新页面数据 addModal.hide() // 让模态框隐藏 }) }) ``` # 删除和编辑的click事件 页面渲染的时候,给 ``标签加自定义属性 `data-id="${item.id}"` ```js // 找到tbody注册点击事件,里面判断点击的是删除,还是编辑 document.querySelector('tbody').addEventListener('click', function (e) { if (e.target.classList.contains('bi-trash')) { // 说明点击了删除 // console.log('你点击了删除') let id = e.target.dataset.id // 发送ajax请求,进行删除操作 axios({ url: `/students/${id}`, method: 'DELETE' }).then(result => { // console.log(result) message.success('删除成功') // 提示 renderStudent() // 更新页面数据 }) } if (e.target.classList.contains('bi-pen')) { // 说明点击了编辑 console.log('你点击了编辑') } }) ``` # 编辑功能 ## 修改模态框的标题 点击 + 号的时候,修改模态框的标题: ```js // 点击 + 的时候,让模态框显示 document.querySelector('#openModal').addEventListener('click', function () { document.querySelector('.modal-title').innerHTML = '添加学员' addModal.show() // 让模态框显示 }) ``` 点击 编辑 的时候,修改模态框的标题和 属性: ```js // 找到tbody注册点击事件,里面判断点击的是删除,还是编辑 document.querySelector('tbody').addEventListener('click', function (e) { // 判断点击的是否是删除 -------------- 略,上一步已经写完了 // 判断点击的是否是编辑 if (e.target.classList.contains('bi-pen')) { // 说明点击了编辑 // console.log('你点击了编辑') let id = e.target.dataset.id document.querySelector('.modal-title').innerHTML = '修改学员' // document.querySelector('.modal-title').setAttribute('属性名', 值) document.querySelector('.modal-title').setAttribute('data-id', id) addModal.show() // 显示模态框 } }) ``` ## 做数据回填 代码位置:点击 tbody 的时候,判断点击的是 编辑。 ```js // 找到tbody注册点击事件,里面判断点击的是删除,还是编辑 document.querySelector('tbody').addEventListener('click', async function (e) { if (e.target.classList.contains('bi-trash')) { // 说明点击了删除 // 代码略。。。。。。。。。。。。。。。。。。。。。。。。。。。。 } if (e.target.classList.contains('bi-pen')) { // 说明点击了编辑 // console.log('你点击了编辑') let id = e.target.dataset.id // 获取编辑按钮的自定义属性值 document.querySelector('.modal-title').innerHTML = '修改学员' // 下面是设置模态框标题(h5)的自定义属性,值是当前修改的学员的id // document.querySelector('.modal-title').setAttribute('属性名', 值) document.querySelector('.modal-title').setAttribute('data-id', id) addModal.show() // 显示模态框 // 发送请求,获取当前修改的这个学员的详细信息 let result = await axios({ url: `/students/${id}` }) console.log(result.data.data) // 输出的是 个人的详细信息 // 获取城市 和 区县,渲染到第2个和第3个下拉框的位置 let res1 = await axios({ url: '/api/city', params: { pname: result.data.data.province } }) let res2 = await axios({ url: '/api/area', params: { pname: result.data.data.province, cname: result.data.data.city } }) // console.log(res1) let newArr1 = res1.data.data.map(item => ``) city.innerHTML = shi + newArr1.join('') // console.log(res2) let newArr2 = res2.data.data.map(item => ``) area.innerHTML = xian + newArr2.join('') // 做数据回填,继续使用插件 【 val(表单, 数据) 】,关于这个插件,只是简化代码的步骤。 val(document.querySelector('#form'), result.data.data) } }) ``` ## 点击确认,完成编辑 ```js // 点击模态框中的 确认 按钮 document.querySelector('#submit').addEventListener('click', async function () { // 获取表单各项的值 // let data = val(表单) let data = val(document.querySelector('#form')) // 检查是否是接口需要的数据 // console.log(data) data.age = +data.age data.group = +data.group data.gender = +data.gender data.hope_salary = +data.hope_salary data.salary = +data.salary // console.log(data) let title = document.querySelector('.modal-title').innerHTML let result if (title === '添加学员') { // Ajax提交,添加数据 result = await axios({ method: 'POST', url: '/students', data: data }) } else if (title === '修改学员') { // Ajax提交,修改数据 let id = document.querySelector('.modal-title').dataset.id result = await axios({ method: 'PUT', url: `/students/${id}`, data: data }) } message.success(result.data.message) // 使用插件提示消息 document.querySelector('#form').reset() // 重置表单 renderStudent() // 更新页面数据 addModal.hide() // 让模态框隐藏 }) ``` # 项目的退出功能 退出,和登录是相反的。 登录后,存储了token,跳转到index.html 退出后,移除token,跳转到login.html common.js中: ```js // 退出功能 document.querySelector('#logout')?.addEventListener('click', function () { // 1. 移除token localStorage.removeItem('token') // 2. 跳转到登录页 location.href = './login.html' // 路径和JS文件在哪里无关;和html文件有关 }) ```