1、git管理
- 在gitee创建了远程仓库
- 在本地提交文件到本地仓库
- 将本地仓库提交到远程仓库
- 具体操作请查看git
1.实现远程仓库的别名设置
git remote add 别名 地址
2。查看设置是否成功
git remote -v
3.第一次提交需要建立 关联
git push -u 别名 master
4.后面的提交就只需要
git push
2、axios根路径的全局配置
- 在utils目录下创建request.js文件
- 在request文件中创建根路径的全局配置(axios.defaults.baseURL)
utils/request.js
axios.defaults.baseURL = 'http://www.itcbc.com:8000'
3、提示插件的使用
toastr插件的使用:utils/mytoastr.js
1.引入插件的资源
2.添加必须要结构和样式
3.初始化
4.按文档说明使用
3.1 引入插件的资源
<link rel="stylesheet" href="./assets/lib/toastr/toastr.min.css" />
<script src="./assets/lib/toastr/toastr.min.js"></script>
3.2 初始化
toastr.options = {
positionClass: 'toast-top-right', // 提示框位置,这里填类名
showDuration: '300', // 提示框渐显所用时间
hideDuration: '300', // 提示框隐藏渐隐时间
timeOut: '2000' // 提示框持续时间
}
3.3 调用方法显示提示
toastr.info('提示信息'); // 普通提示
toastr.success('提示信息'); // 成功提示
toastr.warning('提示信息'); // 警告提示
toastr.error('提示信息'); // 错误提示
4、用户信息校验插件
- 表单验证插件的下载路径
4.1 引入插件的资源
<link rel="stylesheet" href="./assets/lib/validator/bootstrapValidator.min.css" />
<script src="./assets/lib/validator/bootstrapValidator.min.js"></script>
4.2 初始化-配置校验规则
// 添加表单的校验规则
// 比如,验证一个用户名和密码
function test() {
return {
// 指定需要校验的字段名称
fields: {
username: {
// 这里username是 input 的name属性值,表示对这个输入框进行验证
validators: {
// 添加真正的校验规则
notEmpty: {
//不能为空
message: '用户名不能为空.' // 如果不满足校验规则,则给出这句提示
},
stringLength: {
//检测长度
min: 2, // 最少2位
max: 15, // 最多15位
message: '用户名需要2~15个字符'
}
}
},
password: {
validators: {
notEmpty: {
message: '密码不能为空'
},
stringLength: {
//检测长度
min: 6,
max: 15,
message: '密码需要6~15个字符'
}
}
}
}
}
}
4.3 使用插件语法绑定提交事件
$('.register form')
.bootstrapValidator(test())
.on('success.form.bv', function(e) {
e.preventDefault()
// 接下来收集数据发起ajax请求
})
5、注册
一:实现登陆和注册面板的显示和隐藏
二:实现注册功能
5.1 实现注册和登陆面板的显示和隐藏
- 获取去注册和去登录按钮元素,以及注册面板和登录面板元素
- 给去登录按钮和去注册按钮绑定事件
- 点击去注册,让登录面板隐藏,同时让注册面板显示
- 反过来,点击去登录,让注册面板隐藏,同时让登录面板显示
// 1.获取元素
// 1.1 去注册
let goRegister = document.querySelector('.goRegister')
// 1.2 去登陆
let goLogin = document.querySelector('.goLogin')
// 1.3 注册面板
let register = document.querySelector('.register')
// 1.4 登陆面板
let login = document.querySelector('.login')
// 2.实现登陆和注册面板的显示和隐藏
goRegister.addEventListener('click', function() {
login.style.display = 'none'
register.style.display = 'block'
})
goLogin.addEventListener('click', function() {
register.style.display = 'none'
login.style.display = 'block'
})
5.2 实现注册功能
- 收集表单数据
- 发起ajax请求
- 请求成功后判断code状态值
- 判断code状态值如果是0,表示账号还没注册,提示注册成功
- 判断code状态值如果是1,表示账号已注册,提示账号已存在
// 3.实现注册
// 比如,注册
// $('.register form'):指定监听那个表单的submit默认提交行为
// bootstrapValidator:调用插件的验证方法
// test():校验规则 ,要自己定义
// success.form.bv:submit的默认提交行为
$('.register form')
.bootstrapValidator(test())
.on('success.form.bv', function(e) {
e.preventDefault()
// 通过验证,这里的代码将会执行。我们将Ajax请求的代码放到这里即可
// 1.收集数据
let username = rusername.value
let password = rpassword.value
// 2.发起ajax请求
axios({
method: 'post',
url: '/api/register',
data: { username, password }
}).then(res => {
if (res.data.code == 0) {
toastr.success(res.data.message)
} else if (res.data.code == 1) {
toastr.warning(res.data.message)
}
})
})
6、登陆
- 也要使用提示框插件
- 也要使用用户信息校验插件
- 按插件的请求绑定事件
- 发起ajax请求
- 请求成功后判断code状态值
- 判断code状态值如果是0,将token存储到本地,提示登录成功,跳转到首页
- 否则提示账号或密码错误
// 4.实现登陆 -- /api/login
$('.login form')
.bootstrapValidator(test())
.on('success.form.bv', function(e) {
e.preventDefault()
// 1.收集数据
let username = lusername.value
let password = lpassword.value
// 2.发起ajax请求
axios({
method: 'post',
url: '/api/login',
data: { username, password }
}).then(res => {
if (res.data.code == 0) {
localStorage.setItem('token_79', res.data.token)
toastr.success(res.data.message)
// 跳转到首页
location.href = './index.html'
} else {
toastr.error(res.data.message)
}
})
})
7、状态保持
HTTP协议是无状态协议,并不会记忆用户之前的操作,对于它而言每个请求都是单独的,崭新的请求。所以我们需要实现 状态保持
- 1.在可视化项目中,除了登陆和注册接口,其它所有接口的请求都需要身份认证
- 2.这种身份认证的场景在后台数据管理项目中必定存在
- 3.如果发生401错误,这种错误说明 在请求接口数据的时候,你得告诉服务器你是谁,得证明你的权限进行接口的请求
7.1 登陆成功之后接收token并存储(实现状态保持的第一种方法)
在login.js文件登陆成功代码中进行添加,登录成功后进行状态保持的本地存储
if (res.data.code == 0) {
// 将后台响应的token存储到本地存储
localStorage.setItem('token_79', res.data.token)
toastr.success(res.data.message)
// 跳转到首页
location.href = './index.html'
}
7.2 在后续请求时传递token
- 必须须请求头中传递token
- 请求头的键名必须是 Authorization
- 具体的请求头配置信息可以到axios文档中进行查看
// 实现数据渲染
function init() {
axios({
url: '/student/list',
// 添加axios请求头的配置
headers: { Authorization: localStorage.getItem('token_79') }
}).then(res => {
console.log(res)
})
}
8、拦截器
8.1 请求拦截器(实现状态保持的第二种方法)
// 添加请求拦截器:所有请求都会经过
// config:请求报文对象
axios.interceptors.request.use(
function(config) {
// 在发送请求之前做些什么:以请求头的方式传递token
let token = localStorage.getItem('token_79')
// 判断是否有token,如果有token才进行传递
if (token) {
// 进行token的请求头的设置
config.headers.Authorization = token
}
return config
},
function(error) {
// 对请求错误做些什么
return Promise.reject(error)
}
)
9、学员信息
9.1 数据渲染
一:渲染之前要准备好 结构和数据
二:渲染的实现方式就是遍历拼接
- 发起ajax请求
- 请求成功后对结构和数据进行遍历拼接
// 实现数据渲染
function init() {
axios({
url: '/student/list'
}).then(res => {
console.log(res)
let htmlStr = ''
res.data.data.forEach(function(value, index) {
htmlStr += `<tr>
<th scope="row">${index + 1}</th>
<td>${value.name}</td>
<td>${value.age}</td>
<td>${value.sex}</td>
<td>${value.group}</td>
<td>${value.phone}</td>
<td>${value.salary}</td>
<td>${value.truesalary}</td>
<td>${value.province + value.city + value.county}</td>
<td>
<button type="button" class="btn btn-primary btn-sm">修改</button>
<button type="button" class="btn btn-danger btn-sm">删除</button>
</td>
</tr>`
})
tbody.innerHTML = htmlStr
})
}
init()
9.2 数据添加
使用js方式弹出模态框
- 点击添加学员打开模态框
- 让模态框的标题改成录入新学员
- 模态框按钮的文本内容改成确定添加
- 调用重置按钮的click事件,让表单内容清空
// 获取添加学员按钮
let btnAddDialog = document.querySelector('.btnAddDialog')
// 获取模态框确定按钮
let determine = document.querySelector('.determine')
// 获取重置按钮
let btnreset = document.querySelector('.btnreset')
// 打开学员添加模态框
btnAddDialog.addEventListener('click', function() {
$('#addModal').modal('show')
addModalLabel.innerText = '录入新学员'
determine.innerHTML = '确定添加'
btnreset.click()
})
加载省的数据
页面一打开就去加载省的数据:只加载一次就可以了
- 发起ajax请求
- 请求成功后做省的数据动态渲染
// 获取省级菜单
let province = document.querySelector('[name="province"]')
// 加载省的数据
axios({
url: '/geo/province'
}).then(res => {
let htmlStr = '<option selected value="">--省--</option>'
res.data.forEach(function(value, i) {
htmlStr += `<option value="${value}">${value}</option>`
})
province.innerHTML = htmlStr
})
加载当前所选择的省的市
- 监听省的选项的变化(change事件),加载这个省所对应的市(看文档)
- 用户选择省的默认值的时候,就让市和县同时也跳回默认值
- 获取省的value值,因为想要加载市,前提是需要获取到省的值
- 发起ajax请求
- 请求成功后做市的数据动态渲染
// 获取市级菜单
let city = document.querySelector('[name="city"]')
// 获取县级菜单
let county = document.querySelector('[name="county"]')
// 加载市的数据
province.addEventListener('change', function() {
// 用户选择省的默认值,就让市和县也跳回默认值
city.innerHTML = '<option selected value="">--市--</option>'
county.innerHTML = '<option selected value="">--县--</option>'
// 获取省的value值
// 因为加载市的前提,是需要获取到省的值
let pname = province.value
// 如果用户选择的是默认值(---省---)
if (!pname) {
return
}
// 发起ajax请求
axios({
url: '/geo/city',
params: {pname}
}).then(res => {
let htmlStr = '<option selected value="">--市--</option>'
res.data.forEach(value => {
htmlStr += `<option value="${value}">${value}</option>`
})
city.innerHTML = htmlStr
})
})
加载市所对应的区县
- 实现市县级联
- 监听市下拉列表的change事件,加载这个市所对应的区县
- 用户如果选择了市的默认值,就让县同时也跳回默认值
- 获取省和市的value值,因为想要加载县,前提是需要加载出省和市的值
- 发起ajax请求
- 请求成功后做县的数据动态渲染
// 加载县的数据
city.addEventListener('change', function() {
// 用户选择市的默认值,就让县也跳回默认值
county.innerHTML = '<option selected value="">--县--</option>'
// 获取省和市的value值
// 因为加载县的前提,是需要加载省和市的值
let pname = province.value
let cname = city.value
// 如果用户选择的是默认值(---市---)
if (!cname) {
return
}
// 发起ajax的请求
axios({
url: '/geo/county',
params: {pname, cname}
}).then(res => {
let htmlStr = '<option selected value="">--县--</option>'
res.data.forEach(value => {
htmlStr += `<option value="${value}">${value}</option>`
})
county.innerHTML = htmlStr
})
})
添加数据校验
// 添加数据校验
function student() {
return {
fields: {
name: {
validators: {
notEmpty: {
message: '姓名不能为空'
},
stringLength: {
min: 2,
max: 10,
message: '姓名长度2~10位'
}
}
},
age: {
validators: {
notEmpty: {
message: '年龄不能为空'
},
greaterThan: {
value: 18,
message: '年龄不能小于18岁'
},
lessThan: {
value: 60,
message: '年龄不能超过60岁'
}
}
},
sex: {
validators: {
choice: {
min: 1,
max: 1,
message: '请选择性别'
}
}
},
group: {
validators: {
notEmpty: {
message: '组号不能为空'
},
regexp: {
regexp: /^\d{1,2}$/,
message: '请选择有效的组号'
}
}
},
phone: {
validators: {
notEmpty: {
message: '手机号不能为空'
},
regexp: {
regexp: /^1\d{10}$/,
message: '请填写有效的手机号'
}
}
},
salary: {
validators: {
notEmpty: {
message: '实际薪资不能为空'
},
greaterThan: {
value: 800,
message: '期望薪资不能低于800'
},
lessThan: {
value: 100000,
message: '期望薪资不能高于100000'
}
}
},
truesalary: {
validators: {
notEmpty: {
message: '实际薪资不能为空'
},
greaterThan: {
value: 800,
message: '实际薪资不能低于800'
},
lessThan: {
value: 100000,
message: '实际薪资不能高于100000'
}
}
},
province: {
validators: {
notEmpty: {
message: '省份必填'
}
}
},
city: {
validators: {
notEmpty: {
message: '市必填'
}
}
},
county: {
validators: {
notEmpty: {
message: '县必填'
}
}
}
}
}
}
实现新增
- 做数据校验
- 使用formdata收集数据
- 发起ajax请求
- 请求成功后判断code如果是0,表示数据添加成功
- 给出提示信息
- 我们自己要调用重置元素的事件
- 让模态框隐藏
- 页面重新渲染
// 获取form表单
let addForm = document.querySelector('.add-form')
// 实现学员信息的添加
$('.add-form')
.bootstrapValidator(student())
.on('success.form.bv', function(e) {
e.preventDefault()
// 收集数据
let formdata = new FormData(addForm)
let data = {}
formdata.forEach(function(value, key) {
data[key] = value
})
// 发起ajax请求
axios({
method: 'post',
url: '/student/add',
data
}).then(res => {
// 请求成功后判断code如果是0,表示添加成功
if(res.data.code == 0) {
// 给出提示信息
toastr.success(res.data.message)
// 我们自己要调用重置元素的事件
btnreset.click()
// 让模态框隐藏
$('#addModal').modal('hide')
// 页面重新渲染
init()
}
})
})
9.3 数据删除
- 先存储自定义属性id
- 再添加委托事件
- 获取自定义属性id,因为删除是需要通过获取用户id进行删除的
- 发起ajax请求
- 判断code值如果是0
- 给出删除成功提示信息
- 做页面的动态渲染
- 判断code值如果是0
// 实现学员信息的删除
tbody.addEventListener('click', function(e) {
if(e.target.classList.contains('btn-danger')) {
// 获取自定义属性id,因为删除是需要通过获取用户id进行删除的
let id = e.target.dataset.id
// 发起ajax请求
axios({
method: 'DELETE',
url: '/student/delete',
params: {id}
}).then(res => {
if(res.data.code == 0) {
toastr.success(res.data.message)
init()
}
})
}
})
9.4 数据编辑
下拉列表的基本特性
1、取值
- 如果option选项有value属性设置,则优先获取value属性
- 如果没有value属性则获取option标签之间的内容
2、赋值:优先将选项的value匹配你所赋的值,将匹配项显示
- 如果有选项的value与赋的值匹配则进行展示
- 如果没有,则没有任何的内容
数据回填
编辑和新增共用一个模态框
-
打开模态框
- 使用js方式弹出模态框,因为不仅仅是弹出模态框,而且还要实现数据的回填
-
获取你想回填的数据
-
自定义属性存储所有值再获取所有值
-
存储数据id,根据id查询数据,实现数据回填 -- 现在这个业务的实现方式
- 后台得有这样的接口
-
-
发起ajax请求
- 实现数据的回填
- 常规的表单元素,可以直接赋值给value属性
- 单选按钮需要做特殊处理,判断如果是男选中男的单选按钮,否则选中女的单选按钮
- 省市联级的市和县也需要做特殊的处理
- 如果没有value选项,也需要进行赋值,则可以设置它的innerHTML
- 实现数据的回填
// 获取学员姓名表单元素
let name = document.querySelector('[name="name"]')
// 获取学员年龄表单元素
let age = document.querySelector('[name="age"]')
// 获取学员组号表单元素
let group = document.querySelector('[name="group"]')
// 获取学员性别表单元素
let sexs = document.querySelectorAll('[name="sex"]')
// 获取学员手机号表单元素
let phone = document.querySelector('[name="phone"]')
// 获取学员期望薪资表单元素
let salary = document.querySelector('[name="salary"]')
// 获取学员实际薪资表单元素
let truesalary = document.querySelector('[name="truesalary"]')
// 单击表格中的编辑按钮,弹出模态框,实现数据的回填
tbody.addEventListener('click', function(e) {
if (e.target.classList.contains('btnedit')) {
addModalLabel.innerText = '编辑学员信息'
determine.innerHTML = '确定编辑'
// 弹出模态框
$('#addModal').modal('show')
// 获取当前数据的id
let id = e.target.dataset.id
// 根据id查询对应的数据
axios({
url: '/student/one',
params: { id }
}).then(res => {
let data = res.data.data
// 常规表单元素(输入框),可以直接赋值value属性的
name.value = data.name
age.value = data.age
phone.value = data.phone
salary.value = data.salary
truesalary.value = data.truesalary
// 单选按钮
data.sex == '男' ? (sexs[0].checked = true) : (sexs[1].checked = true)
// 下拉列表
group.value = data.group
province.value = data.province
// 如果没有value选项,也需要进行赋值,则可以设置它的innerHTML
city.innerHTML = `<option selected value="${data.city}">--${data.city}--</option>`
county.innerHTML = `<option selected value="${data.county}">--${data.county}--</option>`
})
}
})
实现编辑功能
-
提升体验:修改模态框的标题和按钮的文本内容
- 单击新增打开模态框的时候,设置为 “录入新学员”
- 单击编辑打开模态框的时候,设置为“ 编辑学员”
-
在确认按钮的处理事件中,判断标题的文件内容,决定当前操作是新增还是编辑
-
编辑操作同样需要收集用户数据,不同的是,编辑还需要传入数据id
- 在编辑弹框的时候可以获取到数据id
- 将id存储到全局变量
- 在满足编辑条件的时候,在参数中追加id参数
let editid
// 实现学员信息的添加 或 编辑
$('.add-form')
.bootstrapValidator(student())
.on('success.form.bv', function(e) {
e.preventDefault()
// 收集数据
let formdata = new FormData(addForm)
let data = {}
formdata.forEach(function(value, key) {
data[key] = value
})
if (dialogTitle.innerText == '录入新学员') {
// 发起ajax请求
axios({
method: 'post',
url: '/student/add',
data
}).then(res => {
console.log(res)
if (res.data.code == 0) {
toastr.success(res.data.message)
// 我们自己要调用重置元素的事件
btnreset.click()
$('#addModal').modal('hide')
init()
}
})
} else {
// 实现编辑提交
// 追加数据id
data.id = editid
axios({
url: '/student/update',
method: 'put',
data
}).then(res => {
if (res.data.code == 0) {
toastr.success(res.data.message)
// 我们自己要调用重置元素的事件
btnreset.click()
$('#addModal').modal('hide')
init()
}
})
}
})
10、成绩录入
10.1 成绩数据的渲染
let tbody = this.document.querySelector('tbody')
// 成绩渲染
function init() {
axios({
url: '/score/list'
}).then(res => {
let data = res.data.data
let htmlStr = ''
for (let key in data) {
htmlStr += `<tr>
<th scope="row">${key}</th>
<td>${data[key].name}</td>
<td class="score">${data[key].score[0]}</td>
<td class="score">${data[key].score[1]}</td>
<td class="score">${data[key].score[2]}</td>
<td class="score">${data[key].score[3]}</td>
<td class="score">${data[key].score[4]}</td>
</tr>`
}
tbody.innerHTML = htmlStr
})
}
init()
10.2 成绩的录入和修改
-
为成绩单元格添加委托事件
-
在单元格中添加一个输入框
-
创建一个input输入框
-
判断 当前td中是否有输入框
- 如果没有:追加到当前td中
- 如果有:不创建也不追加了
-
-
先将td的原始值要存储起来
-
为输入框赋值内容
-
为输入框添加必要的样式
-
让输入框聚焦-选中
-
为输入框添加一个失焦事件,在事件处理函数中让td的内容还原
-
为输入框添加一个按键事件,当按下enter键的时候,实现成绩的录入和修改
- 获取td的自定义属性
- 学员是存储在td的父容器tr中,通过parentNode获取数据
- 先将td.innerHTML值清空。再设置td的innerText
// 实现成绩的录入和修改
// 1.页面效果
// 2.功能实现
tbody.addEventListener('click', function(e) {
if (e.target.className == 'score') {
let td = e.target
let score = td.innerText
if (!td.querySelector('input')) {
// 清空Td的原始内容
td.innerText = ''
// 创建一个input输入框
let input = document.createElement('input')
// 追加到当前td中
td.appendChild(input)
// 为input赋值,值就是td中的原始的内容
input.value = score
// 为输入框设置样式:去除边框
input.style.border = 'none'
input.style.background = 'transparent'
input.style.outline = 'none'
input.style.textAlign = 'center'
input.select()
// 为输入框添加一个失焦事件
input.addEventListener('blur', function() {
td.innerText = score
})
// 实现录入或修改
input.addEventListener('keyup', function(e) {
if (e.key == 'Enter') {
// 发起ajax请求
axios({
url: '/score/entry',
method: 'post',
data: {
// 当前学员id
stu_id: td.parentNode.dataset.id,
// 第几组
batch: td.dataset.batch,
// 成绩
score: input.value
}
}).then(res => {
console.log(res)
toastr.success('成绩录入或修改成功')
td.innerHTML = ''
td.innerText = input.value
})
}
})
}
}
})
11、图表页
11.1 网站概览
班级概况:
let total = document.querySelector('.total')
let avgSalary = document.querySelector('.avgSalary')
let avgAge = document.querySelector('.avgAge')
let proportion = document.querySelector('.proportion')
// 获取班级概况:只需要渲染一次
;(function() {
axios({
url: '/student/overview'
}).then(res => {
console.log(res)
let data = res.data.data
total.innerText = data.total
avgSalary.innerText = data.avgSalary
avgAge.innerText = data.avgAge
proportion.innerText = data.proportion
})
})()
11.2籍贯
需要使用到echarts插件
添加静态图表
-
引入插件的资源:
-
添加必要的结构或样式: 准备一个定义了高宽的 DOM 容器
-
初始化
- 通过 echarts.init 方法初始化一个 echarts 实例
- 通过 setOption 方法生成一个简单的图表
// 生成静态饼图
function createPie(data) {
// 基于准备好的dom,初始化echarts实例
let myChart = echarts.init(document.querySelector('.pie'));
// 指定图表的配置项和数据
let option = {
title: {
text: '籍贯分析',
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
series: [
{
name: 'Nightingale Chart',
type: 'pie',
radius: [10, 100],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
},
data
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
渲染动态图表
所谓动态饼图就是准备好饼图所需要渲染的数据
它要求的数据格式是对象数组
1.value是这个省的学员的数量
2.name是数据中的省份名称
实现方式
1.判断一下当前数组中有没有当前遍历到的省份,如果有,则数量+1
2.如果没有,则添加一条新的记录,name是当前省份名称,value值为1
// 获取所有学员信息
function init() {
axios({
url: '/student/list'
}).then(res => {
console.log(res)
let data = res.data.data
// 1.从源数据中提取出饼图数据
let pieArr = []
// 2.提取饼图数据....
data.forEach(function(value) {
// 判断一个pieArr中是否已经有了当前省份的记录,如果有,数量+1
// let obj = pieArr.filter(function(v){
// return v.name == value.province
// })[0]
let obj = null
for (let i = 0; i < pieArr.length; i++) {
if (pieArr[i].name == value.province) {
// 将数组中的对象赋值给obj,造成obj和数组中的对象指向同一个地址,后期修改其中一个另外一个也会变化
obj = pieArr[i]
}
}
// 如果当前遍历到的省份已存在,则将数量+1
if (obj) {
obj.value++
}
// 否则,添加一条新的数据
else {
pieArr.push({ value: 1, name: value.province })
}
})
// 调用方法生成动态饼图
createPie(pieArr)
})
}
init()
11.3 薪资
生成静态折线图
// 生成静态折线图
function createLine(names, salarys, truesalarys) {
// 基于准备好的dom,初始化echarts实例
let myChart = echarts.init(document.querySelector('.line'));
// 指定图表的配置项和数据
let option = {
title: {
text: '薪资 Salary'
},
dataZoom: [
{
start: 10,
end: 30
}
],
tooltip: {
trigger: 'axis'
},
legend: {
data: ['期望薪资', '实际薪资']
},
xAxis: {
type: 'category',
data: names
},
yAxis: {
type: 'value'
},
series: [
{
name: '期望薪资',
data: salarys,
type: 'line',
smooth: true,
lineStyle: {
color: 'red'
}
},
{
name: '实际薪资',
data: truesalarys,
type: 'line',
smooth: true,
lineStyle: {
color: 'blue'
}
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
生成动态折线图
// 获取所有学员信息
function init() {
axios({
url: '/student/list'
}).then(res => {
console.log(res)
let data = res.data.data
// 从源数据中提取出饼图数据--这是最终的饼图的数据
let pieArr = []
// 最终的折线图数据
let names = [],
salarys = [],
trueSalarys = []
// 1.遍历从接口获取的原始数据
data.forEach(function (value) {
// 2.提取饼图数据.... 开始
// 判断一个pieArr中是否已经有了当前省份的记录,如果有,数量+1,如果没有则在pieArr中添加一条新记录
// value就是当前所遍历到的原始用户信息对象
// 拿当前遍历到的value的province属性值,去pieArr数组中查找,看看能不能找到
let obj = null
for (let i = 0; i < pieArr.length; i++) {
// 说明这个省真的存在过
if (pieArr[i].name == value.province) {
obj = pieArr[i]
break
}
}
// 如果存在过,之前也找到了这个对象,那么就修改数量
if (obj) {
obj.value++
}
// 没有存在过
else {
pieArr.push({ value: 1, name: value.province })
}
// 2.提取饼图数据.... 结束
// 3.提取折线图数据 ----开始
names.push(value.name)
salarys.push(value.salary)
trueSalarys.push(value.truesalary)
// 3.提取折线图数据 ----结束
})
// 调用方法生成动态饼图
createPie(pieArr)
// 调用方法生成折线图
createLine(names, salarys, trueSalarys)
})
}
init()
11.4 成绩
生成静态的柱状图
// 生成静态柱状图
function createBar() {
let mychart = echarts.init(document.querySelector('.barChart'))
let option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['平均分', '低于60分人数', '60-80分之间', '高于80分人数']
},
grid: {
top: '10%',
left: '2%',
right: '2%',
bottom: '2%',
containLabel: true
},
xAxis: [
{
type: 'category',
axisTick: { show: false },
data: ['2012', '2013', '2014', '2015', '2016']
}
],
yAxis: [
{
type: 'value',
min: 0,
max: 100,
interval: 10,
axisLabel: {
formatter: '{value} 分'
}
},
{
type: 'value',
min: 0,
max: 10,
interval: 1,
axisLabel: {
formatter: '{value} 人'
}
}
],
series: [
{
name: '平均分',
type: 'bar',
barWidth: 20,
data: [55, 66, 77, 88, 44]
},
{
name: '低于60分人数',
type: 'bar',
barWidth: 20,
data: [2, 3, 4, 5, 6],
yAxisIndex: 1
},
{
name: '60-80分之间',
type: 'bar',
barWidth: 20,
data: [3, 4, 5, 6, 7],
yAxisIndex: 1 // 设置与那个轴的数据对应
},
{
name: '高于80分人数',
type: 'bar',
barWidth: 20,
data: [1, 6, 5, 3, 2],
yAxisIndex: 1
}
]
}
mychart.setOption(option)
}
渲染动态柱状图
// 按组查询成绩
function searchScore(batch) {
axios({
url: `/score/batch?batch=${batch}`
}).then(res => {
createBar(res.data.data)
})
}
searchScore(4)
显示不同组的成绩
let btn = document.querySelector('.btn')
let batch = document.querySelector('#batch')
// 显示和隐藏右侧菜单项
btn.addEventListener('click', function() {
if (batch.style.display == 'block') {
batch.style.display = 'none'
} else {
batch.style.display = 'block'
}
})
batch.addEventListener('click', function(e) {
searchScore(e.target.dataset.index)
})
11.5 地图
生成静态地图
function mapChart(chinaGeoCoordMap, chinaDatas) {
let myChart = echarts.init(document.querySelector('.map'))
var convertData = function (data) {
var res = []
for (var i = 0; i < data.length; i++) {
var dataItem = data[i]
var fromCoord = chinaGeoCoordMap[dataItem[0].name]
var toCoord = [113.434345, 23.135522]
if (fromCoord && toCoord) {
res.push([
{
coord: fromCoord,
value: dataItem[0].value
},
{
coord: toCoord
}
])
}
}
return res
}
var series = []
;[['广东省', chinaDatas]].forEach(function (item, i) {
console.log(item)
series.push(
{
type: 'lines',
zlevel: 2,
effect: {
show: true,
period: 4, //箭头指向速度,值越小速度越快
trailLength: 0.02, //特效尾迹长度[0,1]值越大,尾迹越长重
symbol: 'arrow', //箭头图标
symbolSize: 5 //图标大小
},
lineStyle: {
normal: {
width: 1, //尾迹线条宽度
opacity: 1, //尾迹线条透明度
curveness: 0.3 //尾迹线条曲直度
}
},
data: convertData(item[1])
},
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
//涟漪特效
period: 4, //动画时间,值越小速度越快
brushType: 'stroke', //波纹绘制方式 stroke, fill
scale: 4 //波纹圆环最大限制,值越大波纹越大
},
label: {
normal: {
show: true,
position: 'right', //显示位置
offset: [5, 0], //偏移设置
formatter: function (params) {
//圆环显示文字
return params.data.name
},
fontSize: 13
},
emphasis: {
show: true
}
},
symbol: 'circle',
symbolSize: function (val) {
return 5 + val[2] * 5 //圆环大小
},
itemStyle: {
normal: {
show: false,
color: '#f00'
}
},
data: item[1].map(function (dataItem) {
return {
name: dataItem[0].name,
value: chinaGeoCoordMap[dataItem[0].name].concat([
dataItem[0].value
])
}
})
},
//被攻击点
{
type: 'scatter',
coordinateSystem: 'geo',
zlevel: 2,
rippleEffect: {
period: 4,
brushType: 'stroke',
scale: 4
},
label: {
normal: {
show: true,
position: 'right',
//offset:[5, 0],
color: '#0f0',
formatter: '{b}',
textStyle: {
color: '#0f0'
}
},
emphasis: {
show: true,
color: '#f60'
}
},
symbol: 'pin',
symbolSize: 50,
data: [
{
name: item[0],
value: chinaGeoCoordMap[item[0]].concat([10])
}
]
}
)
})
//
let option = {
// 标题
title: {
// left: 'center',
text: '来广路线 From',
textStyle: {
color: '#6d767e'
}
},
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(166, 200, 76, 0.82)',
borderColor: '#FFFFCC',
showDelay: 0,
hideDelay: 0,
enterable: true,
transitionDuration: 0,
extraCssText: 'z-index:100',
formatter: function (params, ticket, callback) {
//根据业务自己拓展要显示的内容
var res = ''
var name = params.name
var value = params.value[params.seriesIndex + 1]
res =
"<span style='color:#fff;'>" + name + '</span><br/>数据:' + value
return res
}
},
backgroundColor: '#fff',
visualMap: {
// 图例值控制(官方叫做视觉映射组件)
min: 0,
max: 1,
calculable: true,
show: false,
color: ['#f44336', '#fc9700', '#ffde00', '#ffde00', '#00eaff'],
textStyle: {
color: '#fff'
}
},
geo: {
map: 'china',
zoom: 1.2,
label: {
emphasis: {
show: false
}
},
roam: true, //是否允许缩放
itemStyle: {
normal: {
color: 'rgba(51, 69, 89, .5)', //地图背景色
borderColor: '#516a89', //省市边界线00fcff 516a89
borderWidth: 1
},
emphasis: {
color: 'rgba(37, 43, 61, .5)' //悬浮背景
}
}
},
series: series
}
myChart.setOption(option)
}
渲染动态地图
// 获取所有学员信息
function init() {
axios({
url: '/student/list'
}).then(res => {
console.log(res)
let data = res.data.data
// 从源数据中提取出饼图数据--这是最终的饼图的数据
let pieArr = []
// 最终的折线图数据
let names = [],
salarys = [],
trueSalarys = []
// 地图数据
let chinaGeoCoordMap = {},
chinaDatas = []
// 1.遍历从接口获取的原始数据
data.forEach(function (value) {
// 2.提取饼图数据.... 开始
// 判断一个pieArr中是否已经有了当前省份的记录,如果有,数量+1,如果没有则在pieArr中添加一条新记录
// value就是当前所遍历到的原始用户信息对象
// 拿当前遍历到的value的province属性值,去pieArr数组中查找,看看能不能找到
let obj = null
for (let i = 0; i < pieArr.length; i++) {
// 说明这个省真的存在过
if (pieArr[i].name == value.province) {
obj = pieArr[i]
break
}
}
// 如果存在过,之前也找到了这个对象,那么就修改数量
if (obj) {
obj.value++
}
// 没有存在过
else {
pieArr.push({ value: 1, name: value.province })
}
// 2.提取饼图数据.... 结束
// 3.提取折线图数据 ----开始
names.push(value.name)
salarys.push(value.salary)
trueSalarys.push(value.truesalary)
// 3.提取折线图数据 ----结束
// 4。提取地图数据----开始
chinaGeoCoordMap[value.province] = [value.jing, value.wei]
// chinaDatas.push([{ name: value.province, value: 0 }])
// { value: 1, name: value.province } >> [{ value: 1, name: value.province }]
chinaDatas = pieArr.map(function (v) {
return [v]
})
// 4。提取地图数据----结束
})
// 调用方法生成动态饼图
createPie(pieArr)
// 调用方法生成折线图
createLine(names, salarys, trueSalarys)
// 调用方法生成地图
mapChart(chinaGeoCoordMap, chinaDatas)
})
}
init()
12、退出
let tuichu = this.document.querySelector('.logout')
// 退出
tuichu.addEventListener('click', function() {
// 清除之前的token值
localStorage.removeItem('token_79')
// 重定向到登陆页
location.href = './login.html'
})
13、响应拦截统一处理401
// 添加响应拦截器,拦截所有的服务器响应,对401进行统一的处理
// 添加响应拦截器
axios.interceptors.response.use(
function(response) {
// 响应没有报错
// 对响应数据做点什么
return response
},
function(error) {
// 响应报错
console.dir(error)
if (error.response.status == 401) {
// toastr.error('用户信息验证失败,请重新登陆')
// setTimeout(() => {
window.parent.location.href = './login.html'
// }, 1000)
}
// 对响应错误做点什么
return Promise.reject(error)
}
)