原生js: AI聊天功能, 仿照chatGPT问答功能

问:

现在我们需要一个ai聊天功能, 接口已经给出:

只要是message就是我们的数据, 是message_end就是结束信息, 其他的我们不需要管.

回答:

我们不使用传统的fetch请求这个接口, 而是使用sse, eventSource去请求,

当我们输入框回车 或者 点击元素, 获取到输入框中用户输入的值, 然后创建用户问题标签,

同时, 检查当前有无eventSource , 没有的话创建eventSource, eventSource要设置,withCredentials:true携带cookie.

当请求接口的时候没有返回值之前, 我们需要创建一个 "正在加载..."的标签, 给用户提示.

在eventSource.onmessage外面创建一个newStr空字段, 

注意: 要在eventSource.onmessage外面创建, 在里面创建会创建很多的newStr,

eventSource.onmessage处理返回的数据,

这里加了一个小功能: 接口返回文字我们的网页跟随, 返回文字增加一行我们就向下滑动,时刻是在最下面, 不需要用户手动下滑.

判断: 当时要是message_end, 就调用eventSource.close关闭连接.


 

问:

eventSource = new EventSource(fullUrl, { withCredentials: true });的{ withCredentials: true }什么呢意思?

回答:

在JavaScript中,`EventSource` 是用于接收服务器推送的一种技术,通常用于实现服务器发送事件(Server-Sent Events,SSE)。在你的代码片段中,`EventSource` 的初始化使用了一个配置对象 `{ withCredentials: true }`。

`withCredentials: true` 的意思是在跨域请求中携带凭据(credentials),比如说在请求中包括 cookies 和 HTTP认证信息(即使是跨域请求)。这对于需要在跨域请求中进行认证或会话管理的情况非常有用。具体来说:

- 如果 `withCredentials` 设置为 `true`,则 `EventSource` 在发送请求时会包括当前页面的 cookies 和 HTTP认证信息(如果存在的话)。
- 如果 `withCredentials` 设置为 `false` 或未指定,默认情况下,跨域请求中不会携带 cookies 和 HTTP认证信息。

使用 `withCredentials: true` 的场景包括需要进行用户身份验证或授权的跨域服务器推送。这确保了在跨域情况下能够正确地传递身份验证信息,使得服务器能够基于用户的会话状态提供个性化的数据或服务。

需要注意的是,为了确保安全性,跨域请求中的 `withCredentials` 功能通常需要服务器端的支持,并且需要服务器配置允许接收包含凭据的请求

问:

我们调用ai聊天接口, 触发一次事件, 会不停的调用这个接口, 请问为什么?

回答:

function handleKeyPress(event) {
        if (event.keyCode === 13) {
            event.preventDefault();

            var input = document.getElementById('chat_input');
            var question = input.value.trim();

            if (question !== '') {
                var dialogueContainer = document.querySelector('.dialogue dl');
                let str = "";

                // 创建用户问题的对话框项
                var dt = document.createElement('dt');
                var dtParagraph = document.createElement('p');
                dtParagraph.textContent = '' + question + '';
                dt.appendChild(dtParagraph);
                dialogueContainer.appendChild(dt);

                const appinfo = getCookie('appinfo');
                var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
                var params = {
                    query: question,
                    unicodeToChinese: 1,
                };

                if (appinfo) {
                    params.appinfo = appinfo;
                }

                var queryString = new URLSearchParams(params).toString();
                var fullUrl = apiUrl + '?' + queryString;

                
                        eventSource = new EventSource(fullUrl, { withCredentials: true });

                    // 创建对话框和段落元素
                    var dd = document.createElement('dd');
                    var ddParagraph = document.createElement('p');
                    dd.appendChild(ddParagraph);
                    dialogueContainer.appendChild(dd);

                    // 初始化段落元素的文本内容
                    var newStr = '';

                    // 处理消息事件
                    eventSource.onmessage = function (event) {
                        var data = JSON.parse(event.data);

这里我们触发handleKeyPress事件后, eventSource = new EventSoutce会一直调用, 所以就一直创建新的连接,

需要怎么改?

function handleKeyPress(event) {
        if (event.keyCode === 13) {
            event.preventDefault();

            var input = document.getElementById('chat_input');
            var question = input.value.trim();

            if (question !== '') {
                var dialogueContainer = document.querySelector('.dialogue dl');
                let str = "";

                // 创建用户问题的对话框项
                var dt = document.createElement('dt');
                var dtParagraph = document.createElement('p');
                dtParagraph.textContent = '' + question + '';
                dt.appendChild(dtParagraph);
                dialogueContainer.appendChild(dt);

                const appinfo = getCookie('appinfo');
                var apiUrl = 'https://xxxx.xxxx.com/xxxx-x/xxxx/xxxx';
                var params = {
                    query: question,
                    unicodeToChinese: 1,
                };

                if (appinfo) {
                    params.appinfo = appinfo;
                }

                var queryString = new URLSearchParams(params).toString();
                var fullUrl = apiUrl + '?' + queryString;

                // 检查是否已有 EventSource 实例
                if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                    eventSource = new EventSource(fullUrl, { withCredentials: true });

我们在创建eventSource = new EventSource之前先进入判断: 当前没有eventSource的情况采取创建连接,否则不创建连接.这样修改后触发一次handleKeyPress事件就创建一个eventSource连接.

其中遇到的问题:

接口返回得文字是一个字或者两个字或者三个字, 我们在页面展示的时候, 总是会按字数的增加递增或者一个字一行:

你好

你好啊

你好啊是

你好啊是啥

x

x

x

xxx

的一个

xx

xx

xx

x

x

xx

xx

x

xx

xx

深度

xx

xxx

xx

上面两种情况不是我们需要的, 

这个情况的原因是:

我们每次循环遍历文字我们都创建了一个p标签, 导致接口返回一个字我们就会重新开启一行, 这是p标签导致的.

聊天函数最终代码:

<!-- 聊天显示 -->
<div class="dialogue">
            <dl>
                <!--<dt>
                    <p>“xxxx”是什么?</p>
                </dt>
                <dd>
                    <p>“xxxx, xxxx数据资讯产品,帮助读者完成资讯获取、背景调查、数据分析和决策制定
                    </p>
                </dd>-->
            </dl>
        </div>
<!-- 聊天输入框 -->
    <div class="customer_service">
        <p>联系客服</p>

        <!-- <input class="chat_input" type="text" placeholder="请输入您想咨询的问题…"> -->
        <input id="chat_input" class="chat_input" type="text" placeholder="请输入您想咨询的问题…"
            onkeydown="handleKeyPress(event)">
    </div>







// 聊天函数
var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口

function handleKeyPress(event) {
    console.log(event, '聊天函数event');
    if (event.keyCode === 13 || event.type === 'click') {
        if (event.keyCode === 13) {
            event.preventDefault();
            event.stopPropagation(); // 阻止事件继续传播
        }

        var input = document.getElementById('chat_input');
        var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

        if (question !== '') {
            var dialogueContainer = document.querySelector('.dialogue dl');
            let str = "";

            // 创建用户问题的对话框项
            var dt = document.createElement('dt');
            var dtParagraph = document.createElement('p');
            dtParagraph.textContent = '' + question + '';
            dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
            dt.appendChild(dtParagraph);
            dialogueContainer.appendChild(dt);

            const appinfo = getCookie('appinfo');
            var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
            var params = {
                query: question,
                unicodeToChinese: 1,
            };

            if (appinfo) {
                params.appinfo = appinfo;
            }

            var queryString = new URLSearchParams(params).toString();
            var fullUrl = apiUrl + '?' + queryString;

            // 检查是否已有 EventSource 实例
            if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                eventSource = new EventSource(fullUrl, { withCredentials: true });


                // 创建对话框和段落元素
                //var dd = document.createElement('dd');
                //var ddParagraph = document.createElement('p');
                //dd.appendChild(ddParagraph);
                //dialogueContainer.appendChild(dd);



                var loadingAnswer = '正在加载...'; // 定义 loading 回答
                // 创建对话框和段落元素 创建 loading 回答的对话框项
                var loadingDt = document.createElement('dd');
                var loadingDtParagraph = document.createElement('p');
                loadingDtParagraph.textContent = loadingAnswer;
                loadingDt.appendChild(loadingDtParagraph);
                dialogueContainer.appendChild(loadingDt);

                // 初始化段落元素的文本内容
                var newStr = '';

                // 处理消息事件
                eventSource.onmessage = function (event) {
                    var data = JSON.parse(event.data);
                    if (data.event === "message") {
                        // 使用每个新字符更新段落元素的文本内容 更新 loading 回答为接口返回的数据
                        newStr += data.answer;
                        loadingDtParagraph.textContent = newStr;


                        // 滚动页面到底部
                        window.scrollTo(0, document.body.scrollHeight);
                        // 如果是第一个字符,显示对话框
                        if (newStr.length === 1) {
                            showDialogue();
                        }

                        // 如果接收到完整的回复,关闭 EventSource 对象
                        if (data.complete) {
                            eventSource.close();
                        }
                    } else if (data.event === "message_end") {
                        eventSource.close();
                    }
                };

                function showDialogue() {
                    // 显示对话框和段落元素样式
                    loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                }
            }

            // 清空输入框
            input.value = '';
        }
    }
}

AI返回数据是markedown类型, 怎么转化:

现在接口中返回的数据是markedown类型的数据:

"xx是否提供xxxx购买?\n“xxx”有连xxxxxxxx,自动续订,可以随时取消续订。我们xxxxxxxx方式。\nxxxxxxxxxxxxxxxx,用户可以xxxxxxxxx选项。\n“xxxxx”是什么?\n“xxxxx是集xxxx、xxxx、xxxxxxxxx,xxxxxxxxxx、xxxx、xxxx和xxxx"

"权益包括且不限于:\n(1)内含“xxx”权限,可畅读xxx和《xxxx》xxxxxxx,xxxxxxxx;\n(2)使用xx数据旗下企业、人物、股票、债券xxxxxxxx;\n(3)xxxxxxxxx;\n(4)完整使用xxxxxx三个专业特色xxx(可单独购买):“xxx”xxxxxxxx、xxxxxxx、xxxxxxxxxx;\n(5)xxxxxxxx《xxxx》xxxxxxxx。"

对这样的数据类型我们需要使用markedown进行转化, 目前是单文件h5页面, 请问怎样转化接口返回得markedown数据?

回答:

<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

在文件中引入cdn, 然后直接使用:

loadingDtParagraph.innerHTML = marked.parse(newStr); // 使用markdown进行渲染 ios的13和14版本会导致无法正常使用

这样我们就可以将返回markedown数据解析完成再页面显示, 

注意: 

ios16以下设备会出现无法执行marked.parse(newStr)这段代码的情况, ios16及以上是没问题的.

我们需要判断, 当ios小于16的时候我们使用markedown-it进行markedown数据的渲染,

问:

解析markedown文本除了marked.parse()方法还有什么方法?

回答:


 

markedown-it的使用方法:

先引入markedown-it的cdn,

<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>

然后直接使用:

// 创建markedown-it
// 初始化 markdown-it
var md = window.markdownit();
loadingDtParagraph.innerHTML = md.render(newStr);

注意: 当我们使用markedown-it的时候, 接口返回数据要是不光有markedown数据, 而且还有<a href="https://xxxxx.com">https://xxxx.com</a>这样的数据, 我们需要将其直接转成html, 这胡思后我们需要在初始化md的时候在括号里加上 {html: true}:

markedown-it最终使用方法:

// 创建markedown-it
// 初始化 markdown-it
var md = window.markdownit({ html: true });

否则ios设备无法将a标签进行转换.

问:

ios手机中使用loadingDtParagraph.innerHTML = newStr;就可以正常显示, 但是使用md.render(newStr);就会导致<a>无法解析成html,请问怎么办? 我现在也需要markedown解析, 需哦一还是需要使用md.render,且a标签无法解析成html的情况之出现在iOS手机中,安卓手机正常?

回答:

聊天函数使用markedown, 兼容ios16以下版本的整体代码:

// 聊天函数
        var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口
		var isProcessing = false; // 标志变量,用于控制是否正在处理请求
        
        
        
        function handleKeyPress(event) {
            console.log(event, '聊天函数event');
            if (event.keyCode === 13 || event.type === 'click') {
                if(event.keyCode === 13){
                   event.preventDefault();
                   event.stopPropagation(); // 阻止事件继续传播
                 }
				if (isProcessing) {
                    return; // 如果正在处理请求,返回,不允许提问
                }

                isProcessing = true; // 设置标志,表示正在处理请求
              
                var input = document.getElementById('chat_input');
                var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

                if (question !== '') {
                    var dialogueContainer = document.querySelector('.dialogue dl');
                    let str = "";

                    // 创建用户问题的对话框项
                    var dt = document.createElement('dt');
                    var dtParagraph = document.createElement('p');
                    dtParagraph.textContent = '' + question + '';
                    dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
                    dt.appendChild(dtParagraph);
                    dialogueContainer.appendChild(dt);

                  
                    // 滚动页面到底部
        			window.scrollTo(0, document.body.scrollHeight);
                  
                    const appinfo = getCookie('appinfo');
                    var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
                    var params = {
                        query: question,
                        unicodeToChinese: 1,
                    };

                    if (appinfo) {
                        params.appinfo = appinfo;
                    }

                    var queryString = new URLSearchParams(params).toString();
                    var fullUrl = apiUrl + '?' + queryString;

                    // 检查是否已有 EventSource 实例
                    if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                        eventSource = new EventSource(fullUrl, { withCredentials: true });
						

                        // 创建对话框和段落元素
                        //var dd = document.createElement('dd');
                        //var ddParagraph = document.createElement('p');
                        //dd.appendChild(ddParagraph);
                        //dialogueContainer.appendChild(dd);
                      	
                      
                      	
                      	var loadingAnswer = '正在回答中,请稍等...'; // 定义 loading 回答
                      	// 创建对话框和段落元素 创建 loading 回答的对话框项
                        var loadingDt = document.createElement('dd');
                        var loadingDtParagraph = document.createElement('p');
                        loadingDtParagraph.textContent = loadingAnswer;
                        loadingDt.appendChild(loadingDtParagraph);
                        dialogueContainer.appendChild(loadingDt);

                        // 初始化段落元素的文本内容
                        var newStr = '';
						
                        // 创建markedown-it
                        // 初始化 markdown-it 开始为小括号里没有东西, 但是现在我们需要对后端返回的标签类型数据解析成html, 所以这里我们设置 html:true, 让他可以解析标签.
        				var md = window.markdownit({ html: true });
                        // 处理消息事件
                        eventSource.onmessage = function (event) {
                            var data = JSON.parse(event.data);
                            if (data.event === "message") {
                                // 使用每个新字符更新段落元素的文本内容 更新 loading 回答为接口返回的数据
                                newStr += data.answer;
                                // loadingDtParagraph.textContent = newStr; // 因为返回的数据中有标签嵌套 我们希望将标签进行转化 所以使用innerHTML
                                
								// loadingDtParagraph.innerHTML = newStr; // 暂时注释, 好用功能
								// loadingDtParagraph.innerHTML = marked.parse(newStr); // 使用markdown进行渲染 ios的13和14版本会导致无法正常使用
                                
                                // 上面的是好用的, 但是ios16以下会有问题, markedown没办法解析, 导致答案出不来, 我们判断一下, ios16以下就不使用markedown 或者 使用另一个markedown解析器: markedown-it来针对ios16以下版本进行markedown文本解析
                                if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && parseInt(navigator.appVersion) < 16) {
                                    // loadingDtParagraph.innerHTML = newStr;
                                    
                                    loadingDtParagraph.innerHTML = md.render(newStr);
                                } else {
                                    loadingDtParagraph.innerHTML = marked.parse(newStr);
                                }
                              
                                
                                // 滚动页面到底部
        						// window.scrollTo(0, document.body.scrollHeight);
                                // 如果是第一个字符,显示对话框
                                if (newStr.length === 1) {
                                    showDialogue();
                                }

                                // 如果接收到完整的回复,关闭 EventSource 对象
                                if (data.complete) {
                                    eventSource.close();
                                    isProcessing = false; // 请求处理完毕,允许新的提问
                                }
                            } else if (data.event === "message_end") {
                                eventSource.close();
                                isProcessing = false; // 请求处理完毕,允许新的提问
                            }
                        };

                        function showDialogue() {
                            // 显示对话框和段落元素样式
                            loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                        }
                    }

                    // 清空输入框
                    input.value = '';
                }
            }
        }

对AI进行询问后, 划到页面最底部:

使用原生js实现满足某个条件或进入某个回调后, 让页面在底部?

回答:

案例:
当键盘收起事件触发后, 希望页面滑到最底端
window.addEventListener('resize', function() {
    if (window.innerHeight === document.documentElement.clientHeight) {
        // 键盘收起,滚动到页面底部
        window.scrollTo(0, document.body.scrollHeight);
    }
});

点击输入框后, 调用xxxx的方法:

问:

回答:

要在输入框中输入内容时调用 `xxxx.keyboardLayout()` 方法,你可以通过 JavaScript 的 `onkeydown` 事件来实现。你可以修改你的 `handleKeyPress` 函数,使其在处理键盘按下事件时调用 `xxxx.keyboardLayout()` 方法。

以下是一个示例代码:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调用方法示例</title>
    <script>
        function handleKeyPress(event) {
            // 调用 xxxx.keyboardLayout() 方法
            xxxx.keyboardLayout();
        }
    </script>
</head>
<body>
    <div class="customer_service">
        <p>联系客服</p>
        <input id="chat_input" class="chat_input" type="text" placeholder="请输入您想咨询的问题…" onkeydown="handleKeyPress(event)">
    </div>
</body>
</html>


在这个示例中,每当用户在输入框中按下键盘时,`handleKeyPress` 函数会被调用,并且 `xxxx.keyboardLayout()` 方法也会被执行。确保 `xxxx` 对象和 `keyboardLayout` 方法在 `handleKeyPress` 函数执行之前已经定义好。

问:

我希望点击输入框就调用这个方法而不是按下键盘才调用?

回答:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调用方法示例</title>
    <script>
        function handleClick() {
            // 调用 xxxx.keyboardLayout() 方法
            xxxx.keyboardLayout();
        }
    </script>
</head>
<body>
    <div class="customer_service">
        <p>联系客服</p>
        <input id="chat_input" class="chat_input" type="text" placeholder="请输入您想咨询的问题…" onclick="handleClick()">
    </div>
</body>
</html>

当AI进行回答过程中, 设置无法再次进行提问:

// 聊天函数
        var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口

        function handleKeyPress(event) {
            console.log(event, '聊天函数event');
            if (event.keyCode === 13 || event.type === 'click') {
                if(event.keyCode === 13){
                   event.preventDefault();
                   event.stopPropagation(); // 阻止事件继续传播
                 }
				
                var input = document.getElementById('chat_input');
                var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

                if (question !== '') {
                    var dialogueContainer = document.querySelector('.dialogue dl');
                    let str = "";

                    // 创建用户问题的对话框项
                    var dt = document.createElement('dt');
                    var dtParagraph = document.createElement('p');
                    dtParagraph.textContent = '' + question + '';
                    dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
                    dt.appendChild(dtParagraph);
                    dialogueContainer.appendChild(dt);

                  
                   
        			window.scrollTo(0, document.body.scrollHeight);
                  
                    const appinfo = getCookie('appinfo');
                    var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
                    var params = {
                        query: question,
                        unicodeToChinese: 1,
                    };

                    if (appinfo) {
                        params.appinfo = appinfo;
                    }

                    var queryString = new URLSearchParams(params).toString();
                    var fullUrl = apiUrl + '?' + queryString;

                   
                    if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                        eventSource = new EventSource(fullUrl, { withCredentials: true });
						

                      
                      
                      	
                      	var loadingAnswer = '正在加载...'; // 定义 loading 回答
                      	
                        var loadingDt = document.createElement('dd');
                        var loadingDtParagraph = document.createElement('p');
                        loadingDtParagraph.textContent = loadingAnswer;
                        loadingDt.appendChild(loadingDtParagraph);
                        dialogueContainer.appendChild(loadingDt);

                       
                        var newStr = '';

                       
                        eventSource.onmessage = function (event) {
                            var data = JSON.parse(event.data);
                            if (data.event === "message") {
                              
                                newStr += data.answer;
                                
								loadingDtParagraph.innerHTML = newStr;
								
                                
                                if (newStr.length === 1) {
                                    showDialogue();
                                }

                                
                                if (data.complete) {
                                    eventSource.close();
                                }
                            } else if (data.event === "message_end") {
                                eventSource.close();
                            }
                        };

                        function showDialogue() {
                            // 显示对话框和段落元素样式
                            loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                        }
                    }

                    // 清空输入框
                    input.value = '';
                }
            }
        }这是我的聊天函数, 现在我希望聊天进行中不能进行另一个问题的提问, 一个问题问答结束在可以进行下个问题的提问

回答:

var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问
var isProcessing = false; // 标志变量,用于控制是否正在处理请求

function handleKeyPress(event) {
    if (event.keyCode === 13 || event.type === 'click') {
        if (event.keyCode === 13) {
            event.preventDefault();
            event.stopPropagation(); // 阻止事件继续传播
        }

        if (isProcessing) {
            return; // 如果正在处理请求,返回,不允许提问
        }

        isProcessing = true; // 设置标志,表示正在处理请求

        var input = document.getElementById('chat_input');
        var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

        if (question !== '') {
            var dialogueContainer = document.querySelector('.dialogue dl');
            let str = "";

            // 创建用户问题的对话框项
            var dt = document.createElement('dt');
            var dtParagraph = document.createElement('p');
            dtParagraph.textContent = question;
            dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
            dt.appendChild(dtParagraph);
            dialogueContainer.appendChild(dt);

            window.scrollTo(0, document.body.scrollHeight);

            const appinfo = getCookie('appinfo');
            var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
            var params = {
                query: question,
                unicodeToChinese: 1,
            };

            if (appinfo) {
                params.appinfo = appinfo;
            }

            var queryString = new URLSearchParams(params).toString();
            var fullUrl = apiUrl + '?' + queryString;

            if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                eventSource = new EventSource(fullUrl, { withCredentials: true });

                var loadingAnswer = '正在加载...'; // 定义 loading 回答

                var loadingDt = document.createElement('dd');
                var loadingDtParagraph = document.createElement('p');
                loadingDtParagraph.textContent = loadingAnswer;
                loadingDt.appendChild(loadingDtParagraph);
                dialogueContainer.appendChild(loadingDt);

                var newStr = '';

                eventSource.onmessage = function (event) {
                    var data = JSON.parse(event.data);
                    if (data.event === "message") {
                        newStr += data.answer;
                        loadingDtParagraph.innerHTML = newStr;

                        if (newStr.length === 1) {
                            showDialogue();
                        }

                        if (data.complete) {
                            eventSource.close();
                            isProcessing = false; // 请求处理完毕,允许新的提问
                        }
                    } else if (data.event === "message_end") {
                        eventSource.close();
                        isProcessing = false; // 请求处理完毕,允许新的提问
                    }
                };

                function showDialogue() {
                    // 显示对话框和段落元素样式
                    loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                }
            }

            // 清空输入框
            input.value = '';
        }
    }
}

AI回答过程中返回上一页, 会导致调用两次这个聊天接口, 设置只调用一次, 且AI记录中answer: "";为空的情况下不显示聊天记录:

问:

// 聊天函数
        var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口
		var isProcessing = false; // 标志变量,用于控制是否正在处理请求
        // 设置 marked 的选项
        marked.setOptions({
            gfm: true,
            breaks: true,
            sanitize: false
        });
        
        function handleKeyPress(event) {
            console.log(event, '聊天函数event');
            if (event.keyCode === 13 || event.type === 'click') {
                if(event.keyCode === 13){
                   event.preventDefault();
                   event.stopPropagation(); // 阻止事件继续传播
                 }
				if (isProcessing) {
                    return; // 如果正在处理请求,返回,不允许提问
                }

                isProcessing = true; // 设置标志,表示正在处理请求
              
                var input = document.getElementById('chat_input');
                var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

                if (question !== '') {
                    var dialogueContainer = document.querySelector('.dialogue dl');
                    let str = "";

                    // 创建用户问题的对话框项
                    var dt = document.createElement('dt');
                    var dtParagraph = document.createElement('p');
                    dtParagraph.textContent = '' + question + '';
                    dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
                    dt.appendChild(dtParagraph);
                    dialogueContainer.appendChild(dt);

                  
                    // 滚动页面到底部
        			window.scrollTo(0, document.body.scrollHeight);
                  
                    const appinfo = getCookie('appinfo');
                    var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
                    var params = {
                        query: question,
                        unicodeToChinese: 1,
                    };

                    if (appinfo) {
                        params.appinfo = appinfo;
                    }

                    var queryString = new URLSearchParams(params).toString();
                    var fullUrl = apiUrl + '?' + queryString;

                    // 检查是否已有 EventSource 实例
                    if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                        eventSource = new EventSource(fullUrl, { withCredentials: true });
						

                        // 创建对话框和段落元素
                        //var dd = document.createElement('dd');
                        //var ddParagraph = document.createElement('p');
                        //dd.appendChild(ddParagraph);
                        //dialogueContainer.appendChild(dd);
                      	
                      
                      	
                      	var loadingAnswer = '正在回答中,请稍等...'; // 定义 loading 回答
                      	// 创建对话框和段落元素 创建 loading 回答的对话框项
                        var loadingDt = document.createElement('dd');
                        var loadingDtParagraph = document.createElement('p');
                        loadingDtParagraph.textContent = loadingAnswer;
                        loadingDt.appendChild(loadingDtParagraph);
                        dialogueContainer.appendChild(loadingDt);

                        // 初始化段落元素的文本内容
                        var newStr = '';

                        // 处理消息事件
                        eventSource.onmessage = function (event) {
                            var data = JSON.parse(event.data);
                            if (data.event === "message") {
                                // 使用每个新字符更新段落元素的文本内容 更新 loading 回答为接口返回的数据
                                newStr += data.answer;
                                // loadingDtParagraph.textContent = newStr; // 因为返回的数据中有标签嵌套 我们希望将标签进行转化 所以使用innerHTML
                                
								// loadingDtParagraph.innerHTML = newStr; // 暂时注释, 好用功能
								loadingDtParagraph.innerHTML = marked.parse(newStr); // 使用markdown进行渲染
                                
                                // 滚动页面到底部
        						// window.scrollTo(0, document.body.scrollHeight);
                                // 如果是第一个字符,显示对话框
                                if (newStr.length === 1) {
                                    showDialogue();
                                }

                                // 如果接收到完整的回复,关闭 EventSource 对象
                                if (data.complete) {
                                    eventSource.close();
                                    isProcessing = false; // 请求处理完毕,允许新的提问
                                }
                            } else if (data.event === "message_end") {
                                eventSource.close();
                                isProcessing = false; // 请求处理完毕,允许新的提问
                            }
                        };

                        function showDialogue() {
                            // 显示对话框和段落元素样式
                            loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                        }
                    }

                    // 清空输入框
                    input.value = '';
                }
            }
        }
当我点击触发聊天函数后, 开始在页面渲染接口数据, 在渲染过程中我点击了返回按钮返回上一个页面, 再次回到这个页面加载之前的聊天记录可以看到请求了两次聊天接口, 为什么?

回答:

var eventSource; // 在函数外部定义 eventSource 变量
var isProcessing = false; // 标志变量,用于控制是否正在处理请求


// 页面卸载时关闭 EventSource
window.addEventListener('beforeunload', function () {
    if (eventSource) {
        eventSource.close();
    }
});

// 清理事件监听器
function setupEventListeners() {
    var input = document.getElementById('chat_input');
    input.removeEventListener('keypress', handleKeyPress); // 移除现有的监听器
    input.addEventListener('keypress', handleKeyPress); // 添加新的监听器
}

function handleKeyPress(event) {
    console.log(event, '聊天函数event');
    if (event.keyCode === 13 || event.type === 'click') {
        if (event.keyCode === 13) {
            event.preventDefault();
            event.stopPropagation(); // 阻止事件继续传播
        }
        
        if (isProcessing) {
            return; // 如果正在处理请求,返回,不允许提问
        }

        isProcessing = true; // 设置标志,表示正在处理请求
        
        var input = document.getElementById('chat_input');
        var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

        if (question !== '') {
            var dialogueContainer = document.querySelector('.dialogue dl');

            // 创建用户问题的对话框项
            var dt = document.createElement('dt');
            var dtParagraph = document.createElement('p');
            dtParagraph.textContent = '' + question + '';
            dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
            dt.appendChild(dtParagraph);
            dialogueContainer.appendChild(dt);

            // 滚动页面到底部
            window.scrollTo(0, document.body.scrollHeight);
            
            const appinfo = getCookie('appinfo');
            var apiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
            var params = {
                query: question,
                unicodeToChinese: 1,
            };

            if (appinfo) {
                params.appinfo = appinfo;
            }

            var queryString = new URLSearchParams(params).toString();
            var fullUrl = apiUrl + '?' + queryString;

            // 检查是否已有 EventSource 实例
            if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                eventSource = new EventSource(fullUrl, { withCredentials: true });

                var loadingAnswer = '正在回答中,请稍等...'; // 定义 loading 回答
                var loadingDt = document.createElement('dd');
                var loadingDtParagraph = document.createElement('p');
                loadingDtParagraph.textContent = loadingAnswer;
                loadingDt.appendChild(loadingDtParagraph);
                dialogueContainer.appendChild(loadingDt);

                var newStr = '';

                //

问:

if(haveNextPage) {
              fetch(historyFullUrl, {
                method: 'GET',
                credentials: 'include',
              })
              .then(response => {
                  if (!response.ok) {
                      throw new Error('Network response was not ok');
                  }
                  return response.json();
              })
              .then(data => {
                  console.log(data.data, 'data.data');

                  haveNextPage = data.data.has_more; // 存储: 是否有下一页数据.
                  console.log(haveNextPage, 'haveNextPage');

                  lastItem = data.data.data[data.data.data.length -1];
                  console.log(lastItem, '最后一条数据');
                  lastId = lastItem.id;
                  console.log(lastId, 'lastId')

                  // 根据 haveNextPage 决定是否继续请求下一页



                  data.data.data.forEach(entry => {
                      console.log(entry.data[0], 'entry.data[0]');
                      // 先创建dd, 要实现创建dt在创建dd就会反过来,导致ai先回答用户再提问
                      var recordDd = document.createElement('dd');
                      var recordDdParagraph = document.createElement('p');
                      // recordDdParagraph.textContent = entry.data[0].answer; // AI回复
                      recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复
                      recordDd.appendChild(recordDdParagraph);
                      dialogueContainerTop.prepend(recordDd); // 插入到顶部

                      // 后创建dt, 
                      var recordDt = document.createElement('dt');
                      var recordDtParagraph = document.createElement('p');
                      recordDtParagraph.textContent = entry.data[0].query; // 用户消息
                      recordDtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;

                      recordDt.appendChild(recordDtParagraph);
                      dialogueContainerTop.prepend(recordDt); // 插入到顶部

                  });
              })
              .catch(error => {
                  console.error('Error fetching history:', error);
              });
            } else {
				console.log('has_more是false, 没有下一页了')
            }
data.data.data.forEach(entry =>回调中我希望先判断一下返回数据的entry.data[0].answer; // AI回复 有没有数据, 要是为空串的话就直接返回

回答:

if (haveNextPage) {
    fetch(historyFullUrl, {
        method: 'GET',
        credentials: 'include',
    })
    .then(response => {
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        return response.json();
    })
    .then(data => {
        console.log(data.data, 'data.data');

        haveNextPage = data.data.has_more; // 存储: 是否有下一页数据.
        console.log(haveNextPage, 'haveNextPage');

        lastItem = data.data.data[data.data.data.length - 1];
        console.log(lastItem, '最后一条数据');
        lastId = lastItem.id;
        console.log(lastId, 'lastId');

        // 根据 haveNextPage 决定是否继续请求下一页
        data.data.data.forEach(entry => {
            // 检查 AI 回复是否为空串
            if (!entry.data[0].answer) {
                console.log('AI 回复为空,跳过该项');
                return; // 如果为空,跳过当前循环
            }

            console.log(entry.data[0], 'entry.data[0]');
            // 先创建 dd, 要实现创建 dt 在创建 dd 就会反过来, 导致 AI 先回答用户再提问
            var recordDd = document.createElement('dd');
            var recordDdParagraph = document.createElement('p');
            recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复
            recordDd.appendChild(recordDdParagraph);
            dialogueContainerTop.prepend(recordDd); // 插入到顶部

            // 后创建 dt,
            var recordDt = document.createElement('dt');
            var recordDtParagraph = document.createElement('p');
            recordDtParagraph.textContent = entry.data[0].query; // 用户消息
            recordDtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;

            recordDt.appendChild(recordDtParagraph);
            dialogueContainerTop.prepend(recordDt); // 插入到顶部
        });
    })
    .catch(error => {
        console.error('Error fetching history:', error);
    });
} else {
    console.log('has_more 是 false, 没有下一页了');
}

样式问题:

问:

回答:

问:

ai返回数据, 使用markedown进行渲染, 但是全局设置了ul,li:none;导致返回数据渲染成li后没有相关的数字1.2.3.,现在请问怎么办?

回答:

/* 全局样式 */
ul, li {
    list-style: none; /* 隐藏所有无序和有序列表 */
}

/* 只针对 .dialogue 内部的有序列表 */
.dialogue ol {
    list-style-type: decimal; /* 显示数字 1, 2, 3... */
    padding-left: 20px; /* 添加左侧内边距以避免文本紧贴边缘 */
}
 

案例代码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试有序列表</title>
    <style>
        /* 全局样式 */
        ul, li {
            list-style: none; /* 隐藏所有无序和有序列表 */
        }

        /* 只针对 .dialogue 内部的有序列表 */
        .dialogue ol {
            padding-left: 20px; /* 添加左侧内边距以避免文本紧贴边缘 */
        }

        /* 针对每个 li 设置样式 */
        .dialogue ol li {
            list-style-type: decimal; /* 显示数字 1, 2, 3... */
        }
    </style>
</head>
<body>
    <div class="dialogue">
        <ol>
            <li>第一项</li>
            <li>第二项</li>
            <li>第三项</li>
        </ol>
    </div>
</body>
</html>


 

新增功能 AI聊天功能下滑显示聊天记录:

let lastId = null; // 设置字段用来存储下方循环遍历的最后一个的id字符串, 请求接口传参需要
        let lastItem; // 存储当前十条数据的最后一条
        let haveNextPage = true; // 默认没有下一页
        // 聊天记录函数
        // 上划触发事件处理函数
        function handleSwipeUp() {
            var dialogueContainerTop = document.querySelector('.dialogue_top dl');
            // dialogueContainerTop.innerHTML = ''; // 清空现有内容

            var historyApiUrl = 'https://xxxx.xxxx.com/xxxx-xxxx/xxxx/xxxx';
            //var historyParams = {
              //  limit: 10, // 获取十条记录
            //};
            //var historyQueryString = new URLSearchParams(historyParams).toString();
            //var historyFullUrl = historyApiUrl + '?' + historyQueryString;
            var historyFullUrl;
            if(lastId !== null) { // 不是第一次请求
              historyFullUrl = historyApiUrl + '?' + new URLSearchParams({
                lastId: lastId // 携带上一次请求的 lastId
              }).toString();
              console.log(historyFullUrl, 'historyFullUrl--有lastId')
            } else { // 是第一次请求数据
              historyFullUrl = historyApiUrl;
              console.log(historyFullUrl, 'historyFullUrl-没有lastId')
            }
          
            if(haveNextPage) {
              fetch(historyFullUrl, {
                method: 'GET',
                credentials: 'include',
              })
              .then(response => {
                  if (!response.ok) {
                      throw new Error('Network response was not ok');
                  }
                  return response.json();
              })
              .then(data => {
                  console.log(data.data, 'data.data');

                  haveNextPage = data.data.has_more; // 存储: 是否有下一页数据.
                  console.log(haveNextPage, 'haveNextPage');

                  lastItem = data.data.data[data.data.data.length -1];
                  console.log(lastItem, '最后一条数据');
                  lastId = lastItem.id;
                  console.log(lastId, 'lastId')

                  // 根据 haveNextPage 决定是否继续请求下一页


				  
                  // 创建markedown-it
                  // 初始化 markdown-it
                  var md = window.markdownit({ html: true });
                  data.data.data.forEach(entry => {
                      console.log(entry.data[0], 'entry.data[0]');
                    
                      // 判断聊天数据中answer时空字符串就不显示
                      // 检查 AI 回复是否为空串
                      if (!entry.data[0].answer) {
                          console.log('AI 回复为空,跳过该项');
                          return; // 如果为空,跳过当前循环
                      }
                      
                      // 先创建dd, 要实现创建dt在创建dd就会反过来,导致ai先回答用户再提问
                      var recordDd = document.createElement('dd');
                      var recordDdParagraph = document.createElement('p');
                    
                      
                      // recordDdParagraph.textContent = entry.data[0].answer; // AI回复
                      
                    
                      // recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复 可以正常使用
                      // recordDdParagraph.innerHTML = marked.parse(entry.data[0].answer); // 使用markdown进行渲染  AI回复
                      
					  // ios16以下版本无法使用markedown, 所以加了一下判断.
                      if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && parseInt(navigator.appVersion) < 16) {
                          // recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复 可以正常使用
                          recordDdParagraph.innerHTML = md.render(entry.data[0].answer);
                      } else {
                          recordDdParagraph.innerHTML = marked.parse(entry.data[0].answer); // 使用markdown进行渲染  AI回复
                      }
                      recordDd.appendChild(recordDdParagraph);
                      dialogueContainerTop.prepend(recordDd); // 插入到顶部

                      // 后创建dt, 
                      var recordDt = document.createElement('dt');
                      var recordDtParagraph = document.createElement('p');
                      recordDtParagraph.textContent = entry.data[0].query; // 用户消息
                      recordDtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;

                      recordDt.appendChild(recordDtParagraph);
                      dialogueContainerTop.prepend(recordDt); // 插入到顶部

                  });
              })
              .catch(error => {
                  console.error('Error fetching history:', error);
              });
            } else {
				console.log('has_more是false, 没有下一页了')
            }
            
        }

        if (isMobileOrTablet()) {
            console.log('手机端');

            if (isMobileOrTablet()) {
                console.log('手机端');

                // 定义一个标志来记录是否已经触发了handleSwipeUp()
                var swipeUpTriggered = false;

                // 记录touchstart时的触摸点位置
                var touchStartY = null;

                // 监听touchstart事件,用于判断用户是否处于顶部并开始下拉
                document.addEventListener('touchstart', function(e) {
                    // 判断是否在页面顶部
                    if (window.scrollY === 0) {
                        // 记录touchstart时的触摸点位置
                        touchStartY = e.touches[0].clientY;
                    }
                });

                // 监听touchmove事件,用于判断是否正在向下滑动
                document.addEventListener('touchmove', function(e) {
                    // 如果touchStartY没有记录值,直接返回
                    if (touchStartY === null) {
                        return;
                    }

                    // 计算当前触摸点的移动距离
                    var touchMoveY = e.touches[0].clientY;

                    // 如果向下滑动(即触摸点向下移动)且handleSwipeUp()未被触发过
                    if (touchMoveY > touchStartY && !swipeUpTriggered) {
                        console.log('长下滑');
                        // 不立即调用handleSwipeUp(),而是等待touchend事件
                        swipeUpTriggered = true; // 标记为已触发
                    }
                });

                // 监听touchend事件,处理滑动结束后的逻辑
                document.addEventListener('touchend', function(e) {
                    // 如果已经触发了下滑操作且handleSwipeUp()未被调用过
                    if (swipeUpTriggered) {
                        handleSwipeUp();
                        swipeUpTriggered = false; // 重置标志
                    }

                    // 重置touchStartY
                    touchStartY = null;
                });
            }


        } else {
            // 网页打开
            // 网页打开
            console.log('电脑端');

            function debounce(func, delay) {
                let timerId;
                return function() {
                    const context = this;
                    const args = arguments;
                    clearTimeout(timerId);
                    timerId = setTimeout(() => {
                        func.apply(context, args);
                    }, delay);
                };
            }



            const debouncedHandleSwipeUp = debounce(handleSwipeUp, 300);

            // 监听页面滚动事件
            window.addEventListener('scroll', function(event) {
                // 检查是否在页面顶部
                if (window.scrollY === 0) {
                    // 如果不是正在执行 scrollToTop 函数,则调用防抖后的 handleSwipeUp 函数
                    if (!isScrollingToTop) {
                        debouncedHandleSwipeUp();
                    }
                }
            });
        }

这样就实现了下滑展示聊天记录的功能了.
 

进入聊天界面显示进入时间:

问:

需要实现进入界面展示当前进入聊天界面的时间, 只展示一次就可以, 不用实时更新.

回答:

// 等待页面加载完成后执行 显示时间
        document.addEventListener('DOMContentLoaded', function() {
            // 获取当前时间
            const now = new Date();
            const timestampElement = document.getElementById('timestamp');
            timestampElement.textContent = now.toLocaleString();
        });

获取cms碎片数据:

问:

当前需要获取cms碎片中的helpInfoClass和helpInfoText数据, 其他的不需要:

<style>
    
</style>

<!--帮助内容 begin-->
<div id="pageFrame">
  <div class="help-banner">
    <h1>很高兴为您服务!</h1>
    <div id="helpNavCon">
      <div class="b">
        <div class="item" style="flex-basis: 50%" id="helpNav_2">
          <img
            src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx/xxxx.png"
            width="19"
            alt="手机碎片接口" />
          <span class="xxxx">使用问题</span>
        </div>
        <div class="item" style="flex-basis: 50%" id="helpNav_2_app">
          <img
            src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx/xxxx.png"
            width="19"
            alt="手机碎片接口" />
          <span class="xxxx">意见反馈</span>
        </div>
        <div class="item" style="flex-basis: 50%" id="helpNav_3">
          <img
            src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx/kefu.png"
            width="19"
            alt="手机碎片接口" />
          <span class="xxxx">人工客服</span>
        </div>
      </div>
    </div>
  </div>
  <div id="helpContent"> </div>
  <div style="padding: 0 15px; margin-top: -15px; padding-bottom: 50px">
    <div class="c">
      <div class="item" style="flex-basis: 50%">
        <a href="xxxx:xxxx@xxxx.com" class="b" tabindex="0">
          <img
            id="help-xxxx"
            src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx/xxxx.png"
            width="19" / alt="手机碎片接口">
          <span>电子邮箱</span>
        </a>
      </div>
      <div class="item" style="flex-basis: 50%">
        <a href="tel:xxxx" class="b" tabindex="0">
          <img
            id="help-xxxx"
            src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx/xxxx.png"
            width="19" / alt="手机碎片接口">
          <span>电话客服</span>
        </a>
      </div>
    </div>
  </div>
</div>

<!-- 以下为用户定义数据部分  开始 -->
<script type="text/javascript">

  //  帮助内容分类
  var helpInfoClass = [
      [1, "常见问题"],
      [2, "订阅问题"],
      [3, "使用问题"],
      [4, "人工客服"]
  ];

  //  编辑修改接口:
  var helpInfoText = [
      
      // [以下为 1, "常见问题"]

      [
          // 本条帮助信息所属类别id
          1,

          // 本条帮助信息标题
          'xxxx的?',

          // 本条帮助信息内容(每个段落单独写一行,内容置于''[英文半角的单引号]中间,行尾以,[英文半角的逗号]结束,最末行的末尾不用加,[英文半角逗号])
          'xxxx:',
          '(1)《xxxx》',
          '(2)xxxx()',
          '(3)“xxxx”的所有内容',
          '',                
          'xxxxxxxx:',
          '“xxxxxxxx”:(3)',
          '',
          '注1:xxxx',
          '注2:xxxxxxxx。'
          
      ],
      [
          1,

          'xxxx?',

          'xxxx:',
          '(1)在手机的xxxxxxxx中,点击下方“我的”-->“xxxx”',
          '(3)进入<a href="http://xxxx.xxxx.com">xxxxxxxx</a>xxxx',
      ],
      [
          1,
          '“xxxxxxxx”是什么?',

          '“xxxxxxxxxxxx,xxxx',
          '(1)xxxx;',
          '(2)使用xxxxxxxx;',
          '(3)xxxx;',
          '',
          '“xxxxxxxx”现推出xxxx版和xxxx版:',
          '“<a href="http://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx.html?id=xxxx&channel=xxxx&channelSource=xxxx">xxxxxxxxxxxx</a>”含上述(1)、(2)、(3)、(4)、(5)权限',
          '',
          '点此可以从<a href="http://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx.html?type=xxxx">“xxxxxxxx”产品列表</a>xxxx,xxxx',
          
      ],
      // [以下为 2, "订阅问题"]

      [
          2,

          'xxxxxxxx?',

         'xxxxxxxx:',
          '(1)《xxxx》',
          '(2)xxxxxxxx)',
          '(3)“xxxxxxxx”的所有内容',
          '(4)“xxxxxxxx”的所有资讯内容',
          '',                
          'xxxxxxxx:',
          '“xxxxxxxx”:(3)',
          '“xxxxxxxx”:(1)(2)(3)',
          '“xxxxxxxx”:(1)(2)(3)(4)',
          '',
          '注1:',
          '注2:xxxx等,'
          
      ],
      [
          2,

          '如何付费订阅?',

          'xxxx:',
          '(1)xxxx',
          '(2)在xxxx首页导航部分点击“订阅”',
          '(3)进入<a href="http://xxxx.xxxx.com">xxxxxxxx</a>xxxx',
          '(4)xxxx'
      ],
      [
          2,
          'xxxx?',

          '海外用户可以在xxxxxxxx的 <a href="http://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx.html?type=xxxx">“xxxx”</a>xxxx。'
      ],
      [
          2,
          'xxxxxxxx?',

          'xxxxxxxx。'
      ],

    // [以下为 3, "使用问题"]

      [
          3,
          'xxxx?',

          'xxxx:',
          '(1)xxxx',
          '(2)xxxx。',
          'xxxx。'
      ],
      [
          3,
          'xxxx和xxxx有什么区别?',

          'xxxx。',
          'xxxx。'
      ],
      [
          3,
          'xxxx?',

          'xxxxxxxx的xxxx,xxxx。请<a href="http://m.xxxx.xxxx.com/m/xxxx/">下载xxxx</a>来浏览xxxx内容。'
      ],

// [以下为 4, "人工客服"]

      [
          4,
          'xxxx?',

          'xxxxxxxx',
          '微信:xxxx',
          '邮箱:<a href="xxxx:xxxx@xxxx.com">xxxx@xxxx.com</a>',
          '电话:xxxx xxxx:?xxxxxxxx'
      ]
  ];
</script>

<!-- 用户定义数据部分  结束 -->

回答:

// 主动请求 xxxx 文档
fetch('https://xxxx.html')
    .then(response => {
        if (!response.ok) {
            throw new Error('网络请求失败');
        }
        return response.text();
    })
    .then(data => {
        // 正则表达式用于匹配 helpInfoClass
        var helpInfoClassRegex = /var helpInfoClass = \[(.*?)\];/s;
        var matchClass = helpInfoClassRegex.exec(data);
        var helpInfoClass = [];

        if (matchClass && matchClass.length > 1) {
            var classData = matchClass[1].trim();
            // 将匹配的字符串转换为数组
            eval('helpInfoClass = [' + classData + '];');
        }

        // 正则表达式用于匹配 helpInfoText
        var helpInfoTextRegex = /var helpInfoText = \[(.*?)\];/s;
        var matchText = helpInfoTextRegex.exec(data);
        var helpInfoText = [];

        if (matchText && matchText.length > 1) {
            var textData = matchText[1].trim();
            // 将匹配的字符串转换为数组
            eval('helpInfoText = [' + textData + '];');
        }

        // 输出提取的数据,或者进行进一步处理
        console.log('helpInfoClass:', helpInfoClass); // tab名称
        console.log('helpInfoText:', helpInfoText);


        // 获取 ul 列表元素, 将写死的tab名称 改为活的数据
        var ulElement = document.querySelector('.AI_question ul');

        // 清空现有的列表项
        ulElement.innerHTML = '';

        // 遍历 helpInfoClass 数组
        helpInfoClass.forEach((item, index) => {
            var itemId = item[0];
            var itemName = item[1];

            // 跳过最后一个人工客服问题
            if (index === helpInfoClass.length - 1) {
                return;
            }

            // 创建 li 元素
            var liElement = document.createElement('li');

            // 将第一个常见问题改为热门问题
            if (index === 0) {
                liElement.textContent = "热门问题";
                liElement.classList.add('cur'); // 添加 cur class
            } else {
                liElement.textContent = itemName;
            }

            // 将 li 元素添加到 ul 元素中
            ulElement.appendChild(liElement);
        });
        ulElement.querySelectorAll('li').forEach((liElement, index) => {
            liElement.addEventListener('click', () => {
                // 清除所有 li 的 'cur' 类
                ulElement.querySelectorAll('li').forEach(item => {
                    item.classList.remove('cur');
                });
                // 添加 'cur' 类到当前点击的 li
                liElement.classList.add('cur');

                var itemId = helpInfoClass[index][0];
                // 获取所有信息展示部分包括sort1,sort2,sort3
                var contentDivs = document.querySelectorAll('.details .content');

                // 隐藏所有内容区域
                contentDivs.forEach(div => {
                    div.style.display = 'none';
                });
                // 定义整个的div包括ul和span"换一批"按钮
                var contentDiv;
                // 定义ul部分不包括span"换一批"按钮
                var contentDivUl;
                if (itemId === 1) {
                    console.log("展示热门问题");
                    contentDiv = document.querySelector('.sort01.content');
                    contentDivUl = contentDiv.querySelector('ul');
                    console.log(contentDiv, "contentDiv1", contentDivUl, 'contentDivUl');
                } else if (itemId === 2) {
                    console.log("展示所有订阅问题");
                    contentDiv = document.querySelector('.sort02.content');
                    contentDivUl = contentDiv.querySelector('ul');
                    console.log(contentDiv, "contentDiv2", contentDivUl, 'contentDivUl');
                } else if (itemId === 3) {
                    console.log("展示所有使用问题");
                    contentDiv = document.querySelector('.sort03.content');
                    contentDivUl = contentDiv.querySelector('ul');
                    console.log(contentDiv, "contentDiv3", contentDivUl, 'contentDivUl');
                } else {
                    console.log("展示所有未知问题");
                    // 这里可以根据需求处理没有匹配到任何内容的情况
                    return;
                }
                // 显示当前选择的内容区域
                contentDiv.style.display = 'block';

                // 清空当前内容区域的所有子元素
                contentDivUl.innerHTML = '';
                console.log(contentDivUl, '清空后的contentDivUl')
                // 计数器,用于限制显示的个数
                let count = 0;

                // 遍历 helpInfoText 找到对应 itemId 的内容并插入到相应的 dl > dt 中
                helpInfoText.forEach(item => {
                    if (count < 5 && item[0] === itemId) {
                        // 给li标题写入文字
                        console.log(item[1], 'item[1]');
                        var dtElement = document.createElement('li');
                        dtElement.textContent = item[1];
                        contentDivUl.appendChild(dtElement);
                        // 增加计数器
                        count++;

                        // 添加点击事件监听器
                        // 绑定点击事件
                        bindClickEvent(dtElement);
                        //dtElement.addEventListener('click', function() {
                        //  console.log(this, 'this');
                        // 获取对应的文本
                        //var clickedText = this.textContent;
                        //console.log(clickedText, 'clickedText----');
                        // 在这里可以进行你想要的操作,比如打印文本或者执行其他逻辑
                        // 调用聊天函数
                        //handleKeyPress({ type: 'click', question: clickedText });
                        //});
                    }
                });
                console.log(contentDivUl, '进入界面后的contentDivUl')
                // 定义一个函数,用来绑定点击事件
                function bindClickEvent(element) {
                    element.addEventListener('click', function (event) {
                        event.stopPropagation(); // 阻止事件继续传播

                        var clickedText = this.textContent;
                        handleKeyPress({ type: 'click', question: clickedText });
                    });
                }






                let typeOne = [];

                let typeTwo = [];

                let typeThree = [];

                // 遍历 helpInfoText 找到对应 itemId 的内容 放入到对应创建的数组中
                helpInfoText.forEach(item => {

                    if (item[0] === 1) {

                        typeOne.push(item)

                    } else if (item[0] === 2) {

                        typeTwo.push(item)

                    } else if (item[0] === 3) {

                        typeThree.push(item)

                    }

                });

                console.log(typeOne, 'typeOne');

                console.log(typeTwo, 'typeTwo');

                console.log(typeThree, 'typeThree');








                // 计算有多少页数的typeOne

                let typeOnePage = Math.ceil(typeOne.length / 5);

                let typeTwoPage = Math.ceil(typeTwo.length / 5);

                let typeThreePage = Math.ceil(typeThree.length / 5);




                // 记录当前在多少页
                let page_now = 1;
















                let countOne = 0;

                let countTwo = 0;

                let countThree = 0;

                var dtElement;

                // 清空当前内容区域的所有子元素

                // contentDivUl.innerHTML = '';

                // 增加span换一批的逻辑

                // 找到span "换一批"按钮并添加点击事件监听器

                var changeBatchButton = contentDiv.querySelector('span');

                if (changeBatchButton) {

                    changeBatchButton.addEventListener('click', () => {

                        // 这里编写换一批按钮点击后的逻辑

                        console.log('换一批按钮被点击了');

                        // 可以在这里实现换一批的数据切换或其他操作

                        if (itemId === 1) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeOnePage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startIndex = (page_now - 1) * 5; // 当前页开始索引

                            const endIndex = Math.min(startIndex + 5, typeOne.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startIndex; i < endIndex; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeOne[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);

                                // 绑定点击事件
                                bindClickEvent(dtElement);

                            }

                        }

                        if (itemId === 2) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeTwoPage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startIndex = (page_now - 1) * 5; // 当前页开始索引

                            const endIndex = Math.min(startIndex + 5, typeTwo.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startIndex; i < endIndex; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeTwo[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);

                                // 绑定点击事件
                                bindClickEvent(dtElement);

                            }

                        }

                        if (itemId === 3) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeThreePage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startIndex = (page_now - 1) * 5; // 当前页开始索引

                            const endIndex = Math.min(startIndex + 5, typeThree.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startIndex; i < endIndex; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeThree[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);

                                // 绑定点击事件
                                bindClickEvent(dtElement);

                            }

                        }

                    });

                }









                // 增加span换一批的逻辑
                // 找到span "换一批"按钮并添加点击事件监听器
                //var changeBatchButton = contentDiv.querySelector('span');
                //if (changeBatchButton) {
                //  changeBatchButton.addEventListener('click', () => {
                // 这里编写换一批按钮点击后的逻辑
                //    console.log('换一批按钮被点击了');
                // 可以在这里实现换一批的数据切换或其他操作
                //});
                //}

















            });
        });
        // 自动点击热门问题以展示数据
        if (ulElement.querySelector('li.cur')) {
            ulElement.querySelector('li.cur').click();
        }
    })
    .catch(error => {
        console.error('发生错误:', error);
    });

我们将碎片放进我们的cms代码中, 然后页面中就可以看到相关的元素了, 但我们只需要碎片代码中的helpInfoClass和helpInfoText, 所以我们使用原生js获取到了这些数据, 同时将碎片的元素放进一个div中, 然后将这个div设置display:none;进行隐藏, 这样既可以获取数据, 同时还可以看不到碎片元素.

上面这部分代码实现了获取碎片数据, 进行渲染, 渲染的数据可以进行点击进行聊天, 同时点击换一批按钮进行切换.

返回顶部按钮的显示隐藏, 返回顶部功能实现:

// 显示或隐藏 返回顶部
      // 定义一个变量来标志是否正在执行 scrollToTop 函数
let isScrollingToTop = false;

// 滚动到顶部函数
function scrollToTop() {
    // 设置标志位,表示正在执行 scrollToTop 函数
    isScrollingToTop = true;

    document.body.scrollTop = 0; // For Safari
    document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera

    // 在一定时间后重置标志位,允许再次触发 handleSwipeUp 函数
    setTimeout(function() {
        isScrollingToTop = false;
    }, 300); // 这里的时间要与 debounce 函数中的 delay 保持一致
}



// 示例的 scrollFunction 函数(根据实际需要修改)
window.onscroll = function() {
    scrollFunction();
};

function scrollFunction() {
    var scrollButton = document.getElementById("topButton");
    if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
        scrollButton.classList.add("show");
    } else {
        scrollButton.classList.remove("show");
    }
}
      

ios13.13版本输入框收缩但是页面没有缩回来的问题:

// 尝试解决 ios13.13版本输入框收缩但是页面没有缩回来的问题
      
      var _input = document.getElementById("chat_input");
_input.addEventListener("blur", function () {
         // 在这里执行键盘收起时的操作
         window.scrollTo(0, document.body.scrollHeight);
});
      
      

整体AI功能代码:

<!-- 智能客服 -->

<div class="AI_service_wrapper">
    <div class="dialogue_top">
        <dl>
            <!--<dt>
                <p>“xxxx数据通”是什么?</p>
            </dt>
            <dd>
                <p>“xxxx数据通是集金融数据、权威资讯、品质服务于一体的金融数据资讯产品,帮助读者完成资讯获取、背景调查、数据分析和决策制定
                </p>
            </dd>-->
        </dl>
    </div>
    <dl class="xxxx_xxxx">
        <dt><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/xxxx.png" alt=""></dt>
        <dd>
            <p>Hi,亲爱的xxxx同学您好,xxxx智能客服很高兴为您服务~这里是xxxx用户服务中心,请问有什么可以帮您?</p>
        </dd>
    </dl>
    <ul class="nav">
          <div class="popupBg"></div>
        <a href="#">
            <li>
                <span><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/icon_subscribe.svg" alt=""></span>
                <p>订阅电邮</p>
              
                  <!-- 新增弹框 -->
                <div class="popup_online" style="display: none;">
                    <div class="avatar">
                        <dl>
                            <dt>
                                <em>张三</em>
                                <span>xxxx传媒</span>
                            </dt>
                            <dd><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/default.svg" alt=""></dd>
                        </dl>
                        <p>XXXXXXXXXXXX@xxxx.com</p>
                    </div>
                    <div class="QRcode"><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/QRcode.png" alt="">
                    </div>
                </div>
            </li>
        </a>
        <a href="#">
            <li>
                <span><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/icon_limits.svg" alt=""></span>
                <p>我的权限</p>
            </li>
        </a>
        <a href="#">
            <li>
                <span>
                    <!-- <img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/icon_history.svg" alt=""> -->
                    <svg width="1.0666666667rem" height="1.0666666667rem" viewBox="0 0 50 50" version="1.1"
                        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                        <title>我的奖品</title>
                        <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                            <g id="画板" transform="translate(-163, 0)">
                                <g id="我的奖品" transform="translate(163, 0)">
                                    <circle id="椭圆形" fill="#3B97E7" cx="25" cy="25" r="25"></circle>
                                    <path
                                        d="M26.3628287,24.5532515 L26.3628287,38 L32.3738613,38 C35.035922,38 37.2159251,35.8198481 37.2159251,33.1579362 L37.2159251,24.5532515 L26.3628287,24.5532515 Z M13.4485146,24.5532515 L13.4485146,33.1579362 C13.4485146,35.8198481 15.6287037,38 18.2905784,38 L24.2987111,38 L24.2987111,24.5532515 L13.4485146,24.5532515 Z M32.9241415,15.6333296 C33.7151901,14.6220723 32.1655869,12.5222261 30.5030352,13.2955778 C28.9117549,14.0450979 27.7666175,15.2615438 26.7792288,16.4631555 C27.6298003,16.5375127 28.492492,16.5553584 29.3490493,16.5136812 C30.3900495,16.4721527 32.3143013,16.4006955 32.9241415,15.6333296 L32.9241415,15.6333296 Z M20.3696789,13.3193721 C19.2214928,12.8672805 18.5493785,13.352052 18.064607,14.152135 C17.7998583,14.6042266 17.5202754,15.2050324 17.9397242,15.6423269 C19.0431472,16.7784299 22.073945,16.6952242 24.1083196,16.4840499 C23.1447252,15.2883868 21.9667591,13.9499579 20.3696789,13.3193721 L20.3696789,13.3193721 Z M26.3538315,17.5458329 L24.2837282,17.5458329 L24.2837282,23.4646255 L12,23.4646255 L12,18.7147646 C12,17.9652445 12.5977572,17.3673386 13.350326,17.3673386 L18.9478957,17.3673386 C17.4428696,17.0551129 16.3186637,16.4096927 16.2740866,15.2199782 C16.2324094,13.8220264 17.8117555,11.5586317 20.1137787,12.0760832 C22.4932078,12.6115291 24.1559454,14.4526123 25.4288656,16.0289097 C26.7138688,14.4228323 28.5340205,12.3496804 31.0146497,12.0226204 C33.0907015,11.7607717 34.8038161,13.8308749 34.5837933,15.1990838 C34.3963017,16.3977956 33.2483015,17.0491643 31.781904,17.3644386 L37.3081652,17.3644386 C38.0576853,17.3644386 38.6584912,17.9652445 38.6584912,18.711716 L38.6584912,23.4617256 L26.3807116,23.4617256 L26.3538315,17.5458329 Z"
                                        id="Fill-1" fill="#FFFFFF"></path>
                                </g>
                            </g>
                        </g>
                    </svg>
                </span>
                <p>我的奖品</p>
            </li>
        </a>
        <a href="#">
            <li>
                <span><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/icon_address.svg" alt=""></span>
                <p>收货地址</p>
            </li>
        </a>
    </ul>
    <div class="AI_question">
        <ul class="tab">
            <li class="cur">热门问题</li>
            <li>订阅问题</li>
            <li>使用问题</li>
        </ul>
        <div class="details">
            <div class="left"><img src="https://xxxx.xxxx.com/xxxx/xxxx/xxxx/guess_img.png" alt=""></div>
            <div class="right">
                <div class="sort01 content" style="display: block;">
                    <ul>
                        <li>xxxx (问题 li)</li>
                        <li>xxxx (回答 li)</li>
                        <li>xxxx (回答 li)</li>
                        <li>xxxx (回答 li)</li>
                        <li>xxxx (回答 li)</li>
                    </ul>
                    <span>换一批</span>
                </div>
                <div class="sort02 content">
                    <ul>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                    </ul>
                    <span>换一批</span>
                </div>
                <div class="sort03 content">
                    <ul>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                        <li>xxxx</li>
                    </ul>
                    <span>换一批</span>
                </div>
            </div>
        </div>
    </div>
    <div class="dialogue">
        <!-- 新增 -->
        <div class="timestamp" id="timestamp">
            <!-- JavaScript 将会插入当前时间 -->
        </div>
        <dl>
            <!--<dt>
                <p>“xxxx数据通”是什么?</p>
            </dt>
            <dd>
                <p>“xxxx数据通是集金融数据、权威资讯、品质服务于一体的金融数据资讯产品,帮助读者完成资讯获取、背景调查、数据分析和决策制定
                </p>
            </dd>-->
        </dl>
    </div>
</div>








<!-- 返回顶部 -->
<div id="topButton" class="scroll-to-top" onclick="scrollToTop()">
  <svg t="1723087880675" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8892" width="1.0666666667rem" height="1.0666666667rem" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M511.998925 1024a519.00307 519.00307 0 0 1-52.31605-2.641914 512.469964 512.469964 0 0 1-50.872214-7.782384 508.599252 508.599252 0 0 1-183.080575-77.168478 514.763719 514.763719 0 0 1-75.775841-62.586749 517.835713 517.835713 0 0 1-62.515069-75.80656c-9.185261-13.598691-17.776603-27.740102-25.599946-42.219432s-15.103968-29.491138-21.555155-44.748706-12.287974-31.068095-17.213403-47.011741q-7.526384-24.176589-12.615654-49.018777a512.162764 512.162764 0 0 1-7.813104-50.790293 517.999552 517.999552 0 0 1 0-104.570661 512.285644 512.285644 0 0 1 7.782384-50.872213 508.599252 508.599252 0 0 1 77.168478-183.039616c9.226221-13.649891 19.0566-26.757064 29.511618-39.413677s21.503955-24.821708 33.075131-36.362164 23.78747-22.661072 36.382643-33.04441 25.825226-20.285397 39.423917-29.470658 27.740102-17.776603 42.219432-25.599946 29.491138-15.103968 44.748706-21.555155 31.068095-12.287974 47.011741-17.213404q24.176589-7.526384 49.018777-12.615654A512.213964 512.213964 0 0 1 459.775034 2.644065a518.081472 518.081472 0 0 1 104.560421 0 512.490444 512.490444 0 0 1 50.872213 7.782384 508.599252 508.599252 0 0 1 183.049856 77.168477 514.763719 514.763719 0 0 1 75.775841 62.586749 517.835713 517.835713 0 0 1 62.515068 75.806561c9.185261 13.598691 17.776603 27.740102 25.599946 42.219431s15.103968 29.491138 21.555155 44.748706 12.287974 31.068095 17.213404 47.011741q7.526384 24.176589 12.615654 49.018778a512.316364 512.316364 0 0 1 7.761903 50.769813 517.968832 517.968832 0 0 1 0 104.56042 512.388044 512.388044 0 0 1-7.782383 50.872214q-5.119989 24.831948-12.646374 49.069976-7.464944 23.93083-17.244124 47.042462-9.7075 22.886352-21.596114 44.758946t-25.681866 42.209191q-13.721571 20.398037-29.450178 39.362477a514.25172 514.25172 0 0 1-69.457775 69.406575c-12.595174 10.383338-25.825226 20.285397-39.434157 29.470658s-27.740102 17.776603-42.219431 25.599946-29.491138 15.103968-44.748706 21.555155-31.068095 12.287974-47.011741 17.213404q-24.176589 7.526384-49.018778 12.615653a512.152524 512.152524 0 0 1-50.769813 7.761904A518.450111 518.450111 0 0 1 511.998925 1024z m119.531269-428.43046v166.37917h25.733066v-72.970086h62.556028a32.153532 32.153532 0 0 0 32.174013-32.174013V627.712832a32.174012 32.174012 0 0 0-32.174013-32.194492z m-149.790406 0A32.184252 32.184252 0 0 0 449.535056 627.712832v102.010666a32.184252 32.184252 0 0 0 32.204732 32.174012h68.249457a32.153532 32.153532 0 0 0 32.184252-32.174012V627.712832a32.194492 32.194492 0 0 0-32.184252-32.194492z m-209.745479 0v25.753546h55.101324v140.625624h25.733066V621.323086h55.101324v-25.753546z m239.615497-342.711601l193.115754 193.146475 23.439311-23.439311-216.596025-216.565305-216.565306 216.555065 23.459791 23.449551 193.125995-193.156715z m38.399919 483.326985h-68.269937a6.399987 6.399987 0 0 1-6.420466-6.420466V627.712832a6.369267 6.369267 0 0 1 1.873916-4.54655 6.256627 6.256627 0 0 1 4.54655-1.884156h68.249457a6.143987 6.143987 0 0 1 4.526071 1.884156 6.318067 6.318067 0 0 1 1.904636 4.54655v102.010666a6.440946 6.440946 0 0 1-6.420467 6.420466z m169.830043-72.970086h-62.576508v-41.891752h62.556028a6.420467 6.420467 0 0 1 6.420467 6.430706v29.040579a6.399987 6.399987 0 0 1-6.420467 6.420467z" p-id="8893" fill="#3D97E7"></path></svg>
</div>

<!-- 聊天输入框 -->
<div class="customer_service">
    <p>联系客服</p>

    <!-- <input class="chat_input" type="text" placeholder="请输入您想咨询的问题…"> -->
    <input id="chat_input" class="chat_input" type="text" placeholder="请输入您想咨询的问题…"
        onkeydown="handleKeyPress(event)" onclick="handleClick()">
</div>


<div style="display:none;">
    <!-- 帮助说明 begin -->
    <!--\#include virtual="/xxxx/xxxx.xxxx"-->
    <!-- 帮助说明 end -->
</div>







// 聊天函数
    var eventSource; // 在函数外部定义 eventSource 变量,以便在整个作用域中访问, 控制回车, 只调用一次接口
    var isProcessing = false; // 标志变量,用于控制是否正在处理请求
    
    
    
    function handleKeyPress(event) {
        console.log(event, '聊天函数event');
        if (event.keyCode === 13 || event.type === 'click') {
            if(event.keyCode === 13){
               event.preventDefault();
               event.stopPropagation(); // 阻止事件继续传播
             }
            if (isProcessing) {
                return; // 如果正在处理请求,返回,不允许提问
            }

            isProcessing = true; // 设置标志,表示正在处理请求
          
            var input = document.getElementById('chat_input');
            var question = event.type === 'click' ? event.question : input.value.trim(); // 获取问题文本

            if (question !== '') {
                var dialogueContainer = document.querySelector('.dialogue dl');
                let str = "";

                // 创建用户问题的对话框项
                var dt = document.createElement('dt');
                var dtParagraph = document.createElement('p');
                dtParagraph.textContent = '' + question + '';
                dtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;
                dt.appendChild(dtParagraph);
                dialogueContainer.appendChild(dt);

              
                // 滚动页面到底部
                window.scrollTo(0, document.body.scrollHeight);
              
                const appinfo = getCookie('appinfo');
                var apiUrl = 'https://xxxx.xxxx.com/xxxx/xxxx/xxxx';
                var params = {
                    query: question,
                    unicodeToChinese: 1,
                };

                if (appinfo) {
                    params.appinfo = appinfo;
                }

                var queryString = new URLSearchParams(params).toString();
                var fullUrl = apiUrl + '?' + queryString;

                // 检查是否已有 EventSource 实例
                if (!eventSource || eventSource.readyState === EventSource.CLOSED) {
                    eventSource = new EventSource(fullUrl, { withCredentials: true });
                    

                    // 创建对话框和段落元素
                    //var dd = document.createElement('dd');
                    //var ddParagraph = document.createElement('p');
                    //dd.appendChild(ddParagraph);
                    //dialogueContainer.appendChild(dd);
                      
                  
                      
                      var loadingAnswer = '正在回答中,请稍等...'; // 定义 loading 回答
                      // 创建对话框和段落元素 创建 loading 回答的对话框项
                    var loadingDt = document.createElement('dd');
                    var loadingDtParagraph = document.createElement('p');
                    loadingDtParagraph.textContent = loadingAnswer;
                    loadingDt.appendChild(loadingDtParagraph);
                    dialogueContainer.appendChild(loadingDt);

                    // 初始化段落元素的文本内容
                    var newStr = '';
                    
                    // 创建markedown-it
                    // 初始化 markdown-it
                    var md = window.markdownit({ html: true });
                    // 处理消息事件
                    eventSource.onmessage = function (event) {
                        var data = JSON.parse(event.data);
                        if (data.event === "message") {
                            // 使用每个新字符更新段落元素的文本内容 更新 loading 回答为接口返回的数据
                            newStr += data.answer;
                            // loadingDtParagraph.textContent = newStr; // 因为返回的数据中有标签嵌套 我们希望将标签进行转化 所以使用innerHTML
                            
                            // loadingDtParagraph.innerHTML = newStr; // 暂时注释, 好用功能
                            // loadingDtParagraph.innerHTML = marked.parse(newStr); // 使用markdown进行渲染 ios的13和14版本会导致无法正常使用
                            
                            // 上面的是好用的, 但是ios16以下会有问题, markedown没办法解析, 导致答案出不来, 我们判断一下, ios16以下就不使用markedown 或者 使用另一个markedown解析器: markedown-it来针对ios16以下版本进行markedown文本解析
                            if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && parseInt(navigator.appVersion) < 16) {
                                // loadingDtParagraph.innerHTML = newStr;
                                
                                loadingDtParagraph.innerHTML = md.render(newStr);
                            } else {
                                loadingDtParagraph.innerHTML = marked.parse(newStr);
                            }
                          
                            
                            // 滚动页面到底部
                            // window.scrollTo(0, document.body.scrollHeight);
                            // 如果是第一个字符,显示对话框
                            if (newStr.length === 1) {
                                showDialogue();
                            }

                            // 如果接收到完整的回复,关闭 EventSource 对象
                            if (data.complete) {
                                eventSource.close();
                                isProcessing = false; // 请求处理完毕,允许新的提问
                            }
                        } else if (data.event === "message_end") {
                            eventSource.close();
                            isProcessing = false; // 请求处理完毕,允许新的提问
                        }
                    };

                    function showDialogue() {
                        // 显示对话框和段落元素样式
                        loadingDt.style.display = 'block'; // 或者使用其他适当的样式来显示对话框
                    }
                }

                // 清空输入框
                input.value = '';
            }
        }
    }






    // 主动请求 xxxxxxxx 文档
      fetch('https://xxxx.xxxx.com/m/xxxx/xxxx.html')
    .then(response => {
        if (!response.ok) {
            throw new Error('网络请求失败');
        }
        return response.text();
    })
    .then(data => {
        // 正则表达式用于匹配 helpInfoClass
        var helpInfoClassRegex = /var helpInfoClass = \[(.*?)\];/s;
        var matchClass = helpInfoClassRegex.exec(data);
        var helpInfoClass = [];

        if (matchClass && matchClass.length > 1) {
            var classData = matchClass[1].trim();
            // 将匹配的字符串转换为数组
            eval('helpInfoClass = [' + classData + '];');
        }

        // 正则表达式用于匹配 helpInfoText
        var helpInfoTextRegex = /var helpInfoText = \[(.*?)\];/s;
        var matchText = helpInfoTextRegex.exec(data);
        var helpInfoText = [];

        if (matchText && matchText.length > 1) {
            var textData = matchText[1].trim();
            // 将匹配的字符串转换为数组
            eval('helpInfoText = [' + textData + '];');
        }
        
        // 输出提取的数据,或者进行进一步处理
        console.log('helpInfoClass:', helpInfoClass); // tab名称
        console.log('helpInfoText:', helpInfoText);
      
      
          // 获取 ul 列表元素, 将写死的tab名称 改为活的数据
        var ulElement = document.querySelector('.AI_question ul');

        // 清空现有的列表项
        ulElement.innerHTML = '';

        // 遍历 helpInfoClass 数组
        helpInfoClass.forEach((item, xxxx) => {
            var itemId = item[0];
            var itemName = item[1];

            // 跳过最后一个人工客服问题
            if (xxxx === helpInfoClass.length - 1) {
                return;
            }

            // 创建 li 元素
            var liElement = document.createElement('li');

            // 将第一个常见问题改为热门问题
            if (xxxx === 0) {
                liElement.textContent = "热门问题";
                liElement.classList.add('cur'); // 添加 cur class
            } else {
                liElement.textContent = itemName;
            }

            // 将 li 元素添加到 ul 元素中
            ulElement.appendChild(liElement);
        });
        ulElement.querySelectorAll('li').forEach((liElement, xxxx) => {
          liElement.addEventListener('click', () => {
              // 清除所有 li 的 'cur' 类
              ulElement.querySelectorAll('li').forEach(item => {
                  item.classList.remove('cur');
              });
              // 添加 'cur' 类到当前点击的 li
              liElement.classList.add('cur');

              var itemId = helpInfoClass[xxxx][0];
              // 获取所有信息展示部分包括sort1,sort2,sort3
              var contentDivs = document.querySelectorAll('.details .content');

              // 隐藏所有内容区域
              contentDivs.forEach(div => {
                  div.style.display = 'none';
              });
              // 定义整个的div包括ul和span"换一批"按钮
              var contentDiv;
              // 定义ul部分不包括span"换一批"按钮
              var contentDivUl;
              if (itemId === 1) {
                  console.log("展示热门问题");
                  contentDiv = document.querySelector('.sort01.content');
                  contentDivUl = contentDiv.querySelector('ul');
                  console.log(contentDiv,"contentDiv1", contentDivUl, 'contentDivUl');
              } else if (itemId === 2) {
                  console.log("展示所有订阅问题");
                  contentDiv = document.querySelector('.sort02.content');
                  contentDivUl = contentDiv.querySelector('ul');
                  console.log(contentDiv,"contentDiv2", contentDivUl, 'contentDivUl');
              } else if (itemId === 3) {
                  console.log("展示所有使用问题");
                  contentDiv = document.querySelector('.sort03.content');
                  contentDivUl = contentDiv.querySelector('ul');
                  console.log(contentDiv,"contentDiv3", contentDivUl, 'contentDivUl');
              } else {
                  console.log("展示所有未知问题");
                  // 这里可以根据需求处理没有匹配到任何内容的情况
                  return;
              }
              // 显示当前选择的内容区域
              contentDiv.style.display = 'block';

              // 清空当前内容区域的所有子元素
              contentDivUl.innerHTML = '';
              console.log(contentDivUl, '清空后的contentDivUl')
              // 计数器,用于限制显示的个数
              let count = 0;
              
              // 遍历 helpInfoText 找到对应 itemId 的内容并插入到相应的 dl > dt 中
              helpInfoText.forEach(item => {
                  if (count < 5 && item[0] === itemId) {
                      // 给li标题写入文字
                      console.log(item[1], 'item[1]');
                      var dtElement = document.createElement('li');
                      dtElement.textContent = item[1];
                      contentDivUl.appendChild(dtElement);
                      // 增加计数器
                      count++;
                    
                      // 添加点击事件监听器
                      // 绑定点击事件
                      bindClickEvent(dtElement);
                      //dtElement.addEventListener('click', function() {
                        //  console.log(this, 'this');
                          // 获取对应的文本
                          //var clickedText = this.textContent;
                          //console.log(clickedText, 'clickedText----');
                          // 在这里可以进行你想要的操作,比如打印文本或者执行其他逻辑
                          // 调用聊天函数
                          //handleKeyPress({ type: 'click', question: clickedText });
                      //});
                  }
              });
              console.log(contentDivUl, '进入界面后的contentDivUl')
              // 定义一个函数,用来绑定点击事件
              function bindClickEvent(element) {
                  element.addEventListener('click', function(event) {
                      event.stopPropagation(); // 阻止事件继续传播
                      
                      var clickedText = this.textContent;
                      handleKeyPress({ type: 'click', question: clickedText });
                  });
              }
            
            
            
            
            
            
              let typeOne = [];

                  let typeTwo = [];

                  let typeThree = [];

                  // 遍历 helpInfoText 找到对应 itemId 的内容 放入到对应创建的数组中
                  helpInfoText.forEach(item => {

                      if (item[0] === 1) {

                        typeOne.push(item)

                      } else if (item[0] === 2) {

                        typeTwo.push(item)

                      }else if (item[0] ===3){

                        typeThree.push(item)

                      }

                  });

                  console.log(typeOne, 'typeOne');

                  console.log(typeTwo, 'typeTwo');

                  console.log(typeThree, 'typeThree');








                  // 计算有多少页数的typeOne

                  let typeOnePage = Math.ceil(typeOne.length / 5);

                  let typeTwoPage = Math.ceil(typeTwo.length / 5);

                  let typeThreePage = Math.ceil(typeThree.length / 5);




                  // 记录当前在多少页
                  let page_now = 1;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
                  let countOne = 0;

                  let countTwo = 0;

                  let countThree = 0;

                  var dtElement;

                  // 清空当前内容区域的所有子元素

                  // contentDivUl.innerHTML = '';

                  // 增加span换一批的逻辑

                  // 找到span "换一批"按钮并添加点击事件监听器

                  var changeBatchButton = contentDiv.querySelector('span');

                  if (changeBatchButton) {

                      changeBatchButton.addEventListener('click', () => {

                          // 这里编写换一批按钮点击后的逻辑

                          console.log('换一批按钮被点击了');

                          // 可以在这里实现换一批的数据切换或其他操作

                          if (itemId === 1) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeOnePage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startxxxx = (page_now - 1) * 5; // 当前页开始索引

                            const endxxxx = Math.min(startxxxx + 5, typeOne.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startxxxx; i < endxxxx; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeOne[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);
                          
                            // 绑定点击事件
                            bindClickEvent(dtElement);

                            }

                          }

                          if (itemId === 2) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeTwoPage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startxxxx = (page_now - 1) * 5; // 当前页开始索引

                            const endxxxx = Math.min(startxxxx + 5, typeTwo.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startxxxx; i < endxxxx; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeTwo[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);
                          
                            // 绑定点击事件
                              bindClickEvent(dtElement);

                            }

                          }

                          if (itemId === 3) {

                            // 首先检查当前页数是否小于最大页数

                            if (page_now < typeThreePage) {

                                page_now += 1; // 如果可以翻页,页数加一

                            } else {

                                page_now = 1; // 否则重置为第一页

                            }



                            // 计算当前页面应该显示的数据起始和结束索引

                            const startxxxx = (page_now - 1) * 5; // 当前页开始索引

                            const endxxxx = Math.min(startxxxx + 5, typeThree.length); // 结束索引,确保不超出数组范围



                            // 清空之前的内容

                            contentDivUl.innerHTML = ''; // 清空之前的内容(如果有的话)



                            // 渲染当前页的数据

                            for (let i = startxxxx; i < endxxxx; i++) {

                                // 给li标题写入文字

                                const dtElement = document.createElement('li');

                                dtElement.textContent = typeThree[i][1]; // 假设typeOne[i]是一个数组并且需要取第二个元素

                                contentDivUl.appendChild(dtElement);
                          
                            // 绑定点击事件
                              bindClickEvent(dtElement);

                            }

                          }

                      });

                  }
            
            
            
            
            
            
            
            
            
              // 增加span换一批的逻辑
              // 找到span "换一批"按钮并添加点击事件监听器
              //var changeBatchButton = contentDiv.querySelector('span');
              //if (changeBatchButton) {
                //  changeBatchButton.addEventListener('click', () => {
                      // 这里编写换一批按钮点击后的逻辑
                  //    console.log('换一批按钮被点击了');
                      // 可以在这里实现换一批的数据切换或其他操作
                  //});
              //}
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
              });
          });
        // 自动点击热门问题以展示数据
        if (ulElement.querySelector('li.cur')) {
            ulElement.querySelector('li.cur').click();
        }
    })
    .catch(error => {
        console.error('发生错误:', error);
    });
  
  
  
  
  
  
  
    
    
  
  
  
  
  
    let lastId = null; // 设置字段用来存储下方循环遍历的最后一个的id字符串, 请求接口传参需要
    let lastItem; // 存储当前十条数据的最后一条
    let haveNextPage = true; // 默认没有下一页
    // 聊天记录函数
    // 上划触发事件处理函数
    function handleSwipeUp() {
        var dialogueContainerTop = document.querySelector('.dialogue_top dl');
        // dialogueContainerTop.innerHTML = ''; // 清空现有内容

        var historyApiUrl = 'https://xxxx.xxxx.com/xxxx/xxxx/xxxx';
        //var historyParams = {
          //  limit: 10, // 获取十条记录
        //};
        //var historyQueryString = new URLSearchParams(historyParams).toString();
        //var historyFullUrl = historyApiUrl + '?' + historyQueryString;
        var historyFullUrl;
        if(lastId !== null) { // 不是第一次请求
          historyFullUrl = historyApiUrl + '?' + new URLSearchParams({
            lastId: lastId // 携带上一次请求的 lastId
          }).toString();
          console.log(historyFullUrl, 'historyFullUrl--有lastId')
        } else { // 是第一次请求数据
          historyFullUrl = historyApiUrl;
          console.log(historyFullUrl, 'historyFullUrl-没有lastId')
        }
      
        if(haveNextPage) {
          fetch(historyFullUrl, {
            method: 'GET',
            credentials: 'include',
          })
          .then(response => {
              if (!response.ok) {
                  throw new Error('Network response was not ok');
              }
              return response.json();
          })
          .then(data => {
              console.log(data.data, 'data.data');

              haveNextPage = data.data.has_more; // 存储: 是否有下一页数据.
              console.log(haveNextPage, 'haveNextPage');

              lastItem = data.data.data[data.data.data.length -1];
              console.log(lastItem, '最后一条数据');
              lastId = lastItem.id;
              console.log(lastId, 'lastId')

              // 根据 haveNextPage 决定是否继续请求下一页


              
              // 创建markedown-it
              // 初始化 markdown-it
              var md = window.markdownit({ html: true });
              data.data.data.forEach(entry => {
                  console.log(entry.data[0], 'entry.data[0]');
                
                  // 判断聊天数据中answer时空字符串就不显示
                  // 检查 AI 回复是否为空串
                  if (!entry.data[0].answer) {
                      console.log('AI 回复为空,跳过该项');
                      return; // 如果为空,跳过当前循环
                  }
                  
                  // 先创建dd, 要实现创建dt在创建dd就会反过来,导致ai先回答用户再提问
                  var recordDd = document.createElement('dd');
                  var recordDdParagraph = document.createElement('p');
                
                  
                  // recordDdParagraph.textContent = entry.data[0].answer; // AI回复
                  
                
                  // recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复 可以正常使用
                  // recordDdParagraph.innerHTML = marked.parse(entry.data[0].answer); // 使用markdown进行渲染  AI回复
                  
                  // ios16以下版本无法使用markedown, 所以加了一下判断.
                  if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && parseInt(navigator.appVersion) < 16) {
                      // recordDdParagraph.innerHTML = entry.data[0].answer; // AI回复 可以正常使用
                      recordDdParagraph.innerHTML = md.render(entry.data[0].answer);
                  } else {
                      recordDdParagraph.innerHTML = marked.parse(entry.data[0].answer); // 使用markdown进行渲染  AI回复
                  }
                  recordDd.appendChild(recordDdParagraph);
                  dialogueContainerTop.prepend(recordDd); // 插入到顶部

                  // 后创建dt, 
                  var recordDt = document.createElement('dt');
                  var recordDtParagraph = document.createElement('p');
                  recordDtParagraph.textContent = entry.data[0].query; // 用户消息
                  recordDtParagraph.style.textAlign = 'left'; // 样式设置为text-align:left;

                  recordDt.appendChild(recordDtParagraph);
                  dialogueContainerTop.prepend(recordDt); // 插入到顶部

              });
          })
          .catch(error => {
              console.error('Error fetching history:', error);
          });
        } else {
            console.log('has_more是false, 没有下一页了')
        }
        
    }

    if (isMobileOrTablet()) {
        console.log('手机端');

        if (isMobileOrTablet()) {
            console.log('手机端');

            // 定义一个标志来记录是否已经触发了handleSwipeUp()
            var swipeUpTriggered = false;

            // 记录touchstart时的触摸点位置
            var touchStartY = null;

            // 监听touchstart事件,用于判断用户是否处于顶部并开始下拉
            document.addEventListener('touchstart', function(e) {
                // 判断是否在页面顶部
                if (window.scrollY === 0) {
                    // 记录touchstart时的触摸点位置
                    touchStartY = e.touches[0].clientY;
                }
            });

            // 监听touchmove事件,用于判断是否正在向下滑动
            document.addEventListener('touchmove', function(e) {
                // 如果touchStartY没有记录值,直接返回
                if (touchStartY === null) {
                    return;
                }

                // 计算当前触摸点的移动距离
                var touchMoveY = e.touches[0].clientY;

                // 如果向下滑动(即触摸点向下移动)且handleSwipeUp()未被触发过
                if (touchMoveY > touchStartY && !swipeUpTriggered) {
                    console.log('长下滑');
                    // 不立即调用handleSwipeUp(),而是等待touchend事件
                    swipeUpTriggered = true; // 标记为已触发
                }
            });

            // 监听touchend事件,处理滑动结束后的逻辑
            document.addEventListener('touchend', function(e) {
                // 如果已经触发了下滑操作且handleSwipeUp()未被调用过
                if (swipeUpTriggered) {
                    handleSwipeUp();
                    swipeUpTriggered = false; // 重置标志
                }

                // 重置touchStartY
                touchStartY = null;
            });
        }


    } else {
        // 网页打开
        // 网页打开
        console.log('电脑端');

        function debounce(func, delay) {
            let timerId;
            return function() {
                const context = this;
                const args = arguments;
                clearTimeout(timerId);
                timerId = setTimeout(() => {
                    func.apply(context, args);
                }, delay);
            };
        }



        const debouncedHandleSwipeUp = debounce(handleSwipeUp, 300);

        // 监听页面滚动事件
        window.addEventListener('scroll', function(event) {
            // 检查是否在页面顶部
            if (window.scrollY === 0) {
                // 如果不是正在执行 scrollToTop 函数,则调用防抖后的 handleSwipeUp 函数
                if (!isScrollingToTop) {
                    debouncedHandleSwipeUp();
                }
            }
        });
    }
    
  
  
  
  
  // 等待页面加载完成后执行 显示时间
    document.addEventListener('DOMContentLoaded', function() {
        // 获取当前时间
        const now = new Date();
        const timestampElement = document.getElementById('timestamp');
        timestampElement.textContent = now.toLocaleString();
    });

  
  
  // 显示或隐藏 返回顶部
  // 定义一个变量来标志是否正在执行 scrollToTop 函数
let isScrollingToTop = false;

// 滚动到顶部函数
function scrollToTop() {
// 设置标志位,表示正在执行 scrollToTop 函数
isScrollingToTop = true;

document.body.scrollTop = 0; // For Safari
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera

// 在一定时间后重置标志位,允许再次触发 handleSwipeUp 函数
setTimeout(function() {
    isScrollingToTop = false;
}, 300); // 这里的时间要与 debounce 函数中的 delay 保持一致
}



// 示例的 scrollFunction 函数(根据实际需要修改)
window.onscroll = function() {
scrollFunction();
};

function scrollFunction() {
var scrollButton = document.getElementById("topButton");
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
    scrollButton.classList.add("show");
} else {
    scrollButton.classList.remove("show");
}
}
  
  
  
  
  // 解决输入框让键盘挡住的情况
  function handleClick() {
    // 调用 xxxx.keyboardLayout() 方法
    xxxx.keyboardLayout();
  }
  
  
  
  // 尝试解决 ios13.13版本输入框收缩但是页面没有缩回来的问题
  
  var _input = document.getElementById("chat_input");
_input.addEventListener("blur", function () {
     // 在这里执行键盘收起时的操作
     window.scrollTo(0, document.body.scrollHeight);
});
  
  
  // 增加限制 当页面改变发生跳转的时候关闭eventsource
  window.addEventListener('beforeunload', function() {
    if (eventSource) {
      eventSource.close();
    }
  });

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值