HTML+JavaScript+CSS+Ajax 实现信息管理系统
项目背景:
为了管理学生信息,需要搭建一个学生信息管理系统,该系统的功能是:通过Ajax+html+js+css来实现学生数据的动态增加/删除/修改,保证数据的实时更新
项目效果:
如下图所示:
第一张图左上角的学员数量在每次页面刷新时都会从后台获取更新,右上角的+号可以实现增加学员信息的功能,每条学生信息里都可以点击对应的图标实现编辑和删除功能
下面这张图是编辑学生信息的弹框,打开弹框的时候,学生信息会回填到对应的表单里面,修改信息之后,点击保存,能够向服务器发送修改请求,动态修改学生数据.
新增学生信息也是共用这个弹框,只是点击确认的时候会向服务器发送新增请求
项目技术和模块划分:
使用的项目技术主要有异步编程、HTTP请求、DOM操作、事件处理、数据渲染、错误处理、模块化和函数封装.
基于软件开发的开闭原则--"对新增开放,对修改关闭",以及模块化思想,所以将项目划分为三个部分:从服务器获取数据模块,渲染界面模块,事件模块
代码实现
从服务器获取数据模块--操作数据
目标:完成主页学生表的渲染
请求所有学生信息函数
// 1. 向服务器获取学生列表数据
async function getStudentsData() {
try {
let res = await axios({ url: '/students' })
return res // {message:'',data:[{}]}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取学生数据异常,检查网络')
}
}
目标:完成编辑学生信息的功能
获取省/市/区数据申请
// 2.1 获取省 - 无参
async function getProvinceData() {
try {
return await axios({ url: '/api/province' }) // {message:'',list:['','']}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取省份数据异常,检查网络')
}
}
// 2.2 获取市 - 需要传入省名称
async function getCityData(pname) {
try {
return await axios({ url: '/api/city?pname=' + pname }) // {message:'',list:['','']}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取城市数据异常,检查网络')
}
}
// 2.3 区 - - 需要传入省和市名称
async function getAreaData(pname, cname) {
try {
return await axios({ url: `/api/area?pname=${pname}&cname=${cname}` }) // {message:'',list:['','']}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取地区数据异常,检查网络')
}
}
获取学生信息数据申请
// 2.4 根据id获取学生的详情老数据
async function getStudentInfoData(id) {
try {
return await axios({ url: `/students/${id}` }) // {message:'',data:{}}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取学生详情异常,检查网络')
}
}
向服务器提交修改学生信息申请
// 2.5 修改学生的详情数据
async function putStudentInfoData(id, body) {
try {
return await axios({ url: `/students/${id}`, method: 'PUT', data: body }) // {message:'',data:[{},{}']}
} catch (error) {
pxmu.fail(error.response && error.response.data.message || '获取学生详情异常,检查网络')
}
}
渲染界面模块
渲染学生数据到主体页面上
async function renderStudents() {
let { data } = await getStudentsData()
console.log(data)
let str = ''
data.forEach(item => {
str += `
<tr>
<td>${item.name}</td>
<td>${item.age}</td>
<td>${item.gender == 0 ? '男' : '女'}</td>
<td>第${item.group}组</td>
<td>${item.hope_salary}</td>
<td>${item.salary}</td>
<td>${item.province}${item.city}${item.area}</td>
<td>
<a href="javascript:;" class="text-success mr-3"><i class="bi bi-pen" data-id="${item.id}"></i></a>
<a href="javascript:;" class="text-danger"><i class="bi bi-trash"></i></a>
</td>
</tr>
`
})
tbodyBox.innerHTML = str
}
渲染省/市/区到界面上
// 2. 渲染省,市,区三个数据到界面上
const provinceBox = document.querySelector('[name=province]')
async function renderProvince() {
const { list } = await getProvinceData()
let str = ''
list.forEach(name => {
str += `<option value="${name}">${name}</option>`
})
provinceBox.innerHTML = `<option value="">--省份--</option>${str}`
}
const cityBox = document.querySelector('[name=city]')
async function renderCity(pname) {
const { list } = await getCityData(pname)
let str = ''
list.forEach(name => {
str += `<option value="${name}">${name}</option>`
})
cityBox.innerHTML = `<option value="">--城市--</option>${str}`
}
const areaBox = document.querySelector('[name=area]')
async function renderArea(pname, cname) {
const { list } = await getAreaData(pname, cname)
let str = ''
list.forEach(name => {
str += `<option value="${name}">${name}</option>`
})
areaBox.innerHTML = `<option value="">--地区--</option>${str}`
}
事件模块
编辑界面相关的事件
数据回显
function registerClickHanderForTbody() {
tbodyBox.addEventListener('click', async function (e) {
// 这个方法中即可做编辑也可以做删除
// 如何判断现在是点击了编辑按钮
if (e.target.className == 'bi bi-pen') {
// alert('编辑')
studentId = e.target.dataset.id
// alert(e.target.dataset.id)
// 1. 根据id获取学生的老数据
const { data } = await getStudentInfoData(studentId)
// 2. 数据回显到页面
for (key in data) {
if (key == 'gender') {
const genderBox = document.querySelectorAll(`[name=${key}]`)
// console.log(genderBox);
genderBox[data[key]].checked = true
} else {
const box = document.querySelector(`[name=${key}]`)
if (box) {
box.value = data[key]
}
}
}
// 3. 显示用户所在的省,市,区
let { province, city, area } = data
// console.log(province,city,area);
provinceBox.value = province
await renderCity(province)
// 回显城市
cityBox.value = city
await renderArea(province, city)
areaBox.value = area
// 打开模态框
bsModal.show()
}
if (e.target.className == 'bi bi-trash') {
alert('删除')
}
})
}
省/市/区的change事件
// 2. 注册省份和市区select的change事件
provinceBox.addEventListener('change', function (e) {
// alert(e.target.value)
// 1. 获取用户选择的省
let pname = e.target.value
// 2. 调用 renderCity
renderCity(pname)
})
cityBox.addEventListener('change', function (e) {
// alert(e.target.value)
// 1. 获取用户选择的省
let cname = e.target.value
// 2. 调用 renderCity
renderArea(provinceBox.value, cname)
})
提交修改学生信息
const btnSubmitBox = document.querySelector('#submit')
const formBox = document.querySelector('#form')
btnSubmitBox.addEventListener('click', async function (e) {
if (studentId > 0) {
// 收集表单数据
let body = serialize(formBox, { hash: true, empty: true })
body.age = +body.age
body.gender = +body.gender
body.group = +body.group
body.hope_salary = +body.hope_salary
body.salary = +body.salary
console.log(body);
// 调用putStudentInfoData完成数据的修改
await putStudentInfoData(studentId, body)
// 关闭模态框+刷新列表数据
bsModal.hide()
studentId = 0
renderStudents()
} else {
// 新增
}
})
技术难点
省市区的联动
代码逻辑的划分