准备工作
需要的文件我已经打包好了: 链接:https://pan.baidu.com/s/1gTIi2nTHHv3bfcy5khKxgw 提取码:i2al
- 前端页面:可参考此篇文章 Element 入门教程 自行编写,也可以直接使用我准备好的模板
- sql 文件
create database db1 character set utf8;
use db1;
drop table if exists tb_brand;
create table tb_brand
(
id int primary key auto_increment,
brand_name varchar(20),
company_name varchar(20),
ordered int,
description varchar(100),
status int
);
insert into tb_brand (brand_name, company_name, ordered, description, status)
values
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '万物互联', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1),
('格力', '格力电器股份有限公司', 30, '让世界爱上中国造', 1),
('阿里巴巴', '阿里巴巴集团控股有限公司', 10, '买买买', 1),
('腾讯', '腾讯计算机系统有限公司', 50, '玩玩玩', 0),
('百度', '百度在线网络技术公司', 5, '搜搜搜', 0),
('京东', '北京京东世纪贸易有限公司', 40, '就是快', 1)
;
SELECT * FROM tb_brand;
业务 - 查询所有
后端
BrandMapper.java :这里要注意实体类属性名称和数据库表名称不一致,使用 resultMap 映射
@Select("select * from tb_brand")
@ResultMap("brandResultMap")
List<Brand> selectAll();
- service
- service 接口:
BrandService.java package com.ruochen.service;
import com.ruochen.pojo.Brand;
import java.util.List;
public interface BrandService {
List<Brand> selectAll();
}
- service 实现类:
BrandServiceImpl.java package com.ruochen.service.impl;
import com.ruochen.mapper.BrandMapper;
import com.ruochen.pojo.Brand;
import com.ruochen.service.BrandService;
import com.ruochen.util.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import java.util.List;
public class BrandServiceImpl implements BrandService {
SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();
@Override
public List<Brand> selectAll() {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> brands = brandMapper.selectAll();
sqlSession.close();
return brands;
}
}
SelectAllServlet.java package com.ruochen.web.servlet;
import com.alibaba.fastjson.JSON;
import com.ruochen.pojo.Brand;
import com.ruochen.service.BrandService;
import com.ruochen.service.impl.BrandServiceImpl;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.List;
@WebServlet("/selectAllServlet")
public class SelectAllServlet extends HttpServlet {
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Brand> brands = brandService.selectAll();
String jsonString = JSON.toJSONString(brands);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
- 测试
前端
业务 - 新增品牌
后端
BrandMapper.java
@Insert("insert into tb_brand values (null, #{brandName}, #{companyName}, #{ordered}, #{description}, #{status})")
void add(Brand brand);
- service
- service 接口:
BrandService.java
void add(Brand brand);
- service 实现类:
BrandServiceImpl.java @Override
public void add(Brand brand) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.add(brand);
sqlSession.commit();
sqlSession.close();
}
}
AddServlet.java package com.ruochen.web.servlet;
import com.alibaba.fastjson.JSON;
import com.ruochen.pojo.Brand;
import com.ruochen.service.BrandService;
import com.ruochen.service.impl.BrandServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
@WebServlet("/addServlet")
public class AddServlet extends HttpServlet {
private BrandService brandService = new BrandServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader br = request.getReader();
String params = br.readLine();
Brand brand = JSON.parseObject(params, Brand.class);
brandService.add(brand);
response.getWriter().write("success");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
前端
- 我们先将上一个功能中查询所有数据 ajax 请求封装为一个方法,方便调用
selectAll() {
var _this = this;
axios({
method: "get",
url: "http://localhost:8080/brand-case/selectAllServlet"
}).then(function (resp) {
_this.tableData = resp.data;
})
},
- 提交按钮绑定了
addBrand 方法,点击提交按钮时发送 ajax 请求,添加数据(数据由模型 brand 绑定获得)
addBrand() {
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/brand-case/addServlet",
data: _this.brand
}).then(function (resp) {
if (resp.data == 'success') {
_this.dialogVisible = false;
_this.selectAll();
_this.$message({
message: '恭喜你,添加成功',
type: 'success'
});
}
})
},
Servlet 优化
- 查看文章:Javaweb 自定义 Servlet 实现按照访问路径转发
业务 - 批量删除
后端
- Dao 层
BrandMapper.java
void deleteByIds(@Param("ids") int[] ids);
BrandMapper.xml
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
- Service 层
- Service 接口:
BrandService.java
void deleteByIds(int[] ids);
- Service 实现类:
BrandServiceImpl.java @Override
public void deleteByIds(int[] ids) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.deleteByIds(ids);
sqlSession.commit();
sqlSession.close();
}
- Web 层:
BrandServlet.java
public void deleteByIds(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader br = request.getReader();
String params = br.readLine();
int[] ids = JSON.parseObject(params, int[].class);
brandService.deleteByIds(ids);
response.getWriter().write("success");
}
前端
- 首先要给批量删除按钮添加点击事件
<el-button type="danger" plain @click="deleteByIds">批量删除</el-button>
- 在编写
deleteByIds 方法之前,我们要想办法获取到选中的 id,复选框选中后会执行一个 handleSelectionChange 回调方法,我们可以通过该方法中的 this.multipleSelection 获取到选中的对象数组
handleSelectionChange(val) {
this.multipleSelection = val;
},
- 然后我们再写一个模型
multipleSelection 用于存放从 this.multipleSelection 获取到的 id
multipleSelection: [],
- 现在我们编写
deleteByIds 方法
deleteByIds() {
this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
for (let selectionElement of this.multipleSelection) {
this.selectedIds.push(selectionElement.id);
}
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/deleteByIds",
data: _this.selectedIds
}).then(function (resp) {
if (resp.data == 'success') {
_this.selectAll();
_this.$message({
message: '恭喜你,删除成功',
type: 'success'
});
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
业务 - 分页查询
分析
- SQL Limit 查询语句
- 页面传递的参数
- 计算
- 开始索引 = (当前页码 - 1) * 每页显示条数
- 查询的条目数 = 每页显示条数
后端
- 创建 JavaBean:
PageBean.java package com.ruochen.pojo;
import java.util.List;
public class PageBean<T> {
private int totalCount;
private List<T> rows;
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public List<T> getRows() {
return rows;
}
public void setRows(List<T> rows) {
this.rows = rows;
}
}
- Dao 层:
BrandMapper.java
@Select("select * from tb_brand limit #{begin}, #{size}")
@ResultMap("brandResultMap")
List<Brand> selectByPage(@Param("begin") int begin, @Param("size") int size);
@Select("select count(*) from tb_brand")
int selectTotalCount();
- Service 层
- Service 接口类:
BrandService.java
PageBean<Brand> selectByPage(int currentPage, int pageSize);
- Service 实现类:
BrandServiceImpl.java @Override
public PageBean<Brand> selectByPage(int currentPage, int pageSize) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
int begin = (currentPage - 1) * pageSize;
int size = pageSize;
List<Brand> rows = brandMapper.selectByPage(begin, size);
int totalCount = brandMapper.selectTotalCount();
PageBean<Brand> pageBean = new PageBean<>();
pageBean.setRows(rows);
pageBean.setTotalCount(totalCount);
sqlSession.close();
return pageBean;
}
- Web 层:
BrandServlet.java
public void selectByPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
PageBean<Brand> pageBean = brandService.selectByPage(currentPage, pageSize);
String jsonString = JSON.toJSONString(pageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
- 测试
前端
- 设置模型总记录数:
totalCount ,初值设为100,在selectAll方法中赋值 - 当前页码模型 currentPage 初值设置为1
- 每页条数模型 pageSize,默认值为 5
pageSize: 5,
totalCount: 100,
currentPage: 1,
- currentPage 和 pageSize 动态拼接到 selectAll url 中
- selectAll 方法改为查询分页
var _this = this;
axios({
method: "get",
url: "http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage=" + this.currentPage + "&pageSize=" + this.pageSize,
}).then(function (resp) {
_this.tableData = resp.data.rows;
_this.totalCount = resp.data.totalCount;
})
- 分页工具条绑定了
handleCurrentChange 和 handleSizeChange 方法,方法中将值设置到模型中,再调用 selectAll 方法即可
handleSizeChange(val) {
this.pageSize = val;
this.selectAll();
},
handleCurrentChange(val) {
this.currentPage = val;
this.selectAll();
},
业务 - 条件查询
后端
-
Dao 层
@ResultMap("brandResultMap")
List<Brand> selectByPageAndCondition(@Param("begin") int begin, @Param("size") int size, @Param("brand") Brand brand);
int selectTotalCountByCondition(Brand brand);
<select id="selectByPageAndCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="brand.brandName != null and brand.brandName != ''">
and brand_name like #{brand.brandName}
</if>
<if test="brand.companyName != null and brand.companyName != ''">
and company_name like #{brand.companyName}
</if>
<if test="brand.status != null">
and status like #{brand.status}
</if>
</where>
limit #{begin}, #{size}
</select>
<select id="selectTotalCountByCondition" resultType="java.lang.Integer">
select count(*)
from tb_brand
<where>
<if test="brandName != null and brandName != ''">
and brand_name like #{brandName}
</if>
<if test="companyName != null and companyName != ''">
and company_name like #{companyName}
</if>
<if test="status != null">
and status like #{status}
</if>
</where>
</select>
-
Service 层
- Service 接口类:
BrandService.java
PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand);
- Service 实现类:
BrandServiceImpl.java @Override
public PageBean<Brand> selectByPageAndCondition(int currentPage, int pageSize, Brand brand) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
int begin = (currentPage - 1) * pageSize;
int size = pageSize;
String brandName = brand.getBrandName();
if (brandName != null && brandName.length() > 0) {
brand.setBrandName("%" + brandName + "%");
}
String companyName = brand.getCompanyName();
if (companyName != null && companyName.length() > 0) {
brand.setCompanyName("%" + companyName + "%");
}
List<Brand> rows = brandMapper.selectByPageAndCondition(begin, size, brand);
int totalCount = brandMapper.selectTotalCountByCondition(brand);
PageBean<Brand> pageBean = new PageBean<>();
pageBean.setRows(rows);
pageBean.setTotalCount(totalCount);
sqlSession.close();
return pageBean;
}
-
Web 层:BrandServlet.java
public void selectByPageAndCondition(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String _currentPage = request.getParameter("currentPage");
String _pageSize = request.getParameter("pageSize");
int currentPage = Integer.parseInt(_currentPage);
int pageSize = Integer.parseInt(_pageSize);
BufferedReader br = request.getReader();
String params = br.readLine();
Brand brand = JSON.parseObject(params, Brand.class);
PageBean<Brand> pageBean = brandService.selectByPageAndCondition(currentPage, pageSize, brand);
String jsonString = JSON.toJSONString(pageBean);
response.setContentType("text/json;charset=utf-8");
response.getWriter().write(jsonString);
}
前端
- 搜索框绑定模型 brand
<el-form :inline="true" :model="brand" class="demo-form-inline">
- 给按钮绑定单击事件 onSubmit ,调用 selectAll 方法,这里我们就要给后台携带
brand 参数(post 方式),但是我们还需要在 url 中携带 currentPage 和 pageSize 参数(get方式),需要同时传输 post 和 get 数据,所以把请求方式改为 post
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage=" + this.currentPage + "&pageSize=" + this.pageSize,
data: this.brand
}).then(function (resp) {
_this.tableData = resp.data.rows;
_this.totalCount = resp.data.totalCount;
})
onSubmit this.currentPage = 1;
- 最后,测试时我们发现 status 显示的仍然是数字,我们要让其显示
启动/禁用 ,我们在 Brand.java 里面有逻辑视图 getStatusStr ,所以在前端页面表格中当前状态 prpo 取 statusStr 即可<el-table-column
prop="statusStr"
align="center"
label="当前状态">
</el-table-column>
业务 - 删除功能
后端
- Dao 层:
BrandMapper.java
@Delete("delete from tb_brand where id = #{id}")
void deleteById(int id);
- Service 层
- Service 接口类:
BrandService.java
void deleteById(int id);
- Service 实现类:
BrandServiceImpl.java @Override
public void deleteById(int id) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.deleteById(id);
sqlSession.commit();
sqlSession.close();
}
- Web 层:
BrandServlet.java ,这里我们不需要单独编写根据 id 删除的 servlet,直接调用 deleteByIdIds 即可
前端
- 删除操作前端要返回给后端删除数据的 id,这里我们通过
template 参数 scope 可以获取到当前行的数据,然后从数据中取出 id,携带 id 通过 axios 向后端发起请求即可<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-row>
<el-button type="primary">修改</el-button>
<el-button type="danger" @click="deleteById(scope.row)">删除</el-button>
</el-row>
</template>
</el-table-column>
deleteById(row) {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let rowId = [row.id];
axios({
method: "pose",
url: "http://localhost:8080/brand-case/brand/deleteByIds",
data: rowId
}).then(resp => {
if (resp.data == 'success') {
this.selectAll();
this.$message({
message: '恭喜你,删除成功',
type: 'success'
});
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
业务 - 修改功能
后端
- Dao 层:
BrandMapper.java
@Update("update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered}, " +
"description = #{description}, status = #{status} where id = #{id}")
void update(Brand brand);
- Service 层
- Service 接口类:
BrandService.java
void update(Brand brand);
- Service 实现类:
BrandServiceImpl.java @Override
public void update(Brand brand) {
SqlSession sqlSession = factory.openSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
brandMapper.update(brand);
sqlSession.commit();
sqlSession.close();
}
public void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BufferedReader br = request.getReader();
String params = br.readLine();
Brand brand = JSON.parseObject(params, Brand.class);
brandService.update(brand);
response.getWriter().write("success");
}
前端
- 首先,我们要新建一个 dialog 来进行数据回显(不用直接用新增数据的 dialog 的原因是新增 dialog 绑定的模型 brand 在搜索框也有绑定,直接使用会出现乱七八糟的问题),同时绑定模型
updateBrand
<el-dialog
title="更新品牌"
:visible.sync="updateVisible"
width="30%"
>
<el-form ref="form" :model="updateBrand" label-width="80px">
<el-form-item label="品牌名称">
<el-input v-model="updateBrand.brandName"></el-input>
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="updateBrand.companyName"></el-input>
</el-form-item>
<el-form-item label="排序">
<el-input v-model="updateBrand.ordered"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="updateBrand.description"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="updateBrand.status"
active-value="1"
inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="updateByBrand">提交</el-button>
<el-button @click="updateVisible = false">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
- updateBrand 模型 和
updateVisible 定义如下
updateBrand: {
status: '',
brandName: '',
companyName: '',
id: "",
ordered: "",
description: ""
},
updateVisible: false,
- 在修改按钮通过
slot-scope 的 scope.row 获取选中行数据<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-row>
<el-button type="primary" @click="update(scope.row)">修改</el-button>
<el-button type="danger" @click="deleteById(scope.row)">删除</el-button>
</el-row>
</template>
</el-table-column>
- 修改按钮绑定
update 方法,进行数据回显
update(row) {
this.updateBrand = {
status: row.status,
brandName: row.brandName,
companyName: row.companyName,
id: row.id,
ordered: row.ordered,
description: row.description
};
this.updateVisible = true;
},
- update dialog 绑定
updateByBrand 方法,进行更新操作
updateByBrand() {
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/update",
data: this.updateBrand
}).then(resp => {
if (resp.data == 'success') {
this.updateVisible = false;
this.selectAll();
this.$message({
message: '恭喜你,更新成功',
type: 'success'
});
}
})
}
问题:status(switch开关)回显有问题,目前不会实现
前端代码优化
- 最后,我们对 axios 进行一下优化,之前我们的异步请求代码是这样的
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage=" + this.currentPage + "&pageSize=" + this.pageSize,
data: this.brand
}).then(function (resp) {
_this.tableData = resp.data.rows;
_this.totalCount = resp.data.totalCount;
})
- 因为在 axios 中 this 指代的是 axios 而不是 vue,我们每次都需要在 axios 外面先将指代 vue 的 this 赋值给 _this,然后在 axios 中使用 _this 调用 vue 中的模型,这样很不方便
- 我们可以将 axios then 的 function 改为箭头函数,箭头函数中 this 会根据上下文语义进行判断,这样就可以代表 vue 对象,修改如下
axios({
method: "post",
url: "http://localhost:8080/brand-case/brand/selectByPageAndCondition?currentPage=" + this.currentPage + "&pageSize=" + this.pageSize,
data: this.brand
}).then(resp => {
this.tableData = resp.data.rows;
this.totalCount = resp.data.totalCount;
})
最后的成品我也打包放在了网盘,需要可以自行下载 链接:https://pan.baidu.com/s/1Ps3parEeOJtazS-Kv7bWEw 提取码:msjj
觉得本文还不错可以点个赞支持一下 😄
|