初识 Ajax

初识 Ajax,第1张

Ajax 服务器的概念 & 初识 Ajax: 1.服务器相关的基础概念: 一.服务器:

服务器的本质:也是一台电脑。

服务器的作用:

存储一个网站的文件(HTML、CSS、JS、图片、音乐…)提供网站的文件给用户

如何获得服务器:

购买(京东、淘宝……)租赁(阿里云、腾讯云……) 2.资源: 服务器上的 网页(html文件)、图片、音乐、视频、字体文件、CSS文件、JS文件等等都称之为资源。所以资源代指服务器上存储的内容。通俗的讲,我们浏览网页时,从网络当中看到的内容都叫做资源。 3.数据也是资源:

网页中的数据,也是服务器对外提供的一种资源。例如股票数据、各行业排行榜等。

4.数据:

服务器多数情况都使用数据表的方式来存储数据,和我们平时见到的表格差不多

5.客户端:

**概念:**在前端开发中,客户端特指“Web 浏览器”。

**作用:**将互联网世界中的 Web 资源加载、并呈现到浏览器窗口中供用户使用。

常见的“客户端浏览器”:

Chrome 浏览器FireFox 浏览器Edge 浏览器Safari 浏览器 6.URL 地址(统一资源定位符):

生活中的地址,表示地球上的一个确切的地理位置

URL 地址,表示服务器上每个资源的确切位置。

7.URL 地址:

服务器上的每个资源,都对应着独一无二的URL地址

数据也是服务器上的资源,对数据的 *** 作(增删改查),也对应着不同的URL地址:

www.itcbc.com:3006/api/**getbo…** 获取图www.itcbc.com:3006/api/**addbo…** 添加图www.itcbc.com:3006/api/**delbo…** 删除图www.itcbc.com:3006/api/**updat…** 修改图 8.客户端与服务器通信的过程:

客户端与服务器之间的通信过程,分为请求 - 响应两个步骤。其中:

请求的概念:客户端通过网络去找服务器要资源的过程,叫做“请求”响应的概念:服务器把资源通过网络发送给客户端的过程,叫做“响应” 浏览器设置请求地发送请求服务器处做出响应浏览器接收结果并显示 9.什么是 Ajax:

Ajax 是浏览器中的技术:用来实现客户端网页请求服务器的数据。

它的英文全称是 Asynchronous Javascript And XML,简称 Ajax。

结论:网页中 Ajax 的应用场景无处不在,有数据的地方就有 Ajax!

二.请求方式: 1.使用 Ajax 请求数据的 5 种方式:

Ajax中,客户端浏览器在请求服务器上的数据时,根据** *** 作性质(增删改查)**的不同,可以分为以下 5 种常见的 *** 作

POST 向服务器新增数据GET 从服务器获取数据DELETE 删除服务器上的数据PUT 更新服务器上的数据(侧重于完整更新:例如更新用户的完整信息)PATCH 更新服务器上的数据(侧重于部分更新:例如只更新用户的手机号) 三.Ajax 基础用法: 1.axios:

axios(发音:艾克C奥斯) 是前端圈最火的、专注于数据请求的库。

中文官网地址:www.axios-http.cn/

英文官网地址:www.npmjs.com/package/axi…

2.axios 的基础语法:
    <script>
        axios({
            method: '请求的类型',
            url: '请求的URL地址'
        }).then(result => {
            //then用来指定请求成功之后的回调函数
            //形参中的result是请求成功之后的结果
        })
    script>
复制代码
3.基于 axios 发起 GET 请求:
    <script src="./lib/axios.js">script>
    <script>
        axios({
            method: 'get',
            url: ' http://www.itcbc.com:3006/api/getbooks'
        }).then(result => {
            console.log(result);
        })
    script>
复制代码
4.GET 请求的查询参数:

刚才查询回来的是所有图书的列表数据,如果想指定查询的条件,可以通过 params 选项来指定查询的参数:

可以携带多个查询参数

    <script src="./lib/axios.js">script>
    <script>
        axios({
            //指定请求方式为get
            method: 'get',
            //指定请求的URL地址
            url: ' http://www.itcbc.com:3006/api/getbooks',
            //查询参数
            params: {
                id: 1, //表示获取id=1,book为弟弟的图书
                bookname:'弟弟',
            }
        }).then(result => {
            console.log(result);
        })
    script>
复制代码
5.GET案例 图书管理:
    <table>
        <thead>
            <tr>
                <th>编号th>
                <th>书名th>
                <th>创造人th>
                <th>出版社th>
            tr>
        thead>
        <tbody>

        tbody>
    table>
    <script src="./lib/axios.js">script>
    <script>
        // rander(arr)
        axios({
            //指定请求方式为get
            method: 'get',
            //指定请求的URL地址
            url: 'http://www.itcbc.com:3006/api/getbooks',

        }).then(result => {
            const arr = result.data.data
            console.log(arr);
            rander(arr)
        })
        function rander(arr) {
            let html = arr.map((value) =>
                `   
          ${value.id}
          ${value.bookname}
          ${value.author}
          ${value.publisher}
        
      `).join('')
            document.querySelector("tbody").innerHTML = html
        }
    script>
6.基于 axios 发起 POST 请求:

​ 使用 axios 发起 POST 请求时,只需要将 method 属性的值设置为 'POST’ ,URL地址改为 ‘/api**/addbook**’:

    <button>加载一个数据button>
    <script src="./lib/axios.js">script>
    <script>
        const button = document.querySelector('button');
        button.addEventListener('click', function () {
            // 执行 post请求新新增数据
            axios({
                method: 'post', // post
                url: 'http://www.itcbc.com:3006/api/addbook', // url
                // 参数
                data: {
                    // 这个里面 不能乱写 属性名和属性值
                    // 乱写参数,如果后端因为你不按照规范出了错 你前端 也是背锅
                    bookname: '李先生与牛马的爱情故事',
                    author: '李先生',
                    publisher: '黑马', // 想传递一个数字格式 不行 (我只能去对比代码看什么地方可能出错)
                    // publisher: Date.now()+"",// 想传递一个数字格式 不行 (我只能去对比代码看什么地方可能出错)
                },
            }).then((result) => {
                // 去银行 *** 作完毕之后  存钱 取钱  纸条 告诉我们   *** 作成功与否
                console.log(result);
            });
        });
    script>
四.接口相关的基础概念:

使用 Ajax 请求数据时,被请求的 URL 地址,就叫做数据接口(简称:接口或 API 接口)。 同时,每个接口必须有对应的请求方式。

www.itcbc.com:3006/api/getbook… 获取图书列表的接口(GET 请求)http://www. itcbc.com:3006/api/addbook 添加图书的接口(POST 请求) 1.接口组成部分及说明: 接口名称:接口的名称,用来快速区分每个接口的作用。如:登录接口、添加图书接口接口 URL:客户端发起 Ajax 调用此接口时,请求的 URL 地址请求方式:接口的请求方式,如:GET、POST、PUT、DELETE 等请求参数:请求此接口时,需要发送到服务器的查询参数或请求体返回示例:当接口请求成功后,服务器响应回来的数据的基本格式返回参数说明:接口响应结果的详细描述 五.案例 – 图书管理: 1.删除图书:

参照接口文档,发现删除图书,需要使用必填的id参数。

循环遍历数据时,将id值存储到 删除按钮的 自定义属性(data-id)中单击 删除 按钮时,获取自定义属性data-id的值,这个值就是id
    <input type="text" />
    <table>
        <thead>
            <tr>
                <th>idth>
                <th>书名th>
                <th>作者th>
                <th>出版社th>
                <th> *** 作th>
            tr>
        thead>
        <tbody>tbody>
    table>
    <script src="./lib/axios.js">script>
    <script>
        const tbody = document.querySelector('tbody');

        tbody.addEventListener('click', function (event) {
            if (event.target.className === 'del') {

                // 判断用户是否确定删除
                if (!confirm("您确定删除吗")) {
                    // 不删除
                    return; // 不再往下执行代码
                }

                //  获取a标签对应的id 自定义属性
                const { id } = event.target.dataset;
                // 根据接口文档的要求 组装代码
                axios({
                    url: 'http://www.itcbc.com:3006/api/delbook',
                    method: 'delete',
                    params: {
                        id,
                    },
                }).then((result) => {
                    console.log(result);
                    getData(); // 删除成功了 重新显示页面数据
                });
            }
        });
        getData();
        function getData(query = '') {
            axios({
                method: 'get',
                url: 'http://www.itcbc.com:3006/api/getbooks' + query,
                // params:{},
            }).then((result) => {
                console.log(result);
                const arr = result.data.data;
                render(arr);
            });
        }

        function render(arr) {
            let html = arr
                .map(
                    (value) => `
      <tr>
          <td>${value.id}td>
          <td>${value.bookname}td>
          <td>${value.author}td>
          <td>${value.publisher}td>
          <td><a data-id="${value.id}" class="del" href="javascript:;">删除a> td>
        tr>
      `
                )
                .join('');
            document.querySelector('tbody').innerHTML = html;
        }
2.修改图书:
<body>
    <div class="left">
        <input type="text" class="keyword" />
        <table>
            <thead>
                <tr>
                    <th>idth>
                    <th>书名th>
                    <th>作者th>
                    <th>出版社th>
                    <th> *** 作th>
                tr>
            thead>
            <tbody>tbody>
        table>
    div>
    <div class="right">
        <form>
            <h3>编辑h3>
            <input type="text" placeholder="书名" class="bookname" />
            <input type="text" placeholder="作者" class="author" />
            <input type="text" placeholder="出版社" class="publisher" />
            <input type="button" value="编辑" class="btnedit" />
        form>
    div>
    <script src="./lib/axios.js">script>
    <script>
        const tbody = document.querySelector('tbody');
        // 全局变量
        let arr; // 列表数据
        let id; // 被点击的数据的id
        const booknameValue = document.querySelector('.bookname');
        const authorValue = document.querySelector('.author');
        const publisherValue = document.querySelector('.publisher');
        const btnedit = document.querySelector('.btnedit');

        tbody.addEventListener('click', function (event) {
            if (event.target.className === 'editbtn') {
                // 获取a身上的下标
                const { index } = event.target.dataset;
                //  获取到 另外一个方法中的 数组
                console.log(arr[index]);
                // 把对应的数据显示到表单中
                booknameValue.value = arr[index].bookname;
                authorValue.value = arr[index].author;
                publisherValue.value = arr[index].publisher;

                // 获取到被编辑的数据id
                id = arr[index].id;
            }
        });

        btnedit.addEventListener('click', function () {
            // 获取表单的值
            const data = {
                // 如何去获取
                id: id,
                bookname: booknameValue.value,
                author: authorValue.value,
                publisher: publisherValue.value,
                appkey: 'wanshao1234'
            };
            // 发送一个编辑 请求
            axios({
                url: 'http://www.itcbc.com:3006/api/updatebook',
                method: 'put',
                // query - params
                // body  - data
                data,
            }).then((result) => {
                console.log(result);
                getData();
            });
            // 提示编辑成功 ->调用 getData 实现重新获取最新的数据
        });
        getData();
        // 定义一个方法 发送请求 获取数据  渲染页面
        function getData() {
            axios({
                url: 'http://www.itcbc.com:3006/api/getbooks',
                method: 'get',
                params: {
                    appkey: 'wanshao1234',
                },
            }).then((result) => {
                console.log(result);
                arr = result.data.data;
                const html = arr
                    .map((value, index) => {
                        return `
                
                ${value.id}
                ${value.bookname}
                ${value.author}
                ${value.publisher}
                ${index}" href="javascript:;" >编辑
              
                `;
                    })
                    .join('');
                tbody.innerHTML = html;
            });
        }
    script>
六.form 表单 在网页中,表单主要负责数据采集功能三个部分组成,分别是:表单标签、表单域、表单按钮HTML 的 就是表单标签,它是一个“容器”,用来将页面上指定的区域划定为表单区域:表单域提供了采集用户信息的渠道,常见的表单域有:input、textarea、select 等。当表单数据填写完毕后,用户点击表单按钮,会触发表单的提交 *** 作,从而把采集到的数据提交给服务器。 1.图书管理确认框:
     // 判断用户是否确定删除
                if (!confirm("您确定删除吗")) {
                    // 不删除
                    return; // 不再往下执行代码
                }
复制代码
2.快速获取表单 :
    <form class="f1">
      <input value="111" type="text" name="username" />
      <input value="222" type="text" name="password" />
      <input value="333" type="text" name="address" />
      <input value="444" type="text" name="phone" />
      <input type="text" />
      <input type="text" />
      <input type="text" />
      <button type="button">注册数据button>
    form>

    <form class="f2" action="">
      <input type="text" name="nickname" value="aabbcc" />
    form>
    <script>
      // 获取表单1 数据 对象格式
      const obj1 = getForm('.f1');
      // 获取表单2 数据 对象格式
      const obj2 = getForm('.f2');

      console.log(obj1);
      console.log(obj2);

      // query 只能传入 form标签的选择器
      function getForm(query) { // 在定义函数的时候写形参 - 名字都可以随意改 
        const inputs = document.querySelectorAll(query + ' input[name]');
        // const inputs = document.querySelectorAll('.f1 input[name]');
        const obj = {};
        inputs.forEach((dom) => {
          obj[dom.name] = dom.value;
        });

        return obj;
      }
    script>
复制代码
3.通过 Ajax 提交表单数据:

通过 Ajax 提交表单采集到的数据,可以防止表单默认提交行为导致的页面跳转问题,提高用户的体验。

监听表单的 submit 提交事件阻止默认提交行为基于 axios 发起请求指定请求方式、请求地址指定请求体数据
    <script>
        $('form').on('submit', function (e) {
            e.preventDefault()
            axios({
                method: 'POST',
                url: 'http://www.itcbc.com:3006/api/form',
                data: {
                    username: $('[name="username"]').val(),
                    password: $('[name="password"]').val()
                }
            }).then(result => {
                console.log(result.data)
            })
        })
    script>
复制代码
4.jQuery 的 serialize() 函数:

jQuery 的 serialize() 函数能够一次性获取到表单中采集的数据,它的语法格式如下: $(‘表单元素的选择器’).serialize();

    <script>
        $('form').on('submit', function (e) {
            e.preventDefault()
            axios({
                method: 'POST',
                url: 'http://www.itcbc.com:3006/api/form',
                data: $('form').serialize() //$('表单元素的选择器').serialize();
            }).then(result => {
                console.log(result.data)
            })
        })
    script>
复制代码
5.serialize() 函数的使用注意点:

在使用 serialize() 函数快速获取表单数据时,必须为每个表单域添加 name 属性! 例如下面的示例中,只能通过 serialize() 函数获取到密码的值:

    <form>
        
        
        <input type="text" /><br />  
        <input type="password" name="password" /><br />
        <button>提交button>
    form>
    <script>
        $('form').serialize(); // password=123
    script>
复制代码
6.serialize() 函数的其他特点: 该方法是jQuery封装的,使用时必须引入jQuery使用serialize(),各表单域必须有 name 属性使用该方法得到的结果是一个查询字符串结构:name=value&name=value该方法 能够 获取 隐藏域的值该方法不能得到禁用状态的值该方法不能得到文件域中的文件信息,所以不能完成文件上传 七.axios 请求方法的别名 1.请求方法的别名:

在实际开发中,常用的 5 种请求方式分别是: GET、POST、PUT、PATCH、DELETE

为了简化开发者的使用过程,axios 为所有支持的请求方法提供了别名:

axios.get ( url , config )axios.delete ( url , config )axios.post ( url, data , config )axios.put ( url, data , config )axios.patch ( url, data , config

data:表示提交到服务器的数据

config:相当于传递给 axios()的那个对象

2.体验 axios.get() 的用法:

使用 axios.get() 可以方便快捷地发起 GET 请求

    <script>
        // 不带查询参数
        axios.get('http://www.itcbc.com:3006/api/get').then(result => {
            console.log(result);
        })
        // 带查询参数
        axios.get('http://www.itcbc.com:3006/api/get', {
            params: { id: 1 }
        }).then(result => {
            console.log(result);
        })
    script>
复制代码
3.体验 axios.post() 的用法:

使用 axios.post() 可以方便快捷地发起 POST 请求:

    <script>
        // 不带请求体
        axios.post('http://www.itcbc.com:3006/api/post').then(result => {
            console.log(result);
        })
        // 带请求体
        axios.post('http://www.itcbc.com:3006/api/post', {
            username: 'zhangsan',
            password: '123456'
        }).then(result => {
            console.log(result);
        })
    script>
复制代码
八.axios 全局配置 和 拦截器: 1.全局配置请求根路径:

在 url 地址中,协议://域名:端口 对应的部分叫做“请求根路径”

全局配置请求根路径的好处:

提高项目的可维护性。全局配置根路径后,后续所有的请求都可以使用全局配置的根路径

假设:端口号从 3009 变成了 3006:

没有全局配置根路径,则每个请求的URL中的端口号都需要被修改!有全局配置根路径,则只需要修改全局配置即可

基于 axios 提供的固定配置,即可轻松配置请求的根路径。语法格式如下: axios.defaults.baseURL = ‘请求根路径’

    <script>
        // 全局配置根路径
        axios.defaults.baseURL = 'http://www.itcbc.com:3006';
        // 请求接口时,不用写根路径
        axios.get('/api/get').then(result => {
            console.log(result);
        })
        // 请求接口时,不用写根路径
        axios.post('/api/post', { username: 'zhangsan', password: '123456' }).then(result => {
            console.log(result);
        })
    script>
复制代码
2.拦截器:

拦截器(interceptors)用来全局拦截 axios 的每一次请求与响应。 好处:可以把每个请求中,某些重复性的业务代码封装到拦截器中,提高代码的复用性。

    <script>
        // 添加请求拦截器
        axios.interceptors.request.use(function (config) {
            // 在发送请求之前做些什么
            return config;
        }, function (error) {
            // 对请求错误做些什么
            return Promise.reject(error);
        });

        // 添加响应拦截器
        axios.interceptors.response.use(function (response) {
            // 2xx 范围内的状态码都会触发该函数。
            // 对响应数据做点什么
            return response;
        }, function (error) {
            // 超出 2xx 范围的状态码都会触发该函数。
            // 对响应错误做点什么
            return Promise.reject(error);
        });
    script>
复制代码
九.FormData和文件上传: 1.FormData基本使用方法: 假设需要收集 …… 里面的所有表单项的值要求,每个表单元素都具有 name 属性
    <script>
        // 1. 获取 form 标签的 DOM对象
        let form = document.querySelector('form');
        // 2. 实例化 FormData 对象,传入 form
        let fd = new FormData(form);
        // 3. 提交数据
        axios.post('/api/formdata', fd).then(result => {
            console.log(result);
        })
    script>
复制代码

注意事项:提交 FormData 对象,不能使用 GET 方式,具体以接口文档为准

2.FormData的API方法:

在提交数据前,可以使用下列API方法对数据进行查看和修改

    <script>
        append('key', 'value'); --向对象中追加数据
        set('key', 'value'); --修改对象中的数据
        delete ('key'); --从对象中删除数据
        get('key')-- 获取指定key的一项数据
        getAll('key')-- 获取指定key的全部数据
        forEach()-- 遍历对象中的数据
    script>
复制代码
3.FormData和serialize的区别:

共同点:

都需要设置表单各项的name属性。都能快速收集表单数据都能够获取到隐藏域()的值都不能获取禁用状态(disabled)的值

不同点:

FormData属于原生的代码;serialiaze是jQuery封装的方法FormData可以收集文件域()的值,而serialize不能。如果有文件上传,则必须使用FormData。得到的结果的数据类型不一样(知道即可)
    <script>
        $('#iptFile').on('change', function (e) {
            let form = $('form')[0]; // 需要将jQuery对象转成DOM对象
            const fd = new FormData(form);
            // 提交FormData,从而完成文件上传
            axios.post('http://www.itcbc.com:3006/api/formdata', fd).then(result => {
                console.log(result.data);
            })
        })
    script>
复制代码
十.文件域补充: 1.实现文件上传:
    <input type="file" accept="image/*">
    <script>
        const input = document.querySelector("input ")
        // 文件域内容改变的时候,获取文件列表及文件对象
        input.addEventListener("change", function () {
            // 获取某个文件对象,比如第一个文件的文件对象
            const file = this.files[0]
            // 追加到FormData对象中
            const formdata = new FormData()
            formdata.append("avatar", file)
            // axios 提交 fd 到指定的接口,从而实现文件上传
            // axios提交FormData的代码
            axios({
                type: 'post',
                url: 'http://www.itcbc.com:3006/api/formdata',
                data: formdata,
                success(result) {
                    console.log(result);
                }
            })
        })
    script>
复制代码
十一.请求报文 & 响应报文:

客户端与服务器通信的过程是基于请求与响应的。其中:

请求报文规定了客户端以什么格式把数据发送给服务器响应报文规定了服务器以什么格式把数据响应给客户端 1.请求报文 - 格式:

请求报文由请求行(request line)、请求头部( header )、空行 和 请求体 4 个部分组成。

在浏览器中,GET 请求比较特殊,它只有请求头,没有请求体。在浏览器中,POST、PUT、PATCH、DELETE 请求既有请求头,又有请求体。 2.响应报文 - 格式:

响应报文由状态行 、响应头部、空行 和 响应体 4 个部分组成。

3.URL参数: 常用的5种请求方式,都可以在URL后面携带请求参数。由于URL的长度有限制,所以请求参数一般都比较小,比如不能做文件上传

常用的请求参数有两种写法:

/api/xxx?参数=值&参数=值 (这种格式的字符串叫做查询字符串,所以这样的参数叫做查询参数)/api/xxx/值/值 (Restful 风格的接口用这种格式的参数)

第一种格式的参数:(/api/xxx?参数=值&参数=值)

    <script>
        // 直接写成查询字符串,并拼接到url后面
        axios.get('/api/xxx?key=value&key=value')
        // 按照axios的语法写,axios会帮我们转成查询字符串
        axios.get('api/xxx', { params: { key: value, key: value } })
    script>
复制代码

第二种格式的参数:( /api/xxx/值/值)

    <script>
        // 只能自己拼接
        axios.get('/api/xxx/100/zhangsan')
    script>
复制代码
4.请求体:

除GET请求以外,其他4种常用的请求方式,都可以设置请求体。

请求体的大小没有限制,所以可以提交大量的数据

常用的请求体格式有如下三种:

参数=值&参数=值 (查询字符串格式)

    <script>
        // axios.post的第二个参数,直接用查询字符串
        axios.post('/api/xxx', 'key=value&key=value')
    script>
复制代码

‘{ “id”: 1, “name”: “zs” }’ (JSON格式)

    <script>
        // axios.post的第二个参数,使用对象。axios会将它转成JSON格式
        axios.post('/api/xxx', { id: 1, name: 'zs' })
    script>
复制代码

new FormData() (FormData对象格式)

    <script>
        let fd = new FormData();
        // axios.post的第二个参数,直接使用 FormData 对象
        axios.post('/api/formdata', fd)
    script>
复制代码

请求的时候,设置了不同格式的请求体,需要一个对应的请求头

第一种格式:参数=值&参数=值 ===> Content-Type: application/x-www-form-urlencoded第二种格式: ‘{ “id”: 1, “name”: “zs” }’ ===> Content-Type: application/json第三种格式: new FormData() =====> Content-Type: multipart/form-data; xxsd随机字符 5.http 响应状态码:

概念:http 响应状态码(Status Code)由三位数字组成,用来标识响应成功与否的状态。 作用:客户端浏览器根据响应状态码,即可判断出这次 http 请求是成功还是失败了。

状态码状态码描述说明
200OK请求成功。
201Created资源在服务器端已成功创建。
304Not Modified资源在客户端被缓存,响应体中不包含任何资源内容!
400Bad Request客户端的请求方式、或请求参数有误导致的请求失败!
401Unauthorized客户端的用户身份认证未通过,导致的此次请求失败!
404Not Found客户端请求的资源地址错误,导致服务器无法找到资源!
500Internal Server Error服务器内部错误,导致的本次请求失败!
6.http 响应状态码 Vs 业务状态码: 所处的位置不同: 在响应头的状态行中所包含的状态码,或者请求列表中的Status,叫做“响应状态码”在响应体的数据中所包含的状态码(案例中叫做code),叫做“业务状态码” 表示的结果不同: 响应状态码只能表示这次请求的成功与否(成功地失败了)业务状态码用来表示这次业务处理的成功与否 通用性不同: 响应状态码是由 http 协议规定的,具有通用性。每个不同的状态码都有其标准的含义,不能乱用。业务状态码是后端程序员自定义的,不具有通用性。

响应状态码:

2xx 成功。3xx 重定向。4xx 客户端错误。5xx 服务器错误。

业务状态码:

0 表示成功;1 表示失败。10 表示成功;20 表示失败。4xxx 表示成功;9xxx 表示失败

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1324551.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存