A. Log Chopping

https://codeforces.com/contest/1672/problem/A
在这里插入图片描述
input

2
4
2 4 2 1
1
1

output

errorgorn
maomao90

在这里插入图片描述
题意
现在有两个人 A A A B B B 想去轮流的拆解 n n n 根木棍,每次拆解时可以选取一根长度大于 1 1 1 的木棍拆分成两根长度都大于0且为整数的木棍,当轮到某人且无法拆解场上任意的一根木棍,则输,请输出赢的名字

思路
显然当场上的木棍的长度都为 1 1 1 的时候便输,所以我们得查询还要多少步会达到必败状态
对于下图的绿色部分即为表示可以额外切分的长度,所以我们取出其并判断奇偶便可以判断
在这里插入图片描述
而绿色的部分即为 s u m a i − n sum_{a_i} - n sumain ,其中 a i a_i ai 表示一开始每根木棍的总和

AC代码

#include <bits/stdc++.h>
#define endl '\n'
#define AC return 0;
using namespace std;
//#define ll long long
//#define int long long


void slove()
{
    int n;
    cin >> n;
    int sum = 0;
    for(int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        sum += x;
    }
    if((sum - n) % 2) 
        cout << "errorgorn" << endl;
    else
        cout << "maomao90" << endl;
        
    
}

signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int T;cin >> T; while(T--)
    slove();
    AC
}
[2025-05-24 10:41:20,519] 工件 Shopping:war exploded: 正在等待服务器连接以启动工件部署… Using CATALINA_BASE: "D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572" Using CATALINA_HOME: "D:\Software\apache-tomcat-9.0.46" Using CATALINA_TMPDIR: "D:\Software\apache-tomcat-9.0.46\temp" Using JRE_HOME: "D:\Software\jdk1.8.0_201" Using CLASSPATH: "D:\Software\apache-tomcat-9.0.46\bin\bootstrap.jar;D:\Software\apache-tomcat-9.0.46\bin\tomcat-juli.jar" Using CATALINA_OPTS: "" 已连接到目标 VM, 地址: ''127.0.0.1:4963',传输: '套接字'' 24-May-2025 22:41:21.574 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log Server.鏈嶅姟鍣ㄧ増鏈�: Apache Tomcat/9.0.46 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鏈嶅姟鍣ㄦ瀯寤�: May 8 2021 17:35:52 UTC 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鏈嶅姟鍣ㄧ増鏈彿: 9.0.46.0 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鎿嶄綔绯荤粺鍚嶇О: Windows 10 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log OS.鐗堟湰: 10.0 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鏋舵瀯: amd64 24-May-2025 22:41:21.576 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log Java 鐜鍙橀噺: D:\Software\jdk1.8.0_201\jre 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log Java铏氭嫙鏈虹増鏈�: 1.8.0_201-b09 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log JVM.渚涘簲鍟�: Oracle Corporation 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: D:\Software\apache-tomcat-9.0.46 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djava.util.logging.config.file=D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572\conf\logging.properties 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:4963,suspend=y,server=n 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -javaagent:D:\Software\idea2021\plugins\java\lib\rt\debugger-agent.jar 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcom.sun.management.jmxremote= 24-May-2025 22:41:21.578 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcom.sun.management.jmxremote.port=1099 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcom.sun.management.jmxremote.ssl=false 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcom.sun.management.jmxremote.password.file=D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572\jmxremote.password 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcom.sun.management.jmxremote.access.file=D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572\jmxremote.access 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djava.rmi.server.hostname=127.0.0.1 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djdk.tls.ephemeralDHKeySize=2048 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dignore.endorsed.dirs= 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcatalina.base=D:\Software\idea2021\bin\IdeaConfig\system\tomcat\90364d68-a8f7-46d9-a535-cf7271408572 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Dcatalina.home=D:\Software\apache-tomcat-9.0.46 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.startup.VersionLoggerListener.log 鍛戒护琛屽弬鏁帮細 -Djava.io.tmpdir=D:\Software\apache-tomcat-9.0.46\temp 24-May-2025 22:41:21.579 淇℃伅 [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent 鍦╦ava.library.path:[D:\Software\jdk1.8.0_201\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;D:\Software\MySQL\MySQL Server 8.0\bin;C:\Program Files\PlasticSCM5\server;C:\Program Files\PlasticSCM5\client;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;D:\Software\Java21\bin;%MYSQL_HOME%\bin;C:\Program Files (x86)\Microsoft SQL Server\160\Tools\Binn\;C:\Program Files\Microsoft SQL Server\160\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\Microsoft SQL Server\160\DTS\Binn\;D:\Mysql\mysql-8.0.36-winx64\bin;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\dotnet\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\NVIDIA Corporation\NVIDIA app\NvDLISR;D:\Software\Java21\bin;D:\Software\Redis\;D:\Software\apache-maven-3.9.9\bin;D:\Software\MySQL\MySQL Shell 8.0\bin\;C:\Users\24337\AppData\Local\Programs\Python\Python313\Scripts\;C:\Users\24337\AppData\Local\Programs\Python\Python313\;C:\Users\24337\AppData\Local\Microsoft\WindowsApps;;D:\IntelliJ IDEA Community Edition 2024.3.4\bin;;D:\Software\IntelliJ IDEA 2024.3.4.1\bin;;D:\Software\PyCharm Community Edition 2025.1\bin;;.]涓婃壘涓嶅埌鍩轰簬APR鐨凙pache Tomcat鏈満搴擄紝璇ュ簱鍏佽鍦ㄧ敓浜х幆澧冧腑鑾峰緱鏈�浣虫�ц兘 24-May-2025 22:41:21.759 淇℃伅 [main] org.apache.coyote.AbstractProtocol.init 鍒濆鍖栧崗璁鐞嗗櫒 ["http-nio-8080"] 24-May-2025 22:41:21.778 淇℃伅 [main] org.apache.catalina.startup.Catalina.load 鏈嶅姟鍣ㄥ湪[361]姣鍐呭垵濮嬪寲 24-May-2025 22:41:21.800 淇℃伅 [main] org.apache.catalina.core.StandardService.startInternal 姝e湪鍚姩鏈嶅姟[Catalina] 24-May-2025 22:41:21.800 淇℃伅 [main] org.apache.catalina.core.StandardEngine.startInternal 姝e湪鍚姩 Servlet 寮曟搸锛歔Apache Tomcat/9.0.46] 24-May-2025 22:41:21.808 淇℃伅 [main] org.apache.coyote.AbstractProtocol.start 寮�濮嬪崗璁鐞嗗彞鏌刐"http-nio-8080"] 24-May-2025 22:41:21.818 淇℃伅 [main] org.apache.catalina.startup.Catalina.start [37]姣鍚庢湇鍔″櫒鍚姩 已连接到服务器 [2025-05-24 10:41:22,120] 工件 Shopping:war exploded: 正在部署工件,请稍候… 24-May-2025 22:41:22.729 淇℃伅 [RMI TCP Connection(3)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars 鑷冲皯鏈変竴涓狫AR琚壂鎻忕敤浜嶵LD浣嗗皻鏈寘鍚玊LD銆� 涓烘璁板綍鍣ㄥ惎鐢ㄨ皟璇曟棩蹇楄褰曪紝浠ヨ幏鍙栧凡鎵弿浣嗘湭鍦ㄥ叾涓壘鍒癟LD鐨勫畬鏁碕AR鍒楄〃銆� 鍦ㄦ壂鎻忔湡闂磋烦杩囦笉闇�瑕佺殑JAR鍙互缂╃煭鍚姩鏃堕棿鍜孞SP缂栬瘧鏃堕棿銆� 24-May-2025 22:41:22.788 淇℃伅 [MLog-Init-Reporter] com.mchange.v2.log.MLog. MLog clients using java 1.4+ standard logging. 24-May-2025 22:41:22.824 淇℃伅 [RMI TCP Connection(3)-127.0.0.1] com.mchange.v2.c3p0.C3P0Registry. Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10] 24-May-2025 22:41:22.926 淇℃伅 [RMI TCP Connection(3)-127.0.0.1] com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource. Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1bqrg23bbrdsow1pw3ici|66086448, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.cj.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1bqrg23bbrdsow1pw3ici|66086448, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/cookieshop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ] [2025-05-24 10:41:23,668] 工件 Shopping:war exploded: 工件已成功部署 [2025-05-24 10:41:23,668] 工件 Shopping:war exploded: 部署已花费 1,548 毫秒 24-May-2025 22:41:31.819 淇℃伅 [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory 鎶妛eb 搴旂敤绋嬪簭閮ㄧ讲鍒扮洰褰� [D:\Software\apache-tomcat-9.0.46\webapps\manager] 24-May-2025 22:41:31.866 淇℃伅 [Catalina-utility-2] org.apache.jasper.servlet.TldScanner.scanJars 鑷冲皯鏈変竴涓狫AR琚壂鎻忕敤浜嶵LD浣嗗皻鏈寘鍚玊LD銆� 涓烘璁板綍鍣ㄥ惎鐢ㄨ皟璇曟棩蹇楄褰曪紝浠ヨ幏鍙栧凡鎵弿浣嗘湭鍦ㄥ叾涓壘鍒癟LD鐨勫畬鏁碕AR鍒楄〃銆� 鍦ㄦ壂鎻忔湡闂磋烦杩囦笉闇�瑕佺殑JAR鍙互缂╃煭鍚姩鏃堕棿鍜孞SP缂栬瘧鏃堕棿銆� 24-May-2025 22:41:31.869 淇℃伅 [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Web搴旂敤绋嬪簭鐩綍[D:\Software\apache-tomcat-9.0.46\webapps\manager]鐨勯儴缃插凡鍦╗50]姣鍐呭畬鎴� HTTP状态 404 - 未找到 类型 状态报告 描述 源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示。 Apache Tomcat/9.0.46
05-25
<template> <view class="chatIndex"> <custom-navbar title="WONSOU" :showBack="true" :background="'#F2F2F2'"> <template #left> <view class="flexCC"> <image src="/static/images/Back@2x.png" class="back-icon" @click="onLeftClick"></image> <!-- --> </view> </template> <!-- 自定义右侧 slot --> <template #right> <image src="/static/images/Dialogue@2x.png" class="icon-more" @click="agrenchatInfo"></image> </template> </custom-navbar> <u-skeleton :loading="loadingskeleton" :animate="true" rows="5" :rowsHeight="200"></u-skeleton> <scroll-view style="flex:1;padding:24rpx 32rpx 100rpx 32rpx;height: calc(100vh - 450rpx);position: relative;" :style="inputType === 'text'?'':'height: calc(100vh - 400rpx);'" scroll-y :show-scrollbar="false" :scroll-top="scrollTop" refresher-enabled :refresher-triggered="refresherTriggered" :refresher-background="'transparent'" @refresherrefresh="refresherrefresh" @scrolltolower="scrolltolower" @scroll="scroll"> <view class="flex"> <image src="/static/images/chatindexHeader.png" mode="" class="chatIndexLogo"></image> <view class="chatIndexLogoText"> <view class="chatIndexLogoTextHi"> hi, </view> <view class="chatIndexLogoText2"> 我是小W </view> </view> </view> <view class="chatIndex_card" v-if="getchatUnreadCountInfo.messages.length > 0"> <view class="flexCAB"> <view class="chatIndex_card_tips"> {{getchatUnreadCountInfo.title}} </view> <view class="flexC" @click="getchatUnreadCount"> <image src="/static/images/changechatindex.png" mode="" class="changechatindex_icon" :class="{ spinning: isSpinning }" @click="uploadUnreadCountInfo"></image> <view class="changechatindex_text" @click="uploadUnreadCountInfo"> 换一换 </view> </view> </view> <view class="cardItem flex" v-for="(item,index) in getchatUnreadCountInfo.messages" :key="index" @click="getNoice(item.content)"> <image :src="item.icon?'https://www.wonsouai.com/static/'+item.icon:'/static/images/new@2x.png'" mode="widthFix" class="cardItem_img"></image> <view class="cardItem_text"> {{item.content}} </view> </view> </view> <view class="" v-for="(item,index) in chatList" :key="index"> <styleTemplate :chatInfo="item" @add-shop-cat="addShopCatInfo" @refresh-answe="RefreshAnsweClick" :makedowType="makedowType" :isLastData="index==chatList.length-1?true:false" @delChatList="delChatList" @erragrenChatinfo="erragrenChatinfo" :openLoading="loading" @ascenrClick="ascenrClick"> </styleTemplate> </view> </scroll-view> <view class="sublimt flexCCC" :style="isFocus?`bottom:` + inputBottom + 'rpx;background:transparent':''"> <image src="/static/images/Down@3x.png" mode="" class="downloadScooltop" @click="scrollTopDown" v-if="isShowDownload" :style="inputType === 'text'?'':'top:-80rpx'"></image> <view class="shopcat flexCCC" v-if="inputType === 'text'"> <view class="shopcatNumInfo"> <image src="/static/images/Shopping_cart@2x.png" class="shopcat_img" mode=""></image> <view class="shopcatNum" v-if="shopCatNum>0"> {{shopCatNum>99?'99+':shopCatNum}} </view> </view> <view class="shopcat_text" @click="jumpShopcat()"> 购物车 </view> </view> <view class="SandTypeInfo" :style="isCancel?'color:#F2525E;':'color:#333333'" v-if="videoSandType"> {{isCancel?'松手取消':'松手发送,上移取消'}} </view> <view class="index_button" :style="videoSandType&&!isCancel ? 'background: linear-gradient( 94deg, #8128FF 2%, #2866F6 57%);' :(videoSandType&&isCancel?'background: #F2525E;':'')"> <!-- 文本输入 --> <view class="flexCAB" v-if="inputType === 'text'"> <image src="/static/images/voice@2x.png" @click="changeVlaueType" class="index_button_img" /> <input type="text" v-model="value" class="inputValue" @focus="isFocus = true" @blur="isFocus = false" :adjust-position="false" @keyboardheightchange="keyboardheightchange" maxlength="500" />{{RefreshId.value}} <view class="flexC"> <!-- <image :src="RefreshId.value!==''?'/static/images/send@2x.png':'/static/images/send@3x.png'" class="index_button_img" @click="pushchatList('text')" /> --> <image :src="RefreshId==''?'/static/images/send@2x.png':'/static/images/send@3x.png'" class="index_button_img" @click="pushchatList('text')" /> </view> </view> <!-- 语音输入 --> <view class="flexCAB viodeinfo" v-if="inputType === 'video'"> <!-- 切换键盘图标 --> <image v-if="!videoSandType" src="/static/images/Keyboard@2x.png" @click="changeVlaueType" class="index_button_img" /> <!-- 提示文字 --> <view class="video_tips" @touchstart.stop.prevent="onLongPress" @touchend.stop.prevent="onTouchEnd" @touchmove.stop.prevent="touchcancel"> {{videoSandType?'':'按住说话'}} <voiceAnother v-show="videoSandType&&countdown > 10" :startColor="startColor" :endColor="endColor" style=" pointer-events: none" /> <view class="" style="font-family: PingFang SC, PingFang SC;font-weight: 500;font-size: 28rpx;color: #fff;" v-show="videoSandType&&countdown < 10"> {{countdown}}秒后将停止录音 </view> </view> <!-- 发送按钮 --> <view class="flexC" v-if="!videoSandType"> <image :src="RefreshId==''?'/static/images/send@2x.png':'/static/images/send@3x.png'" class="index_button_img" @click="pushchatList('text')" /> </view> </view> </view> </view> <guidePageChatIndex v-if="guidePageChatIndexShow" @sublimtGuidechatIndex="sublimtGuidechatIndex" @sublimtOne="sublimtOne"> </guidePageChatIndex> </view> <gao-ChatSSEClient ref="chatSSEClientRef" @onOpen="openCore" @onError="errorCore" @onMessage="messageCore" @onFinish="finishCore" /> </template> <script lang="ts" setup> import { ref, nextTick, onMounted, onUnmounted, getCurrentInstance } from 'vue' import { onLoad, onShow } from '@dcloudio/uni-app' import CustomNavbar from '@/components/CustomNavbar.vue' import styleTemplate from '@/components/styleTemplate.vue' import voiceAnother from '@/components/voiceAnother' import guidePageChatIndex from '@/components/guidePageChatIndex' import { generateRandomString, openSystemSettings, throttle, getToken, md5Encrypt, processString } from '@/utils/public' import { ChatAddMessage, chatChatMessage, productAddAction, productItemList, chatStopMessage, chatReGenerate, chatUnreadCount, verificationGetNlsToken, chatGetVoiceUrl, productList } from '@/utils/api.ts' import { usepublicStore } from '@/store/publicStore' const guidePageChatIndexShow = ref(false) const usepublic = usepublicStore() const isFocus = ref(false) const refresherTriggered = ref(false) const isShowDownload = ref(false) const shopCatNum = ref(0)//购物车的数量 const startColor = ref('#92EEF9') const endColor = ref('#3AD7FE') const value = ref('') const videoSandType = ref(false) const scrollTop = ref(0) const messageId = ref(0) const inputType = ref('text')//输出的是文本还是语音 const name32Potison = ref('') const chatList : any = ref([ ]) const loadingskeleton = ref(true) const getchatUnreadCountInfo : any = ref('') const inputBottom = ref(0) const keyboardheightchange = (e) => { console.log('event', e) if (e.detail.height == 0) { inputBottom.value = 0 } else { inputBottom.value = (e.detail.height - 20) * 2 } } const scrollTopDown = () => { console.log('点击了') scrollTop.value = Math.floor(Math.random() * 9000000000) + 1000000000; } const scroll = (e : any) => { const { scrollTop, scrollHeight, clientHeight } = e.detail // 判断是否接近底部(预留20px缓冲防抖) // console.log('scrollHeight - scrollTop - clientHeight', scrollHeight, scrollTop, clientHeight) if ((scrollHeight - scrollTop) < 700) return isShowDownload.value = true } const scrolltolower = () => { console.log('到底了') isShowDownload.value = false } const isSpinning = ref(false) const uploadUnreadCountInfo = async () => { if (isSpinning.value) return; // 防止重复触发 isSpinning.value = true } const sublimtGuidechatIndex = () => { const initData = { guideIndexTypeOne: true,//首页第一个引导页 guideIndexTypeTwo: true,//首页第二个引导页 guideChatIndexTypeOne: true,//聊天第一个引导页 guideChatIndexTypeTwo: true,//聊天第二个引导页 } uni.setStorageSync('userGuideType', initData) guidePageChatIndexShow.value = false } const getGuideType = () => { // 如果 chatId 已有值或输入框有内容,直接隐藏引导 console.log('chatId.value', chatId.value, value.value) if (chatId.value > 0 || value.value !== '') { guidePageChatIndexShow.value = false return } // 获取缓存 const guideTypeData = uni.getStorageSync('userGuideType') console.log('guideTypeDataguideTypeDataguideTypeDataguideTypeData', guideTypeData) // 缓存存在且为对象,判断引导状态 if (guideTypeData && typeof guideTypeData === 'object') { if (guideTypeData.guideChatIndexTypeOne === false || guideTypeData.guideChatIndexTypeTwo === false) { guidePageChatIndexShow.value = true } return } else { // 如果缓存不存在或格式不对,可以考虑初始化缓存 guidePageChatIndexShow.value = true } } const sublimtOne = (data) => { console.log(data) if (data == 'guideChatIndexTypeOne') { const initData = { guideIndexTypeOne: true,//首页第一个引导页 guideIndexTypeTwo: true,//首页第二个引导页 guideChatIndexTypeOne: true,//聊天第一个引导页 guideChatIndexTypeTwo: false,//聊天第二个引导页 } uni.setStorageSync('userGuideType', initData) } else if (data == 'guideChatIndexTypeTwo') { const initData = { guideIndexTypeOne: true,//首页第一个引导页 guideIndexTypeTwo: true,//首页第二个引导页 guideChatIndexTypeOne: true,//聊天第一个引导页 guideChatIndexTypeTwo: true,//聊天第二个引导页 } uni.setStorageSync('userGuideType', initData) guidePageChatIndexShow.value = false } } const onLeftClick = () => { //需要判断输入框是否存在 let data = { chatId: chatId.value, value: value.value } // 取出本地存储,如果不存在就初始化为空数组 let chatValueInput = uni.getStorageSync('chatValueInputList') || []; // 查找是否已经存在相同 chatId if (data.chatId !== 0) { const index = chatValueInput.findIndex(item => item.chatId === data.chatId); if (index !== -1) { // 已存在,更新 value chatValueInput[index].value = data.value; } else { // 不存在,直接 push chatValueInput.push(data); } // 保存回本地存储 uni.setStorageSync('chatValueInputList', chatValueInput); } uni.navigateBack({ delta: 1 }) } const getproductList = async () => { let data = { pageIndex: 1, pageSize: 10, platform: '', type: 1 } const res : any = await productList(data) console.log('productList', res) shopCatNum.value = res.total } const agrenchatInfo = () => { // if (chatList.value.length > 0) { return uni.navigateTo({ url: '/pages/chat/index' }) } else { return uni.showToast({ title: '当前已是最新对话', icon: "none" }) } } const changeVlaueType = () => { inputType.value = inputType.value == 'text' ? 'video' : 'text' } const chatId = ref(0) const isfwqerr = ref(false) const isfwqerrChatValueIndex = ref(0)//服务器错误重新生成的数据id const isfwqerrAnswersIndex = ref(0)//对应chatvlaue下的答案数组的小标 const erragrenChatinfo = (itema : any) => {//数据服务器错误进行刷新操作 console.log('itemerragrenChatinfo', itema.answersInfo.id)//整个问题的id const chatValueIndex = chatList.value.findIndex(item => item.id == itema.id)//那条信息的idindex const answersIndex = chatList.value[chatValueIndex].answers.findIndex(item => item.id == itema.answersInfo.id) console.log('answersIndexanswersIndex', chatValueIndex, answersIndex) //数据边控 isfwqerr.value = true // chatList.value[chatValueIndex].answers[answersIndex].errmessage = '' // chatList.value[chatValueIndex].answersInfo.errmessage = '' isfwqerrChatValueIndex.value = chatValueIndex isfwqerrAnswersIndex.value = answersIndex stop() setTimeout(() => { start(chatList.value[chatValueIndex].answers[answersIndex].id) }, 1000) } const delChatList = (data : any) => {//删除对应的聊天详情里面的数据 console.log('data', data, chatList.value) const arr = chatList.value.filter(item => item.id !== data); chatList.value = arr RefreshId.value = '' messageId.value = 0 chatList.value.length == 0 ? isShowDownload.value = false : '' } const ascenrClick = (data : any) => { console.log('dataascenrClick', data) value.value = data pushchatList('text') } const pushchatList = throttle(async (type : any) => {//发送消息 console.log('value.value', value.value, name32Potison.value) if (RefreshId.value !== '') { return } if (type == 'text') { if (value.value == '') { return uni.showToast({ title: '发送内容为空', icon: "none" }) } } stop() uni.showLoading({ title: '加载中...' }) let data = { chatId: chatId.value,//,没有就是0 有就是对应的id message: type == 'text' ? value.value : '', voiceName: type == 'voice' ? name32Potison.value : '' } const res = await ChatAddMessage(data) console.log('resresresresres', res) if (Array.isArray(res.answers)) { res.answers = res.answers.map(item => ({ ...item, noteArr: [], sceneArr: [], sceneArrIndex: -1, isBotAsked: false, isPreferences: '', asOtherQuestArr: [], disclaimerText: '', errmessage: "" })) } chatId.value = res.pid res.answersInfo = { message: '', productDetails: '', noteArr: [],//通知的数据数组 sceneArr: [],//询问场景,通勤,运动这些 sceneArrIndex: -1, isBotAsked: false,//不想被问的按钮显示,false不显示,true显示 isPreferences: '',//将信息填写到我得喜好按钮 空就不显示,不空就显示 asOtherQuestArr: [],//你可能还想问的数据结构 disclaimerText: '',//免责说明 errmessage: "",//错误消息 } chatList.value.push(res) uni.hideLoading() console.log('chatList.value', chatList.value) makedowType.value = "wait" setTimeout(() => { start(res.answers[0].id) }, 1000) nextTick(() => { doScroll() }) value.value = '' name32Potison.value = '' }) const getNoice = throttle((content : any) => { value.value = content RefreshId.value = '' pushchatList('text') }) const refresherrefresh = () => { refresherTriggered.value = true messageId.value = chatList.value.length > 0 ? chatList.value[0].id : 0 console.log('出发了') chatChatMessageList('refresher') } const chatChatMessageList = async (type : any) => {//获取数据,进行时数据修改 let data = { chatId: Number(chatId.value), questionId: messageId.value, pageSize: 10 } console.log('datachatChatMessageList', data) let res = await chatChatMessage(data) const result = res.map(item => { item.answers = item.answers.map(itema => { if (itema.status == -100) { itema.errmessage = itema.error } else { itema.errmessage = '' } // ★ 关键修复:exMessage 在 itema 内! const ex = itema.exMessage || []; itema.sceneArr = ex.find(c => c.type === 'OPTIONS')?.body?.split(',') || []; itema.sceneArrIndex = -1; itema.noteArr = []; itema.asOtherQuestArr = ex.filter(c => c.type === 'QUESTION').map(c => c.body); itema.disclaimerText = ex.find(c => c.type === 'STATEMENT')?.body || ''; itema.isPreferences = ex.find(c => c.type === 'USERPROFILE')?.body || ''; return itema; }); return item; }); res = result; console.log('chatChatMessageres', res) // noteArr: [],//通知的数据数组 // sceneArr: [],//询问场景,通勤,运动这些 // sceneArrIndex: -1, // isBotAsked: false,//不想被问的按钮显示,false不显示,true显示 // isPreferences: '',//将信息填写到我得喜好按钮 空就不显示,不空就显示 // asOtherQuestArr: [],//你可能还想问的数据结构 // disclaimerText: ''//免责说明 res.map(item => { item.answersInfo = item.answers[0] item.form = 'detail' item.answersIndex = 0 }) console.log('reschatChatMessage', res, refresherTriggered.value) if (res.length > 0 && res) { chatList.value = res.reverse().concat(chatList.value) } console.log(res.reverse().concat(chatList.value)) // chatList.value.concat(newChatList) //res.reverse().concat(chatList.value) console.log('chatList.value', chatList.value) processData() if (type == 'refresher') { nextTick(() => { refresherTriggered.value = false }) return } else { } if (chatList.value > 0) { isShowDownload.value = true } else { isShowDownload.value = false } setTimeout(() => { doScroll() }, 100) setTimeout(() => { loadingskeleton.value = false }, 1000) // 取本地存储,如果不存在就空数组 const chatValueInput = uni.getStorageSync('chatValueInputList') || []; // 查找第一个匹配 chatId 的项 const matchedItem = chatValueInput.find(item => item.chatId == chatId.value); // console.log('matchedItem', matchedItem) // // 如果找到就赋值 value,否则为空字符串 value.value = matchedItem ? matchedItem.value : ''; // //判断最后一条数据的status // // 1. 取最后一条聊天记录 // // 1. 取最后一条聊天记录 // // 1. 获取最后一条 // const lastIndex = chatList.value.length - 1; // const lastItem = chatList.value[lastIndex]; // if (!lastItem?.answers?.length) return; // // 2. 获取最后一条 answer // const answerIndex = lastItem.answers.length - 1; // const lastAnswer = lastItem.answers[answerIndex]; // // 3. 状态为 1(正常),清空 message // if (lastAnswer.status === 0) { // console.log('lastAnswer.id', lastAnswer.id); // // 修改 message // lastAnswer.message = ""; // // 👉 关键:深拷贝触发响应式(重要) // chatList.value = JSON.parse(JSON.stringify(chatList.value)); // // 然后你可以执行 start() // start(lastAnswer.id); // } } const getchatUnreadCount = async () => {//获取提问的信息 let data = { } const res : any = await chatUnreadCount(data) console.log('resgetchatUnreadCount', res) getchatUnreadCountInfo.value = res isSpinning.value = false setTimeout(() => { loadingskeleton.value = false }, 1000) } const RefreshAnsweClick = async (itema : any) => {//重新生成对应的额回答 console.log('itema', itema) let data = { questionId: itema.id } const res : any = await chatReGenerate(data) console.log('RefreshAnswe', res) RefreshId.value = itema.id const index = chatList.value.findIndex(item => item.id == itema.id) console.log('index', index) console.log('chatList.value[index].answers', chatList.value[index]) chatList.value[index].answers.push({ message: '', productDetails: '', id: res.id, noteArr: [],//通知的数据数组 sceneArr: [],//询问场景,通勤,运动这些 sceneArrIndex: -1, isBotAsked: false,//不想被问的按钮显示,false不显示,true显示 isPreferences: '',//将信息填写到我得喜好按钮 空就不显示,不空就显示 asOtherQuestArr: [],//你可能还想问的数据结构 disclaimerText: '',//免责说明 errmessage: '',//错误信息 }) chatList.value[index].answersInfo = { message: '', productDetails: '', id: res.id, noteArr: [],//通知的数据数组 sceneArr: [],//询问场景,通勤,运动这些 sceneArrIndex: -1, isBotAsked: false,//不想被问的按钮显示,false不显示,true显示 isPreferences: '',//将信息填写到我得喜好按钮 空就不显示,不空就显示 asOtherQuestArr: [],//你可能还想问的数据结构 disclaimerText: '',//免责说明 errmessage: '',//错误信息 } console.log('chatList.value[index]', chatList.value[index]) setTimeout(() => { start(res.id) }, 100) } const jumpShopcat = async () => { uni.navigateTo({ url: '/pages/shop/shopCart' }) } const addShopCatInfo = async (item) => { let data = { productId: item.productId, action: 1 } const res : any = await productAddAction(data) console.log('res', res) uni.showToast({ title: '添加成功', icon: "none" }) getproductList() } const openPhone = () => { uni.chooseImage({ count: 6, //默认9 sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], //从相册选择 success: function (res) { console.log(JSON.stringify(res.tempFilePaths)); uni.navigateTo({ url: '/pages/chat/sanPhone?phone=' + res.tempFilePaths }) } }); } //长按语音输入 const onLongPress = () => { console.log('11111', 111) // uni.showToast({ // title: '长按事件触发', // icon: 'none' // }); const RECORDType = plus.navigator.checkPermission('RECORD') if (RECORDType !== 'authorized') { videoSandType.value = false uni.showModal({ title: '提示', content: '录音服务当前可能尚未打开,请去设置打开!', success: function (res) { if (res.confirm) { videoSandType.value = false openSystemSettings('record') } else if (res.cancel) { console.log('用户点击取消'); } } }); return } // const appAuthorizeSetting = uni.getAppAuthorizeSetting() // console.log('appAuthorizeSetting', appAuthorizeSetting) // if (appAuthorizeSetting.microphoneAuthorized !== 'authorized') { // uni.showModal({ // title: '提示', // content: '请授权打开麦克风权限,用于发送语音消息!', // success: function (res) { // if (res.confirm) { // recorderManager = null // recorderManager = uni.getRecorderManager(); // recorderManager.start(); // // recorderManager.stop(); // } else if (res.cancel) { // console.log('用户点击取消'); // } // } // }) // return // } if (RefreshId.value !== '') { return } startRecording() //videoSandType = true } const onTouchEnd = () => { console.log('2222', 222) inputType.value = 'video' videoSandType.value = false stopRecording() // stopAliSpeech() } const processData = () => {//处理异常的数据 //后端返回的数据为空时候 console.log('processDataChatValue', chatList.value) makedowType.value = '' chatList.value.map(item => { item.answersInfo.message ? '' : item.answersInfo.message = '服务器有点小拥挤,等下再来试试吧~' item.answersInfo.status = 1 }) } const isCancel = ref(false) // 是否滑出区域取消 const voiceSendType = ref(false) const touchcancel = (e : TouchEvent) => { const touch = e.touches[0] const y = touch.clientY // 在 App 端获取元素位置信息 const query = uni.createSelectorQuery() query.select('.viodeinfo').boundingClientRect((data : any) => { if (!data) return const { top, bottom } = data // 判断是否在范围内 if (y < top || y > bottom) { if (!isCancel.value) { isCancel.value = true console.log('手指移出按钮区域,准备取消') startColor.value = '#fff' endColor.value = '#fff' //如果在这里放弃的直接就是取消 voiceSendType.value = true } } else { if (isCancel.value) { isCancel.value = false startColor.value = '#92EEF9' endColor.value = '#3AD7FE' voiceSendType.value = false console.log('回到按钮区域,继续录音', startColor.value, endColor.value) } } }) query.exec() } const getchatGetVoiceUrl = async () => { let data = { name: name32Potison.value } console.log('name32Potison.value', name32Potison.value) const res : any = await chatGetVoiceUrl(data) console.log('chatGetVoiceUrlres', res) return res } const countdown = ref(57) // 当前剩余秒数 const timer : any = ref<number | null>(null) // 开始倒计时 const startCountdown = () => { if (timer.value) clearInterval(timer.value) countdown.value = 57 timer.value = setInterval(() => { countdown.value-- if (countdown.value <= 0) { clearInterval(timer.value!) timer.value = null countdown.value = 57 console.log('倒计时结束') stopRecording() } }, 1000) } // 停止倒计时 const stopCountdown = () => { if (timer.value) { clearInterval(timer.value) timer.value = null countdown.value = 57 recorderManager = null } } //语音 let recorderManager = null; // ✅ 在 plusReady 之后执行,确保 plus.io 可用 function startRecording() { // const appAuthorizeSetting = uni.getAppAuthorizeSetting() // console.log('appAuthorizeSetting', appAuthorizeSetting) recorderManager = uni.getRecorderManager(); recorderManager.onStart(() => { console.log("录音开始"); videoSandType.value = true name32Potison.value = generateRandomString() + '.mp3' startCountdown() }); recorderManager.onError((err) => { console.error("录音错误", err); videoSandType.value = false stopCountdown() }); recorderManager.onStop(async (res) => { console.log("录音停止", res); // res.tempFilePath 是录音生成的文件路径 // 你可以用这个文件去做上传或者转码 // voiceSendType.value = false // isCancel.value = false // stopCountdown() if (voiceSendType.value) { voiceSendType.value = false isCancel.value = false stopCountdown() return } // if (voiceSendType.value) { // voiceSendType.value = false // isCancel.value = false // stopCountdown() // return // } const formData = await getchatGetVoiceUrl() console.log('formData', formData) // const hasNetwork = await getUserNetWork() // if (!hasNetwork) { // return uni.showToast({ // title: '语音发送失败,网络异常!', // icon: "none" // }) // } uni.uploadFile({ url: 'https://voice-1371047959.cos.ap-shanghai.myqcloud.com', //仅为示例,非真实的接口地址 filePath: res.tempFilePath, name: 'file', formData: formData, success: (uploadFileRes) => { console.log('uploadFileRes.data', uploadFileRes, formData); // sendAudioToAli(uploadFileRes.data); stopCountdown() pushchatList('voice') } }); // }); recorderManager.start({ duration: 57000, // 最长录音时间 1 分钟 format: 'mp3', // 安卓端建议使用 'pcm' 或 'mp3' sampleRate: 16000, // 采样率 16kHz numberOfChannels: 1, // 单声道 encodeBitRate: 16 * 1000,// PCM16 }); } // 停止录音 function stopRecording() { if (recorderManager) { recorderManager.stop(); videoSandType.value = false recorderManager = null } } // const autoScroll = ref(true);//是否允许自动滚动 const TrackingOointData = ref('') onLoad((option : any) => { console.log('optionchatIndex', option) chatId.value = option.chatId ? option.chatId : 0 inputType.value = option.type ? option.type : 'text' value.value = option.value ? option.value : '' // chatChatMessageList() if (chatId.value !== 0) { chatChatMessageList() } if (value.value !== '') { pushchatList('text') } TrackingOointData.value = { "event": "ChatPage", startTime: Date.now(), id: chatId.value, endTime: '' } }) onShow(() => { getGuideType() getproductList() getchatUnreadCount() }) onMounted(() => { uni.onUserCaptureScreen((path) => { console.log("用户截屏了", path); uni.showToast({ title: "检测到截屏!", icon: "none" }); }); }) // 页面关闭或退出时关闭 gRPC onUnmounted(() => { // wsClient.close(); uni.offUserCaptureScreen(); stop() TrackingOointData.value.endTime = Date.now() usepublic.changeTrackingOointValue(TrackingOointData.value) console.log('trackingOointValue', usepublic.trackingOointValue) }) //开启sse const chatSSEClientRef = ref(null); const loading = ref(false); const openLoading = ref(false); const openCore = (response) => { openLoading.value = false; console.log("open sse:", response); } const errorCore = (err) => { console.log("error sse:", err); } const safeMarkdownContent = (msg) => { console.log(msg) } interface ProductItemListRequest { ids : number[] | null; } // 监听用户滚动事件(scroll-view) const handleScroll = (e : any) => { // console.log('出发了') // autoScroll.value = false }; const RefreshId = ref('') const productItemLists = async () => {//普通的请求数据图片 console.log('productChatArr.value', productChatArr.value); if (!productChatArr.value) { RefreshId.value = '' return }; const data : ProductItemListRequest = { ids: productChatArr.value, }; const res : any = await productItemList(data); // ------------ 关键逻辑:根据 RefreshId 判断 ------------ const last = chatList.value[chatList.value.length - 1]; last.answers[0].productDetails = res; last.answersInfo.productDetails = res; productChatArr.value = ''; console.log('获取到的商品信息(普通)', res); processData(); doScroll(); // ------------ 逻辑完全结束后再清空 RefreshId ------------ }; const productItemListsrealse = async () => {//重新生成的商品图片 console.log('productChatArr.value', productChatArr.value); if (!productChatArr.value) { RefreshId.value = '' return }; const data : ProductItemListRequest = { ids: productChatArr.value, }; const res : any = await productItemList(data); // ------------ 关键逻辑:根据 RefreshId 判断 ------------ const index = chatList.value.findIndex(item => item.id == RefreshId.value); chatList.value[index].answers[chatList.value[index].answers.length - 1].productDetails = res; chatList.value[index].answersInfo.productDetails = res; console.log('获取到的商品信息(重新生成)', res); productChatArr.value = ''; RefreshId.value = ''; processData(); doScroll(); return // ------------ 逻辑完全结束后再清空 RefreshId ------------ }; const productChatArr : any = ref(''); const makedowType = ref('')//是否是正常的信息 false不是 true是 const messageCore = (msg : any) => { console.log('msg获得到的信息', msg) if (RefreshId.value == '') {//不是重新生成的 if (isfwqerr.value) {//服务器错误 if (msg.data !== '' && msg.data !== 'event: keep' && msg.data !== 'event: end') { console.log('msg.datamsg.datamsg.datamsg.data', msg.data) const jsonStr = msg.data.replace(/^data:\s*/, ''); console.log('jsonStr', JSON.parse(jsonStr)) // const isfwqerrChatValueIndex = ref(0)//服务器错误重新生成的数据id // const isfwqerrAnswersIndex = ref(0)//对应chatvlaue下的答案数组的小标 // 先解析一次 const data = JSON.parse(jsonStr); const answer = chatList.value[isfwqerrChatValueIndex.value].answers[isfwqerrAnswersIndex.value]; const answerInfo = chatList.value[isfwqerrChatValueIndex.value].answersInfo; switch (data.Type) { case "message": { const msg = data.Body; if (!answer.message.includes(msg)) { answer.errmessage = '' answerInfo.errmessage = '' answer.message += msg; answerInfo.message += msg; } scrollTop.value = chatList.value.length * 150000; doScroll(); break; } case "product": { const arr = Array.from(new Set(data.Body.split(','))); productChatArr.value = arr; scrollTop.value = chatList.value.length * 150000; doScroll(); if (RefreshId.value !== '') { productItemListsrealse() } else { productItemLists() } break; } case "note": { const note = data.Body; if (!answer.noteMessage.includes(note)) { answer.noteMessage.push(note); answerInfo.noteArr.push(note); } doScroll(); break; } case "OPTIONS": { const optionsArr = Array.from(new Set(data.Body.split(','))); answer.noteMessage = optionsArr; answerInfo.sceneArr = optionsArr; doScroll(); break; } case "USERPROFILE": { answer.noteMessage = data.Body; answerInfo.isPreferences = data.Body; doScroll(); break; } case "QUESTION": { const question = data.Body; if (!answer.asOtherQuestArr.includes(question)) { answer.asOtherQuestArr.push(question); answerInfo.asOtherQuestArr.push(question); } doScroll(); break; } case "STATEMENT": { answerInfo.disclaimerText = data.Body; doScroll(); break; } case "err": { answer.errmessage = data.Body; answerInfo.errmessage = data.Body; doScroll(); stop(); break; } default: console.warn("未处理的 Type:", data.Type); break; } } return } if (msg.data !== '' && msg.data !== 'event: keep' && msg.data !== 'event: end') { console.log('msg.datamsg.datamsg.datamsg.data', msg.data) const jsonStr = msg.data.replace(/^data:\s*/, ''); console.log('jsonStr', JSON.parse(jsonStr)) // if (chatList.value[chatList.value.length - 1].answers[0].message = '') { // chatList.value[chatList.value.length - 1].answers[0].message = '' // chatList.value[chatList.value.length - 1].answersInfo.message = '' // } // item.noteArr = []//通知的数据数组 // item.sceneArr = []//询问场景,通勤,运动这些 // item.isBotAsked = false//不想被问的按钮显示,false不显示,true显示 // item.isPreferences = ''//将信息填写到我得喜好按钮,false不显示,true显示 // item.asOtherQuestArr = []//你可能还想问的数据结构 // item.disclaimerText = ''//免责说明 if (JSON.parse(jsonStr).Type == "message") { chatList.value[chatList.value.length - 1].answers[0].message += JSON.parse(jsonStr).Body chatList.value[chatList.value.length - 1].answersInfo.message += JSON.parse(jsonStr).Body scrollTop.value = chatList.value.length * 150000 doScroll() } else if (JSON.parse(jsonStr).Type == "product") { let arr = JSON.parse(jsonStr).Body.split(',') console.log('arrarr', arr) arr = JSON.stringify(arr).replace(/"/g, '') productChatArr.value = JSON.parse(arr); scrollTop.value = chatList.value.length * 150000 if (RefreshId.value !== '') { productItemListsrealse() } else { productItemLists() } doScroll() } else if (JSON.parse(jsonStr).Type == "note") {//通知队列 // makedowType.value = 'note' chatList.value[chatList.value.length - 1].answers[0].noteArr.push(JSON.parse(jsonStr).Body) chatList.value[chatList.value.length - 1].answersInfo.noteArr.push(JSON.parse(jsonStr).Body) doScroll() } else if (JSON.parse(jsonStr).Type == "OPTIONS") {//通勤什么运动这个 // makedowType.value = 'note' chatList.value[chatList.value.length - 1].answers[0].sceneArr = (JSON.parse(jsonStr).Body.split(',')) chatList.value[chatList.value.length - 1].answersInfo.sceneArr = (JSON.parse(jsonStr).Body.split(',')) doScroll() } else if (JSON.parse(jsonStr).Type == "USERPROFILE") {//填写之我得喜好 // makedowType.value = 'note' chatList.value[chatList.value.length - 1].answers[0].isPreferences = JSON.parse(jsonStr).Body chatList.value[chatList.value.length - 1].answersInfo.isPreferences = (JSON.parse(jsonStr).Body) doScroll() } else if (JSON.parse(jsonStr).Type == "QUESTION") {// // makedowType.value = 'note' chatList.value[chatList.value.length - 1].answers[0].asOtherQuestArr.push(JSON.parse(jsonStr).Body) chatList.value[chatList.value.length - 1].answersInfo.asOtherQuestArr.push(JSON.parse(jsonStr).Body) doScroll() } else if (JSON.parse(jsonStr).Type == "STATEMENT") {//免责说明 // makedowType.value = 'note' // chatList.value[chatList.value.length - 1].answersInfo.noteMessage = JSON.parse(jsonStr).Body chatList.value[chatList.value.length - 1].answersInfo.disclaimerText = JSON.parse(jsonStr).Body doScroll() } else if (JSON.parse(jsonStr).Type == "err") {//断掉 // makedowType.value = "err" // makedowType.value = "err" chatList.value[chatList.value.length - 1].answers[0].errmessage = JSON.parse(jsonStr).Body chatList.value[chatList.value.length - 1].answersInfo.errmessage = JSON.parse(jsonStr).Body scrollTop.value = chatList.value.length * 150000 doScroll() stop() } } return } else { console.log('msg获得到的信息重新获取这里是重新生成的数据', msg) let index = chatList.value.findIndex(item => item.id == RefreshId.value) const answersIndexAll = chatList.value[index].answers.length - 1 console.log('answersIndexAll', answersIndexAll) // chatList.value[index].answersInfo.message = '' // chatList.value[index].answersInfo.noteMessage = '' console.log('RefreshIdRefreshId', chatList.value[index].answersInfo) if (msg.data !== '' && msg.data !== 'event: keep' && msg.data !== 'event: end') { const jsonStr = msg.data.replace(/^data:\s*/, ''); console.log('jsonStr', JSON.parse(jsonStr)) if (JSON.parse(jsonStr).Type == "message") { // if (makedowType.value !== "message") { // chatList.value[index].answers[chatList.value[index].answers.length - 1].message = '' // chatList.value[index].answersInfo.message = '' // } const body = JSON.parse(jsonStr).Body const answer = chatList.value[index].answers[answersIndexAll] if (!answer.message.includes(body)) { answer.message += body chatList.value[index].answersInfo.message = answer.message } doScroll() } else if (JSON.parse(jsonStr).Type == "product") { let arr = JSON.parse(jsonStr).Body.split(',') console.log('arrarr', arr) arr = JSON.stringify(arr).replace(/"/g, '') productChatArr.value = JSON.parse(arr); if (RefreshId.value !== '') { productItemListsrealse() } else { productItemLists() } doScroll() } else if (JSON.parse(jsonStr).Type == "note") { const body = JSON.parse(jsonStr).Body const answer = chatList.value[index].answers[answersIndexAll] if (!answer.noteArr.includes(body)) { answer.noteArr.push(body) chatList.value[index].answersInfo.noteArr = [...answer.noteArr] } doScroll() } else if (JSON.parse(jsonStr).Type == "OPTIONS") {//通勤什么运动这个 // makedowType.value = 'note' // chatList.value[chatList.value.length - 1].answersInfo.noteMessage = JSON.parse(jsonStr).Body const bodyArr = JSON.parse(jsonStr).Body.split(',') const answer = chatList.value[index].answers[answersIndexAll] // 初始化数组(防止未定义) answer.sceneArr = answer.sceneArr || [] chatList.value[index].answersInfo.sceneArr = chatList.value[index].answersInfo.sceneArr || [] // 遍历累加去重 bodyArr.forEach(item => { if (!answer.sceneArr.includes(item)) { answer.sceneArr.push(item) } }) chatList.value[index].answersInfo.sceneArr = [...answer.sceneArr] doScroll() } else if (JSON.parse(jsonStr).Type == "QUESTION") {// // makedowType.value = 'note' const body = JSON.parse(jsonStr).Body const answer = chatList.value[index].answers[answersIndexAll] if (!answer.asOtherQuestArr.includes(body)) { answer.asOtherQuestArr.push(body) chatList.value[index].answersInfo.asOtherQuestArr = [...answer.asOtherQuestArr] } doScroll() } else if (JSON.parse(jsonStr).Type == "STATEMENT") {//免责说明 // makedowType.value = 'note' chatList.value[index].answersInfo.disclaimerText = JSON.parse(jsonStr).Body doScroll() } else if (JSON.parse(jsonStr).Type == "err") {//断掉 // makedowType.value = "err" makedowType.value = "err" chatList.value[index].answers[answersIndexAll].errmessage = JSON.parse(jsonStr).Body chatList.value[index].answersInfo.errmessage = chatList.value[index].answers[answersIndexAll].errmessage doScroll() stop() } } } // scrollTop.value = (chatList.value.length + msg.data.length * 2) * 100000 // 假设每条消息大约 100rpx 高 // console.log('autoScroll.value', autoScroll.value) if (msg.data == 'event: end') { console.log('makedowType.valueend ', makedowType.value) stop() console.log('productChatArr.value ', productChatArr.value) } } const finishCore = () => { console.log("finish sse") loading.value = false; // processData() stop() } // 统一滚动函数 —— 放在 script 顶部附近 const doScroll = (force = false) => { // 如果用户手动滚动过并且没有强制滚动,则不自动滚 // if (!autoScroll.value && !force) return; // 等待两次 nextTick,确保列表 DOM 完全渲染(SSE 流异步数据较稳) nextTick(() => { // 设为一个大数,scroll-view 会剪裁到最大滚动位置 // 也可以用具体计算或查询容器高度(见注释) scrollTop.value = chatList.value.length * Math.floor(Math.random() * 9000000000) + 1000000000; }); }; const start = (messageId : number) => { if (loading.value) return; const localInfo = uni.getStorageSync('localInfo') const headetToken = generateRandomString() const newDate = Date.now() openLoading.value = true; loading.value = true; chatSSEClientRef.value.startChat({ /** * 将它换成你的地址 * 注意: * 如果使用 sse-server.js 要在手机端使用的话,请确保你的手机电脑处在一个局域网下并且是正常的ip地址 */ url: `https://api.wonsouai.com/api/custom/chat/GetMessage?messageId=${messageId}`, // 请求头 headers: { 'Content-Type': 'application/json', 'lng': localInfo?.longitude, 'lat': localInfo?.latitude, 'authorization': getToken() ? `Bearer ${getToken()}` : '', 'timestamp': newDate, 'secretKey': headetToken, 'ClientPlatform': '1', 'secret': md5Encrypt(processString(headetToken) + newDate), }, // 默认为 post method: 'get', // body: { // "stream":true, // "model": "deepseek-chat", // "messages": [ // {"role": "system", "content": "你是来自艺咖科技的数字员工,你的名字叫小咖。"}] // } }) makedowType.value = 'wait' } const stop = async () => { makedowType.value = "" let data = { messageId: chatList.value[chatList.value.length - 1].answers[chatList.value[chatList.value.length - 1].answers.length - 1].id } console.log('datadatadata', data) const res : any = await chatStopMessage(data) console.log("stop停止的接口", res); chatSSEClientRef.value.stopChat() console.log('chatStopList', chatList.value) chatList.value[chatList.value.length - 1].answersInfo.status = 1; chatList.value[chatList.value.length - 1].answers[chatList.value[chatList.value.length - 1].answers.length - 1].status = 1 processData() refresherTriggered.value = false isfwqerr.value = false isfwqerrChatValueIndex.value = '' isfwqerrAnswersIndex.value = '' nextTick(() => { // 设为一个大数,scroll-view 会剪裁到最大滚动位置 // 也可以用具体计算或查询容器高度(见注释) scrollTop.value = 1000000000000; }); } </script> <style lang="less" scoped> .chatIndex { background: #F2F2F2; width: 100vw; height: 100vh; padding: 0 0; position: relative; overflow: hidden; .back-icon { width: 48rpx; height: 48rpx; } .chatIndexLogo { width: 200rpx; height: 200rpx; display: flex; margin-left: 20rpx; } .chatIndexLogoText { font-size: 36rpx; font-weight: bold; display: inline-block; height: 88rpx; line-height: 44rpx; margin-top: 52rpx; margin-left: 5rpx; background: linear-gradient(135deg, #8128FF, #2866F6, #8128FF); /* 两头紫,中间蓝,流动时过渡更顺滑 */ background-size: 200% auto; /* 背景扩大,才有流动效果 */ -webkit-background-clip: text; -webkit-text-fill-color: transparent; animation: flowLight 1s linear infinite; /* 控制流光速度 */ } @keyframes flowLight { 0% { background-position: 0% center; } 100% { background-position: -200% center; } } .back-logo { width: 78rpx; height: 100rpx; display: flex; margin-left: 16rpx; } .icon-more { width: 48rpx; height: 48rpx; display: flex; margin-right: 20rpx; } .chatIndex_card { width: calc(686rpx - 76rpx); // height: calc(588rpx - 96rpx); background: #FFFFFF; border-radius: 40rpx; padding: 48rpx 48rpx; .blueColor { font-family: Alimama Agile VF, Alimama Agile VF; font-weight: bold; font-size: 44rpx; color: #2256DE; } .chatIndex_card_tips { font-family: PingFang SC, PingFang SC; font-weight: bold; font-size: 32rpx; color: #000000; margin-top: 8rpx; } .changechatindex_icon { width: 36rpx; height: 36rpx; flex-shrink: 0; } /* 无限旋转 */ .spinning { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .changechatindex_text { font-family: PingFang SC, PingFang SC; font-weight: 400; font-size: 28rpx; height: 36rpx; line-height: 36rpx; color: #333333; margin-left: 16rpx; } .cardItem { width: calc(622rpx - 64rpx); // height: calc(80rpx - 48rpx); background: rgba(34, 86, 222, 0.1); padding: 24rpx 32rpx; border-radius: 16rpx; margin: 32rpx 0 0 0; display: inline-flex; .cardItem_img { width: 36rpx; height: 36rpx; display: flex; flex-shrink: 0; margin-right: 12rpx; } .cardItem_text { font-family: PingFang SC, PingFang SC; font-weight: 500; font-size: 28rpx; margin-top: 6rpx; color: #333333; } } } .sublimt { height: 184rpx; width: 750rpx; position: fixed; bottom: 0; left: 0; right: 0; background: #F2F2F2; // background: red; .downloadScooltop { width: 92rpx; height: 92rpx; position: absolute; top: -70px; left: 330rpx; } .shopcat { width: 196rpx; height: 72rpx; border-radius: 16rpx 16rpx 16rpx 16rpx; border: 2rpx solid #D6DADE; position: absolute; left: 32rpx; bottom: 170rpx; background: #F2F2F2; .shopcatNumInfo { position: relative; .shopcat_img { width: 32rpx; height: 32rpx; } .shopcatNum { position: absolute; padding: 0 8rpx; display: inline-flex; height: 28rpx; background: #F2525E; border-radius: 50rpx; border: 2px solid #FFFFFF; font-family: PingFang SC, PingFang SC; font-weight: 500; font-size: 20rpx; top: -20rpx; right: -20rpx; color: #FFFFFF; line-height: 28rpx; } } .shopcat_text { font-family: PingFang SC, PingFang SC; font-weight: 500; font-size: 28rpx; margin-left: 16rpx; color: #000000; } } .SandTypeInfo { position: absolute; left: 50rpx; bottom: 170rpx; width: calc(686rpx - 48rpx); font-family: PingFang SC, PingFang SC; font-weight: 400; font-size: 28rpx; text-align: center; } .index_button { width: calc(686rpx - 48rpx); background: #FFFFFF; box-shadow: 0rpx 8rpx 20rpx 0rpx rgba(150, 174, 226, 0.25); border-radius: 20rpx 20rpx 20rpx 20rpx; position: absolute; bottom: 48rpx; left: 32rpx; right: 32rpx; padding: 30rpx 32rpx; .index_button_img { width: 48rpx; height: 48rpx; display: flex; margin: 0 16rpx; } .inputValue { font-family: PingFang SC, PingFang SC; font-weight: bold; font-size: 32rpx; color: #333333; flex: 1; margin: 0 10rpx; } .video_tips { font-family: PingFang SC, PingFang SC; font-weight: 400; font-size: 28rpx; height: 45rpx; line-height: 45rpx; color: #1D1D1D; flex: 1; text-align: center; margin: 0 10rpx; } } } } </style>梳理一下,给我讲解一下
最新发布
12-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值