前端学习04-增删改查(条件、分页)

增删改查,条件查询,分页查询,表单数据校验,传参总结

1. 查询所有学生信息(无参数)

1.1. 前后端字段统一

注意,class这个单词不能用,这是java里面的关键字;

1.2. 注释掉分页器

1.3. StudentView.vue

表格html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<el-table :data=" tableData " stripe style="width: 100%">
<!--
prop给本列取名为date,这个prop与到时候传参名对应
label表示表示页面显示为日期
width设置了显示的宽度
align表示对齐方式,默认是居左,这里设置为中心对齐
-->
<el-table-column prop=" id " label="id" width="100" align="center" />
<el-table-column prop=" name " label="姓名" align="center" />
<el-table-column prop=" age " label="年龄" width="120" align="center" />
<el-table-column prop=" studentClass " label="班级" align="center" />
<el-table-column prop=" phone " label="手机号" align="center" />
<el-table-column label="操作" align="center">
<el-button size="mini">编辑</el-button>
<el-button size="mini" type="danger" plain>删除</el-button>
</el-table-column>
</el-table>

script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script>

import request from '@/utils/request';

// export default是导出一个模块的默认内容,其他文件可以通过import语句来引入并使用这个默认导出的内容。
export default {

// 定义一些数据,供其他组件调用
data() {
return {
input: '',
// 定义表格数据内容声明变量(理解为实体类定义在这)
tableData: []
}
},

//页面加载默认调用created里面的方法,对标onMounted()
created(){
this.getList();
},

// methods方法,里面定义一些方法,供页面上的控件调用(包括调用后台接口都是在这里进行)
methods: {
getList() {
request.get("/student/list").then(res => {
//注意code的类型是数字还是字符串
if (res.code === 200){
// 把后台返回的数据赋值给tableData
// this是因为vue的this指向vue实例,this.tableData是vue实例的tableData属性
this.tableData = res.data.records;
}
console.log(res.data.records);
console.log(res.code);
})
}
}
}
</script>

1.4. 接口总结

在页面部分:我们使用:data绑定我们的表格数据tableData。在表格中使用prop对每个字段进行对应,prop起名要和java返回值类型保持一致。

在请求部分,我们分三步走:

  1. 在data()中return我们的tableData,相当于声明,后面就可以定义使用了(html表格中定义的表格数据是tableData,在发起请求也是把返回值的内容赋值给tableData)。
  2. 在created方法中默认调用getList的方法(created方法对标onMounted,页面加载默认调用),意思是我们加载这个页面你的时候默认会调用methods里面的getList方法。
  3. 在methods里定义getList方法,在里面调用我们之前封装的request对象发送axios请求。然后把嵌在后端返回值里的实体数组对应上我们的表格数据tableData即可(注意后端返回的code类型是数字还是字符串)。

2. 条件查询

武哥视频35:43

思路:我们点击按钮,将输入框中的值作为参数,通过@click绑定传入到我们methods中定义好的方法,然后将查询到的数据绑定到表格数据tableData上。

  1. 将findByNameOrPhone方法通过@click绑定到搜索按钮

  2. 在methods中定义findByNameOrPhone方法

  3. 输入框绑定params对象里面的属性,记得要再data中做初始化

  4. findByNameOrPhone方法中传参数

现在我们可以直接使用findByNameOrPhone方法来代替之前的getList方法。将两个查询列表的方法合二为一,变成一个新的getList方法。

有时候前端不传递参数后端不受影响,是因为后端在设计时考虑到了参数的默认处理方式;而有的时候需要在后端显式声明参数的可选性,这取决于后端接口的设计和业务需求。

前端发送的是params对象,params对象到了会自动拆分开来,匹配到page对象和student对象中。

2.1. Ext.重置按钮

武哥54:15

创建一个按钮,点击按钮的时候先清空input框中的内容,然后调用getList方法进行查询。

  1. 按钮通过@Click绑定reset方法

  2. 在reset方法中重置params里面的值并请求getList方法

    需要有this

3. 分页查询信息

3.1. Ext.注意前后端参数名对应

MybatisPlus中的pageNum是current,页容量pageSize是size。

3.2. 分页器绑定的方法与值

分析前端分页器代码(分析分页器相关绑定的方法和值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 分页器 -->
<!--
align='center'对齐方式居中
@size-change表示每页显示多少条,绑定handleSizeChange方法,改变时触发方法
@current-change表示当前页码,绑定handleCurrentChange方法,改变时触发方法
:current-page表示当前页码
:page-sizes表示每页显示多少条的选项,可以自定义
layout='prev, pager, next, jumper'表示显示页码,上一页,下一页,跳转
:total表示总共有多少页
-->
<div class="block" style="margin-top:15px;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="params.current"
:page-sizes="[5, 10, 15, 20]"
:page-size="params.size"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>

@size-change绑定方法handleSizeChange,我们在methods中定义,当我们切换“每页多少条”的时候会调用handleSizeChange方法;@current-change绑定方法handleCurrentChange,我们在methods中定义,当我们切换“第几页”的时候会调用handleCurrentChange方法,现在我们需要在methods中声明这两个方法。

:current-page代表当前在第几页,绑定params对象中的属性current。

:page-sizes代表每页能有多少条的选项,我们写死就好,”[10, 15, 20, 30]”

:page-size代表当前页有多少条,绑定params对象中的属性size

:total表示总共有多少页,绑定total字段

1和2是分页器根据total和pageSizes自己计算来的,不需要我们进行传值。

那么下面我们定义这些字段:

3.3. 分页器相关的方法编写

首先我们看到返回的result:

getList方法拿到返回的total:

点击current或者size的时候重新发起请求:

注意:这里的handleSizeChange和handleCurrentChange方法是回调函数,回调说明:我们点击完要看多少页,或者想看多少容量之后,默认会把我们点击的参数传到参数列表中,虽然我们上面并没有做任何的传值操作。

这里需要理解一下回调,这里是怎么对应的。

4. 新增、编辑信息

4.1. el-dialog对话框,弹窗

新的组件都放到一个新的div里面

https://element.eleme.cn/#/zh-CN/component/dialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!-- 增加或修改的对话框dialog -->
<div>
<el-dialog :title=addOrUpdateFlag :visible.sync="dialogOpen" width="50%" center>
<el-form :model="form">

<!-- 放在一行里 -->
<el-row style="display: flex;justify-content: center;width: 100%;margin: 0 auto;">
<!-- 定义每一行的宽度 -->
<el-col :span="10">
<el-form-item label="姓名:">
<el-input placeholder="请输入姓名" v-model="form.name" autocomplete="off" clearable="true" />
</el-form-item>
</el-col>
<el-col :span="10" style="margin-left: 10px;">
<el-form-item label="年龄:">
<el-input placeholder="请输入年龄" v-model="form.age" autocomplete="off" clearable="true" />
</el-form-item>
</el-col>
</el-row>

<!-- 放在一行里 -->
<el-row style="display: flex;justify-content: center;width: 100%;margin: 0 auto;">
<!-- 定义每一行的宽度 -->
<el-col :span="10">
<el-form-item label="班级:">
<el-input placeholder="请输入班级" v-model="form.studentClass" autocomplete="off"
clearable="true" />
</el-form-item>
</el-col>
<el-col :span="10" style="margin-left: 10px;">
<el-form-item label="手机号:">
<el-input placeholder="请输入手机号" v-model="form.phone" autocomplete="off"
clearable="true" />
</el-form-item>
</el-col>
</el-row>

<!-- 放在一行里 -->
<el-row style="display: flex;justify-content: center;width: 100%;margin: 0 auto;">
<!-- 定义每一行的宽度 -->
<el-col :span="20">
<el-form-item label="备注:">
<el-input :rows="3" placeholder="请输入备注" v-model="form.remark" autocomplete="off"
type="textarea" clearable="true" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogOpen = false">取 消</el-button>
<el-button type="primary" @click="this.addOrUpdate">确 定</el-button>
</div>
</el-dialog>

</div>

4.2. 对话框是否显示

  1. 对话框的显示状态初始值定义

  2. 额外封装方法,供其他方法操作对话框的启动或关闭

  • 封装js代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //打开增加更新对话框
    openDialog(row) {

    //重置form表单属性
    this.form = {};

    this.dialogOpen = true;

    //对表单中的数据进行判断,如果有id就是更新操作,没有id就是添加操作
    if (row.id) {
    this.addOrUpdateFlag = '更新信息';
    //将表格中的数据转移到表单中
    this.form = {...row};
    } else {
    this.addOrUpdateFlag = '添加信息';
    }
    },

    //关闭增加更新对话框
    closeDialog(evt) {
    this.dialogOpen = false;

    //解决按钮点击完之后,按钮不取消聚焦问题
    let target = evt.target;
    if (target.nodeName == "SPAN") {
    target = evt.target.parentNode;
    }
    target.blur();
    },

4.3. 表单属性定义与匹配

我们定义的form对应form标签

1
2
3
4
5
6
7
8
9
//添加或修改表单
form: {
id: '',
name: '',
age: '',
studentClass: '',
phone: '',
remark: '',
},

4.4. 关于新增与更新表单的区别

  1. 标题名称

  2. 设置某些字段在更新中显示

4.5. 添加,更新请求

  1. 按钮对应

  2. 发起请求

  • js请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    //添加或更新方法
    addOrUpdate(evt) {

    //判断是添加还是更新
    if (this.form.id) {
    //更新
    request.post('/student/update', this.form).then(res => {
    if (res.code === 200) {
    this.alertMessage(res.msg, 'success');
    this.getList();
    this.closeDialog();
    } else {
    this.alertMessage(res.msg, 'error');
    }
    }).catch(error => {
    console.log("error:" + error);
    }
    );
    } else {
    //添加
    request.post('/student/add', this.form).then(res => {
    if (res.code === 200) {
    this.alertMessage(res.msg, 'success');
    this.getList();
    this.closeDialog();
    } else {
    this.alertMessage(res.msg, 'error');
    }
    }).catch(error => {
    console.log("error:" + error);
    }
    );
    }

    //解决按钮点击完之后,按钮不取消聚焦问题
    let target = evt.target;
    if (target.nodeName == "SPAN") {
    target = evt.target.parentNode;
    }
    target.blur();
    this.closeDialog();
    },

5. 删除信息

5.1. 页面对应

5.2. 删除请求

  • 详细请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 根据id删除数据的方法
    deleteById(id) {
    // 弹出确认删除提示框
    this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
    }).then(() => {
    console.log("id:" + id);
    // 用户点击确定按钮时执行的操作
    // 发起删除请求
    request.delete(`/student/${id}`)
    .then(res => {
    // 处理成功响应
    if (res.code === 200) {
    this.alertMessage(res.msg, 'success');
    this.getList();
    } else {
    this.alertMessage(res.msg, 'error');
    }
    })
    .catch(error => {
    console.log("error:" + error);
    });
    }).catch((error) => {
    // 用户点击取消按钮时执行的操作
    // 显示已取消删除的提示消息
    this.$message({
    type: 'info',
    message: error
    });
    });
    },

6. 传参详解

6.1. get方法向url传参

  1. 模板字符串拼接
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const param1 = 'value1';
    const param2 = 'value2';
    const url = `/api/data?param1=${param1}&param2=${param2}`;

    request.get(url)
    .then(response => {
    // 处理响应
    })
    .catch(error => {
    // 处理错误
    });
  2. 普通字符串拼接
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const params = new URLSearchParams();
    params.append('param1', 'value1');
    params.append('param2', 'value2');

    request.get('/api/data?' + params.toString())
    .then(response => {
    // 处理响应
    })
    .catch(error => {
    // 处理错误
    });
  3. 传递params
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    request.get('/api/data', {
    params: {
    param1: 'value1',
    param2: 'value2'
    }
    }).then(response => {
    // 处理响应
    }).catch(error => {
    // 处理错误
    });
  4. pathVariable-字符串拼接
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    methods: {
    fetchData() {
    const userId = 123; // 假设 userId 是你要传递的参数
    const url = 'https://example.com/api/user/' + userId;

    // 发起 GET 请求
    request.get(url)
    .then(response => {
    // 处理响应数据
    })
    .catch(error => {
    // 处理错误
    });
    }
    }

6.2. post方法向body传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
addOrUpdate() {
//添加
request.post('/student/add', this.form).then(res => {
if (res.code === 200) {
this.alertMessage(res.msg, 'success');
this.getList();
this.closeDialog();
} else {
this.alertMessage(res.msg, 'error');
}
}).catch(error => {
console.log("error:" + error);
}
);

6.3. delete+Pathvriable传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 根据id删除数据的方法
deleteById(id) {
request.delete(`/student/${id}`)
.then(res => {
// 处理成功响应
if (res.code === 200) {
this.alertMessage(res.msg, 'success');
this.getList();
} else {
this.alertMessage(res.msg, 'error');
}
})
.catch(error => {
console.log("error:" + error);
});
}

6.4. post方法向url+body传参

params参数拼接在url上面,表单数据在url逗号后面进行传递

https://blog.csdn.net/qq_71214810/article/details/134565037

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//请求列表方法
getList(evt) {
// 将params参数拼接在URL中
const url = `/student/list?${new URLSearchParams(this.pageParams).toString()}`;

//发起请求,selectForm定义的东西放在
request.post(url, this.selectForm).then(res => {
//注意code的类型是数字还是字符串
if (res.code === 200) {
// 把后台返回的数据赋值给tableData
// this是因为vue的this指向vue实例,this.tableData是vue实例的tableData属性
this.tableData = res.data.records;
this.total = res.data.total;
} else {
// 如果状态码不是 200,则显示 Element UI 的弹窗警告
//res即是我们定义的result
this.alertMessage(res.msg, 'error');
// 返回一个 Promise.reject,以便在调用端捕获错误
return Promise.reject(res);
}
}).catch(error => { //请求失败打印错误信息并弹窗
console.log("error:" + error);
//this.alertMessage("未知错误!",'error');
})
},

7. 额外总结

7.1. Ext关于模板、插槽与传值

  1. 页面上将行内定义一个template,设置scope,这样能让调用的方法传参对应到每一行
  2. 注意这里的不能直接调用this

更新的参数对应

删除方法中参数对应

7.2. Ext关于this关键字

在Vue中,this的上下文在不同的场景下会发生变化。为了避免this失效,可以使用箭头函数、bind方法或保存this引用。这样可以确保在不同的上下文中正确地指向Vue实例。

  • 我们默认this会指向vue实例
  • el-table中的单条数据调用方法的时候,不能写this.方法名。因为 this 可能会指向 el-table 的上下文而不是 Vue 实例。
  • 在方法中调用其他方法,需要使用this关键字
  • 在模板中不推荐使用this关键字,Vue.js 通过其模板编译过程自动绑定了所有必需的数据和方法,所以你可以直接在模板中访问它们而无需 this 前缀。

7.3. Ext每一条数据的id是怎么传到delete方法里面的?

答:是通过template+slot+scope.row.id来实现的

理解了,这段代码中通过使用 Vue 的模板语法和作用域插槽来传递每条数据的 ID 到 delete 方法中。让我为你详细解释一下。

  1. 作用域插槽

    在 Vue 中,作用域插槽是一种特殊的插槽,它允许你访问父组件中的数据并在子组件中使用。在这个例子中,很可能是使用了 el-table 组件的作用域插槽,用于渲染表格的每一行数据。

  2. template+slot+scope.row.id

    • template:这是 Vue 模板语法的一部分,用于声明模板块的起始标签。
    • slot:在 Vue 中,插槽(slot)是一种占位符,允许你将任意内容插入到组件中指定的位置。
    • scope.row.id:在作用域插槽中,scope 是一个对象,包含了当前渲染这个插槽的一些信息,比如当前行的数据。scope.row 就是当前行的数据对象,而 .id 则是该数据对象中的一个属性,很可能是这条数据的唯一标识符(ID)。
  3. 传递到 delete 方法中

    当点击删除按钮时,很可能是通过事件处理器调用了 delete 方法,并将当前行的 ID 作为参数传递给了该方法。在 Vue 中,事件处理器可以通过 @click 指令来绑定到 HTML 元素上,并通过方法调用来触发。

所以,总的来说,通过作用域插槽将每一条数据的 ID 传递到 delete 方法中,使得在删除操作时可以准确地操作对应的数据。

7.4. Ext对象属性值copy

点击按钮的时候,需要我们将scope里面的row的属性复制到form中,这样表单就能显示我们已经有的数据了

7.5. Ext每次打开对话框之前置空

如果不置空会出现问题,比如我们打开一个信息的编辑页,form中就已经有值了,但是再次打开新增,表单中还是会有值(添加完一个数据之后,表单中的值会自动清空的)

7.6. 按钮点击完不取消聚焦问题

把下面这段代码放在按钮@click的方法对应的方法中的最后。

1
2
3
4
5
6
//解决按钮点击完之后,按钮不取消聚焦问题
let target = evt.target;
if(target.nodeName == "SPAN"){
target = evt.target.parentNode;
}
target.blur();

7.7. 前端的中间弹窗和上面弹窗记录

消息提示Message

https://element.eleme.cn/#/zh-CN/component/message
文档中展示的是$版本,可以直接在方法中使用,另外还有调用对象的方式来进行消息提示。

代码如下(但是推荐使用文档版本)

1
2
3
4
5
6
7
8
import { Message } from 'element-ui';
//自定义设置弹出信息框方法
alertMessage(msg, type) {
Message({
message: msg,
type: type,
});
},

弹框MessageBox

https://element.eleme.cn/#/zh-CN/component/message-box

弹框是纯放在方法代码里面就行,可以用在删除之前进行弹框确认,示范代码如下:

不用再then里面传参数,then的箭头右边直接共享方法传进来的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 根据id删除数据的方法
deleteById(id) {
// 弹出确认删除提示框
this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
console.log("id:" + id);
// 用户点击确定按钮时执行的操作
// 发起删除请求
request.delete(`/student/${id}`)
.then(res => {
// 处理成功响应
if (res.code === 200) {
this.alertMessage(res.msg, 'success');
this.getList();
} else {
this.alertMessage(res.msg, 'error');
}
})
.catch(error => {
console.log("error:" + error);
});
}).catch((error) => {
// 用户点击取消按钮时执行的操作
// 显示已取消删除的提示消息
this.$message({
type: 'info',
message: error
});
});
},

7.8. 关于true-false属性值问题

以dialog标签的title和show-close属性举例。

1
2
3
<div>
<el-dialog :title=addOrUpdateFlag :visible.sync="dialogOpen"
:before-close="dialogBeforeClose" width="50%" :show-close="false" center>
  1. 无论是否有额外定义属性值,我们前面都要加上冒号声明绑定。这个绑定并不是纯为了传参用的,而是为了让值与标签属性绑定上。

7.9. 小知识点

  1. 无参数情况,可以省略括号
  2. 定义属性和方法,后面可以直接加逗号
  3. !important高优先级

7.10. 插槽自定义属性

title和content本来是el-page-header的属性,通过插槽可以进行向下的高级自定义

1
2
3
4
5
6
7
8
<el-page-header>
<template #title>
<span style="color:#fff; font-size: 15px;">主页</span>
</template>
<template #content>
<span style="color: #fff; font-size: 16px;">详情页面</span>
</template>
</el-page-header>

8. 校验规则

8.1. 输入框必填校验

8.2. 重新打开输入框,必填校验还在哪里提示

添加dialog的属性,关闭对话框摧毁元素。

设置打开dialog的方法,在最开始设置form为空

8.3. 提交按钮绑定校验

下面这里要注意传参穿的是字符串,要加上引号啊!

8.4. 数字校验

在自定义的基础上添加两点:


前端学习04-增删改查(条件、分页)
http://wahoyu.xyz/2024/02/15/Vue04/
作者
Wahoyu
发布于
2024年2月15日
许可协议