节是建立在上节的基础上,上一节给大家讲了管理后台表格如何展示数据,但是当我们的数据比较多的时候我们就需要做分页处理了。这一节给大家讲解如何实现表格数据的分页显示。
一,定义表格和分页组件
简单说说代码
<html><head> <meta charset="utf-8"> <title>freemarker+bootstrap学习</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- 新 Bootstrap4 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css"></head><body><div class="container-fluid"> <div class="row clearfix"> <div class="col-md-12 column"> <table class="table table-bordered table-condensed table-striped"> <thead> <tr> <th>id</th> <th>姓名</th> <th>微信</th> <th colspan="2">操作</th> </tr> </thead> <tbody> <#list productInfoPage as productInfo> <tr> <td>${productInfo.id}</td> <td>${productInfo.name}</td> <td>${productInfo.wechat}</td> <td> <#if productInfo.id%2 == 0> <a href="#">下架</a> <#else> <a href="">上架</a> </#if> </td> </tr> </#list> </tbody> </table> </div> <#--分页--> <div class="col-md-12 column"> <ul class="pagination "> <#if currentPage lte 1> <li class="disabled "><a class="page-link" href="#">上一页</a></li> <#else> <li> <a class="page-link" href="/pageList?page=${currentPage - 1}&size=${size}">上一页</a> </li> </#if> <#list 1..totalPage as index> <#if currentPage == index> <li class="page-item active "><a class="page-link" href="#">${index}</a> </li> <#else> <li> <a class="page-link" href="/pageList?page=${index}&size=${size}"> ${index}</a> </li> </#if> </#list> <#if currentPage gte totalPage> <li class="disabled "><a class="page-link" href="#">下一页</a></li> <#else> <li> <a class="page-link" href="/pageList?page=${currentPage + 1}&size=${size}">下一页</a> </li> </#if> </ul> </div> </div></div></body></html>
同样这里的数据我们先用模拟数据,后面会用数据库里的数据。
看下面代码可以看出来,我们模拟了6条数据,然后每页显示2条数据。
package com.qcl.demo.controller;import com.qcl.demo.bean.Demo;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.ModelAndView;import java.util.ArrayList;import java.util.List;import java.util.Map;/** * Created by qcl on 2019-04-29 * desc:freemarker学习 */@RestControllerpublic class DemoController { /* * 分页效果的实现 * */ @GetMapping("/pageList") public ModelAndView list(@RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "2") Integer size, Map<String, Object> map) { List<Demo> demoList = new ArrayList<>(4); demoList.add(new Demo(1, "标题1", "编程小石头1", "2501902696")); demoList.add(new Demo(2, "标题2", "编程小石头2", "2501902696")); demoList.add(new Demo(3, "标题3", "编程小石头3", "2501902696")); demoList.add(new Demo(4, "标题4", "编程小石头4", "2501902696")); demoList.add(new Demo(5, "标题5", "编程小石头4", "2501902696")); demoList.add(new Demo(6, "标题6", "编程小石头4", "2501902696")); demoList.add(new Demo(7, "标题7", "编程小石头7", "2501902696")); int start = (page - 1) * 2; int end = start + size; List<Demo> subList = demoList.subList(start, end); int totalPage = (int) Math.ceil(demoList.size() / size); map.put("productInfoPage", subList); map.put("totalPage", totalPage); map.put("currentPage", page); map.put("size", size); return new ModelAndView("demo/list", map); } }
三,启动springboot查看效果。
注意每一页地址栏的url
可以看出,我们第一次访问时,默认显示第一页,url里没有page和size字段。
访问第2页和第3页时,url里就有了page和size。page是显示那一页,size是每页显示多少条数据。
到这里我们就实现的管理后台的分页效果。
我会把10小时实战入门java系列课程录制成视频,如果你看文章不能很好的理解,可以去看下视频
编程小石头,码农一枚,非著名全栈开发人员。分享自己的一些经验,学习心得,希望后来人少走弯路,少填坑。
一个基于 ElementUI 封装的 table 列表页组件,将包含搜索、列表、分页等功能的页面封装成一个组件
•将搜索、列表、分页三者的交互逻辑封装到组件中,节省开发者代码量
•配置化的请求函数,自动发送请求,自动获取请求参数,自动显示 loading 效果
•配置化的表格项,跟 el-table-column 的配置属性类似
•配置化的搜索表单,支持大部分表单元素
•配置化的分页,跟 el-pagination 的配置属性类似
•自定义是否显示搜索和分页
•自定义标题栏和工具栏
•丰富的插槽提供功能扩展
安装
// npm
npm install vue-pro-table
// yarn
yarn add vue-pro-table
引入
该组件依赖 element-ui,需要自行引入
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.use(ElementUI);
// 引入vue-pro-table
import VueProTable from "vue-pro-table";
Vue.use(VueProTable);
<template>
<vue-pro-table title="列表" :request="getList" :columns="columns">
<template #operate="scope">
<el-button size="mini" type="primary">编辑</el-button>
<el-button size="mini" type="danger">删除</el-button>
</template>
</vue-pro-table>
</template>
<script>
import {getUserList} from 'src/api/xxx';
export default {
data() {
// 表格列配置,大部分属性跟el-table-column配置一样
columns: [
{ label: '序号', type: 'index' },
{ label: '名称', prop: 'nickName', width: 180 },
{ label: '邮箱', prop: 'userEmail' },
{
label: '操作',
fixed: 'right',
width: 180,
align: 'center',
tdSlot: 'operate', // 自定义单元格内容的插槽名称
},
],
},
methods: {
// 请求函数
async getList(params) {
// params是从组件接收的,包含分页和搜索字段。
const { data } = await getUserList(params)
// 必须要返回一个对象,包含data数组和total总数
return {
data: data.list,
total: +data.total,
}
},
}
}
</script>
预览效果
默认不包含搜索表单
•request,请求列表数据的函数
组件加载的时候会自动执行 request 函数,并在加载过程中显示 loading 效果
• data: 列表数据的数组
• total:总数,用于分页
•columns 属性的配置,是一个数组
参数 | 说明 | 类型 | 可选值 | 默认值 |
label | 对应 el-table-column 的 label | string | - | - |
type | 对应 el-table-column 的 type | string | selection/index/expand | - |
prop | 对应 el-table-column 的 prop | string | - | - |
width | 对应 el-table-column 的 width | string,number | - | - |
minWidth | 对应 el-table-column 的 min-width | string,number | - | - |
align | 对应 el-table-column 的 align | string | left/center/right | left |
fixed | 对应 el-table-column 的 fixed | string, boolean | true, left, right | - |
sortable | 对应 el-table-column 的 sortable | boolean | false/true | false |
filters | 对应 el-table-column 的 filters | Array[{ text, value }] | - | - |
tdSlot | 单元格要自定义内容时,可以通过此属性配置一个插槽名称,并且是作用域插槽,可以接收 scope 数据 | string | - | - |
labelSlot | 表头要自定义内容时,可以通过此属性配置一个插槽名称,并且是作用域插槽,可以接收 scope 数据 | string | - | - |
•row-key 属性配置
对应 el-table 的 row-key,默认值是'id'
•search 属性的配置,是一个对象
如果不想显示搜索表单,可以不配置 search 或者 search 设置为 false
参数 | 说明 | 类型 | 可选值 | 默认值 |
labelWidth | label 文字长度 | string | - | - |
inputWidth | 表单项长度 | string | - | - |
fields | 表单字段列表,包含 text,select,radio,checkbox,datetime 等类型,所有字段类型配置见下表 | Array[{字段类型}] | - | - |
•fields 列表的字段类型配置
参数 | 说明 | 类型 | 可选值 | 默认值 |
type | 字段类型 | string | text,textarea,select,radio,radio-button,checkbox,checkbox-button,number,date,daterange,datetime,datetimerange | text |
label | label 文本 | string | - | - |
name | 搜索时的提交的参数名称 | string | - | - |
style | 额外的样式 | object | - | - |
defaultValue | 默认值 | - | - | |
options | 当 type 是 select,radio,radio-button,checkbox,checkbox-button 时的枚举选项 | Array[{name, value}] | - | - |
transform | 搜索前对表单数据进行转换,比如表单数据是数组,但是搜索的时候需要传递字符串。它是一个函数,默认参数是字段的 value,需要返回转换后的结果 | function(value) | - | - |
trueNames | 当 type 是 daterange,datetimerange 时,开始时间和结束时间是在一个数组里面,但是搜索时可能需要两个字段,这时就需要把开始时间和结束时间分别赋值给两个字段,这两个字段的名称就是通过 trueNames 配置,它是一个数组,例如:trueNames: ['startTime', 'endTime'] | Array[string] | ||
min | 当 type 是 number 时的最小值 | number | - | - |
max | 当 type 是 number 时的最大值 | number | - | - |
•
pagination 属性的配置,是一个对象
如果不想显示分页,将 pagination 设置为 false
参数 | 说明 | 类型 | 可选值 | 默认值 |
layout | 组件布局 | string | total, sizes, prev, pager, next, jumper | total, sizes, prev, pager, next, jumper |
pageSize | 每页显示条目个数 | number | - | 10 |
pageSizes | 每页显示个数选择器的选项设置 | Array[number] | - | [10, 20, 30, 40, 50, 100] |
表格上方有一个标题栏,标题栏左侧显示一个标题,右侧是一个可自定义的工具栏
•hide-title-bar
是否隐藏标题栏,布尔值
•title
表格标题
•自定义表格标题
提供一个具名插槽title,来自定义标题的内容
•工具栏
工具栏默认是空的,提供一个具名插槽 toolbar,来自定义工具栏的内容
•selectionChange
如果columns中配置了type为selection的列,可以通过该事件得到已选择的行,参数是一个数组
•refresh
配置 ref 属性,可以通过 ref 获取组件后调用组件内部的refresh方法刷新列表
<template>
<vue-pro-table
ref="proTable"
title="用户列表"
:request="getList"
:columns="columns"
:search="searchConfig"
:pagination="paginationConfig"
@selectionChange="handleSelectionChange"
>
<!-- 工具栏 -->
<template #toolbar>
<el-button
type="primary"
icon="el-icon-plus"
@click="$router.push({name: 'userAdd'})"
>创建账号</el-button>
<el-button
type="danger"
icon="el-icon-refresh"
@click="$refs.pageBox.refresh()"
>刷新</el-button>
</template>
<!-- 展开行 -->
<template #expand="{row}">
{{row.userEmail}}
</template>
<!-- 状态 -->
<template #status="{row}">
<el-tag :type="+row.status === 1 ? 'success' : 'info'">{{+row.status === 1 ? '启用' : '停用'}}</el-tag>
</template>
<!-- 表格操作栏 -->
<template #page-operate="{row}">
<el-button
type="text"
@click="$router.push({name: 'userEdit', params: {
id: row.id
}})"
>编辑</el-button>
<el-button
v-if="+row.status === 1"
type="text"
@click="setUserStatus(row, 0)"
>停用</el-button>
<el-button
v-else
type="text"
@click="setUserStatus(row, 1)"
>启用</el-button>
</template>
<!-- 操作栏头部 -->
<template #th-operate>
<el-input />
</template>
</vue-pro-table>
</template>
<script>
import {getUserList} from 'src/api/xxx';
export default {
data() {
// 表格列配置,大部分属性跟el-table-column配置一样
columns: [
{ label: '', type: 'expand', tdSlot: 'expand' },
{ label: '全选', type: 'selection' },
{ label: '序号', type: 'index' },
{ label: '账户名称', prop: 'nickName', sortable: true },
{ label: '账号', prop: 'userEmail', width: 80 },
{
label: '状态',
prop: 'status',
tdSlot: 'status',
filters: [
{ text: '启用', value: 1 },
{ text: '禁用', value: 0 },
],
},
{ label: '创建时间', prop: 'createTime', align: 'right' },
{ label: '最后修改时间', prop: 'updateTime' },
{
label: '操作',
labelSlot: 'th-operate',
fixed: 'right',
width: 180,
align: 'center',
tdSlot: 'page-operate',
},
],
// 搜索配置
searchConfig: {
labelWidth: '90px',
inputWidth: '360px',
fields: [
{
type: 'text',
label: '账户名称',
name: 'nickName',
defaultValue: 'abc',
},
{
type: 'textarea',
label: '描述',
name: 'description',
},
{
label: '状态',
name: 'status',
type: 'select',
defaultValue: 1,
options: [
{
name: '已发布',
value: 1,
},
{
name: '未发布',
value: 0,
},
],
},
{
label: '性别',
name: 'sex',
type: 'radio',
options: [
{
name: '男',
value: 1,
},
{
name: '女',
value: 0,
},
],
},
{
label: '城市',
name: 'city',
type: 'radio-button',
options: [
{
name: '北京',
value: 'bj',
},
{
name: '上海',
value: 'sh',
},
{
name: '广州',
value: 'gz',
},
{
name: '深圳',
value: 'sz',
},
],
},
{
label: '爱好',
name: 'hobby',
type: 'checkbox',
defaultValue: ['吃饭'],
options: [
{
name: '吃饭',
value: '吃饭',
},
{
name: '睡觉',
value: '睡觉',
},
{
name: '打豆豆',
value: '打豆豆',
},
],
transform: (val) => val.join(','),
},
{
label: '水果',
name: 'fruit',
type: 'checkbox-button',
options: [
{
name: '苹果',
value: '苹果',
},
{
name: '香蕉',
value: '香蕉',
},
{
name: '橘子',
value: '橘子',
},
{
name: '葡萄',
value: '葡萄',
},
],
transform: (val) => val.join(','),
},
{
label: '日期',
name: 'date',
type: 'date',
},
{
label: '时间',
name: 'datetime',
type: 'datetime',
defaultValue: '2020-10-10 8:00:00',
},
{
label: '日期范围',
name: 'daterange',
type: 'daterange',
trueNames: ['startDate', 'endDate'],
style: { width: '360px' },
},
{
label: '时间范围',
name: 'datetimerange',
type: 'datetimerange',
trueNames: ['startTime', 'endTime'],
style: { width: '360px' },
defaultValue: ['2020-10-10 9:00:00', '2020-10-11 18:30:00'],
},
{
label: '数量',
name: 'num',
type: 'number',
min: 0,
max: 10,
},
],
},
// 分页配置
paginationConfig: {
layout: 'total, prev, pager, next, sizes', // 分页组件显示哪些功能
pageSize: 5, // 每页条数
pageSizes: [5, 10, 20, 50],
}
},
methods: {
// 请求函数
async getList(params) {
// params是从组件接收的,包含分页和搜索字段。
const { data } = await getUserList(params)
// 必须要返回一个对象,包含data数组和total总数
return {
data: data.list,
total: +data.total,
}
},
// 选择
handleSelectionChange(arr) {
console.log(arr)
},
}
}
</script>
效果:
分页查询则是在页面上将本来很多的数据分段显示,每页显示用户自定义的行数。可提高用户体验度,同时减少一次性加载,内存溢出风险。
每次翻页从数据库中查询数据。
一次性查询所有数据存入内存,翻页从内存中获取数据。
发送请求访问一个带有分页页面的数据,会发现其主要由两部分组成:
结果总数(totalCount/rows)和结果集(data/list)是来源于两条 SQL:
select count(*) from province
# 他有两个参数,一个是起始页的页码,另一个是每页记录数
# start :(currentPage - 1) * pageSize
# pageSize: 前台给予
select * from province limit #{start} ,#{pageSize}
// 分页数据通过这个构造器封装好
public PageResult(int currentPage, int pageSize, int totalCount, List<T> data) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.totalCount = totalCount;
this.data = data;
// 计算总页数(要先算)
this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
// 利用三元运算符来计算上一页,如果已经是第一页的话,那么他就不会有上一页,让他的上一页为第一页,否则就当前页减1为上一页
this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
// 利用三元运算符计算下一页,如果已经是最后一页的话,那么就没有下一页了,就不让他下一页再增加,否则就当前页自增
this.nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;
}
为了能在页面上显示上述的分页效果,那么我们就得在把页面上的每一个数据封装成到某个对象共享给
JSP。
如果我们不进行封装的话,那么这个七个参数要全部在Session域中去取,比较复杂和恶心。
我们一般会把多个需要共享的数据,封装到一个对象,往后就只需要把数据封装到该对象,再共享该对象即可。
package com.qo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
/**
封装结果数据(某一页的数据)
* @author Xiao_Lin
*/
public class PageResult<T> {
// 用户输入的数据
private int currentPage; // 当前页码
private int pageSize; // 每页显示的条数
//SQL执行后的结果
private int totalCount; // 总条数
private List<T> data; // 当前页数据结果集
// 利用程序计算出来的
private int prevPage; // 上一页
private int nextPage; // 下一页
private int totalPage; // 最后一页
// 分页数据通过这个构造器封装好
public PageResult(int currentPage, int pageSize, int totalCount, List<T> data) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.totalCount = totalCount;
this.data = data;
// 计算总页数(要先算)
this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
// 利用三元运算符来计算上一页,如果已经是第一页的话,那么他就不会有上一页,让他的上一页为第一页,否则就当前页减1为上一页
this.prevPage = currentPage - 1 >= 1 ? currentPage - 1 : 1;
// 利用三元运算符计算下一页,如果已经是最后一页的话,那么就没有下一页了,就不让他下一页再增加,否则就当前页自增
this.nextPage = currentPage + 1 <= totalPage ? currentPage + 1 : totalPage;
}
}
// 查询总记录数,传入一个封装好的查询对象,里面的参数有当前页、每页记录数(可不传,尽量传,为模糊查询做基础)
int queryCount(QueryObject qo);
// 查询结果集,传入一个封装好的查询对象,里面封装好的参数有当前页、每页记录数、起始页页码
List<Province> query(QueryObject qo);
PageResult<Province> query(ProvinceQueryObject qo);
package com.service.impl;
/**
* @author Xiao_Lin
* @date 2021/1/22 22:25
*/
public class ProvinceServiceImpl implements ProvinceService {
ProvinceMapper mapper = ConnUtils.getConnection().getMapper(ProvinceMapper.class);
@Override
public PageResult<Province> query(ProvinceQueryObject qo) {
// 获取查询的记录数
int totalCount = mapper.queryCount(qo);
// 如果总记录数为0,那么说明没数据就不用下一步查询了,提高效率。
if (totalCount == 0){
// 返回一个查询结果集,返回当前页、每页记录数、以及一个空的结果集
return new PageResult<Province>(qo.getCurrentPage(),qo.getPageSize(),totalCount,Collections.EMPTY_LIST);
}
// 记录数不为0,查询出一个结果集
List<Province> provinces = mapper.query(qo);
// 返回一个查询结果集,返回当前页、每页记录数、以及结果集
return new PageResult<Province>(qo.getCurrentPage(),qo.getPageSize(),totalCount,provinces);
}
package com.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 封装分页查询需要的两个请求传入的分页参数
* @author Xiao_Lin
* @date 2021/1/22 21:49
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class QueryObject {
private int currentPage = 1; // 当前页码,要跳转到哪一页的页码(需要给默认值)
private int pageSize = 3 ; // 每页显示条数(需要给默认值)
//用于 Limit 子句第一个 ? 取值,起始页页码
public int getStart(){
return (currentPage-1)*pageSize;
}
}
ProvinceService provinceService = new ProvinceServiceImpl();
QueryObject qo = new QueryObject();
PageResult<Province> pageResult = provinceService.query(qo);
System.out.println("当前页:"+pageResult.getCurrentPage());
System.out.println("结果集数据:" + pageResult.getData());
System.out.println("当前页总记录数:" + pageResult.getTotalCount());
System.out.println("条数:" + pageResult.getData().size());
System.out.println("总页数:" + pageResult.getTotalPage());
System.out.println("上一页:" + pageResult.getPrePage());
System.out.println("下一页:" + pageResult.getNextPage());
package com.servlet;
/**
* @author Xiao_Lin
* @date 2021/1/24 10:17
*/
@WebServlet(urlPatterns = "/listall")
public class ProvinceServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ProvinceService provinceService = new ProvinceServiceImpl();
// 创建QueryObject对象
QueryObject qo = new QueryObject();
// 获取参数
String currentPage = req.getParameter("currentPage");
// 利用工具类判断是否传了这个参数
if (StringUtil.hasLength(currentPage)){
// 如果传了就赋值
qo.setCurrentPage(Integer.parseInt(currentPage));
}
String pageSize = req.getParameter("pageSize");
if (StringUtil.hasLength(pageSize)){
qo.setPageSize(Integer.parseInt(pageSize));
}
// 调用业务层方法来处理请求查询某一页数据
PageResult<Province> query = provinceService.query(qo);
// 把数据共享给jsp
req.setAttribute("pageResult", query);
// 控制跳转到 list.jsp 页面
req.getRequestDispatcher("/WEB-INF/views/product/list.jsp").forward(req, resp);
}
}
包含编写 Servlet 及 JSP,Servlet 处理请求,调用业务方法,把查询到数据共享到 JSP 中,展示给用户。操作步骤:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>产品列表</title>
<script type="text/javascript">
window.onload = function () {
var trClzs = document.getElementsByClassName("trClassName");
for(var i = 0; i < trClzs.length; i++){
trClzs[i].onmouseover = function () {
console.log(1);
this.style.backgroundColor = "gray";
}
trClzs[i].onmouseout = function () {
console.log(2);
this.style.backgroundColor = "";
}
}
}
// 分页 JS
function changePageSize() {
document.forms[0].submit();
}
</script>
</head>
<body>
<form action="/product">
<table border="1" cellspacing="0" cellpadding="0" width="80%">
<tr>
<th>编号</th>
<th>名称</th>
<th>简称</th>
</tr>
<c:forEach var="province" items="${pageResult.data}"
varStatus="status">
<tr class="trClassName">
<td>${status.count}</td>
<td>${province.id}</td>
<td>${province.name}</td>
<td>${province.abbr}</td>
<td>
<a href="/listall?cmd=delete&id=${product.id}">删除</a>
<a href="/listall?cmd=input&id=${product.id}">修改</a>
</td>
</tr>
</c:forEach>
<tr align="center">
<td colspan="9">
<a href="/listall?currentPage=1">首页</a>
<a href="/listall?currentPage=${pageResult.prevPage}">上一页
</a>
<a href="/listall?currentPage=${pageResult.nextPage}">下一页
</a>
<a href="/listall?currentPage=${pageResult.totalPage}">尾页
</a>
当前第 ${pageResult.currentPage} / ${pageResult.totalPage} 页
一共 ${pageResult.totalCount} 条数据
跳转到<input type="number" onchange="changePageSize()"
name="currentPage" value="${pageResult.currentPage}" style="width: 60px;">页
每页显示
<select name="pageSize" onchange="changePageSize()">
<option value="3" ${pageResult.pageSize == 3 ?
'selected' : ''}> 3 </option>
<option value="5" ${pageResult.pageSize == 5 ?
'selected' : ''}> 5 </option>
<option value="8" ${pageResult.pageSize == 8 ?
'selected' : ''}> 8 </option>
</select>条数据
</td>
</tr>
</table>
</form>
</body>
</html>
作者:XiaoLin_Java
原文链接:https://juejin.cn/post/6987553953156169742
*请认真填写需求信息,我们会在24小时内与您取得联系。