From aebf40b7e70951bea94fcbc2b13e53b26132adf1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 1 Apr 2026 11:19:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E6=9B=B4=E6=96=B0AI=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E8=81=8A=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 2 +- .../hs-scroll-indicator.js | 1 + .../hs-scroll-indicator.json | 4 + .../hs-scroll-indicator.wxml | 1 + .../hs-scroll-indicator.wxss | 1 + dist/build/mp-weixin/pages/ai/chat.js | 1 + dist/build/mp-weixin/pages/ai/chat.json | 8 + dist/build/mp-weixin/pages/ai/chat.wxml | 1 + dist/build/mp-weixin/pages/ai/chat.wxss | 1 + dist/build/mp-weixin/static/svg/copy.svg | 1 + dist/dev/mp-weixin/app.js | 2 +- dist/dev/mp-weixin/app.json | 2 +- .../dev/mp-weixin/common/libraries/request.js | 2 +- dist/dev/mp-weixin/common/vendor.js | 4 + dist/dev/mp-weixin/pages/ai/chat.js | 926 ++++++++++++++++++ dist/dev/mp-weixin/pages/ai/chat.json | 6 + dist/dev/mp-weixin/pages/ai/chat.wxml | 1 + dist/dev/mp-weixin/pages/ai/chat.wxss | 375 +++++++ dist/dev/mp-weixin/pages/ai/index.js | 27 - dist/dev/mp-weixin/pages/ai/index.json | 4 - dist/dev/mp-weixin/pages/ai/index.wxml | 1 - dist/dev/mp-weixin/pages/ai/index.wxss | 26 - .../pages/index/components/AiInput.js | 11 +- .../pages/index/components/AiInput.wxml | 2 +- .../pages/index/components/AiInput.wxss | 4 +- dist/dev/mp-weixin/static/svg/copy.svg | 1 + src/pages.json | 4 +- src/pages/index/components/AiInput.vue | 17 +- src/static/svg/copy.svg | 1 + 29 files changed, 1356 insertions(+), 81 deletions(-) create mode 100644 dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.js create mode 100644 dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.json create mode 100644 dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxml create mode 100644 dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxss create mode 100644 dist/build/mp-weixin/pages/ai/chat.js create mode 100644 dist/build/mp-weixin/pages/ai/chat.json create mode 100644 dist/build/mp-weixin/pages/ai/chat.wxml create mode 100644 dist/build/mp-weixin/pages/ai/chat.wxss create mode 100644 dist/build/mp-weixin/static/svg/copy.svg create mode 100644 dist/dev/mp-weixin/pages/ai/chat.js create mode 100644 dist/dev/mp-weixin/pages/ai/chat.json create mode 100644 dist/dev/mp-weixin/pages/ai/chat.wxml create mode 100644 dist/dev/mp-weixin/pages/ai/chat.wxss delete mode 100644 dist/dev/mp-weixin/pages/ai/index.js delete mode 100644 dist/dev/mp-weixin/pages/ai/index.json delete mode 100644 dist/dev/mp-weixin/pages/ai/index.wxml delete mode 100644 dist/dev/mp-weixin/pages/ai/index.wxss create mode 100644 dist/dev/mp-weixin/static/svg/copy.svg create mode 100644 src/static/svg/copy.svg diff --git a/.env.development b/.env.development index fca9954..00c0ba4 100644 --- a/.env.development +++ b/.env.development @@ -1,5 +1,5 @@ NODE_ENV= development -VITE_HTTP_BASE_URL = http://10.39.13.78:8001/api/ +VITE_HTTP_BASE_URL = http://10.39.13.78:8002/api/ # VITE_HTTP_BASE_URL = https://test-weapp-api.linyikj.com.cn/api/ # VITE_HTTP_BASE_URL = https://weapp-api.linyikj.com.cn/api/ VITE_ACCESS_TOKEN_KEY= 'ACCESS_TOKEN_CUSTOMER' \ No newline at end of file diff --git a/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.js b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.js new file mode 100644 index 0000000..7854f14 --- /dev/null +++ b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.js @@ -0,0 +1 @@ +"use strict";const l=require("../../common/vendor.js"),o=l.defineComponent({__name:"hs-scroll-indicator",props:["scroll_view"],setup(o){const e=o;let n=l.index.getWindowInfo();const t=l.ref(0);return l.watch((()=>{var l;return null==(l=null==e?void 0:e.scroll_view)?void 0:l.scrollLeft}),(l=>{let o=null==e?void 0:e.scroll_view,r=(null==o?void 0:o.scrollWidth)-(null==n?void 0:n.windowWidth),i=(null==o?void 0:o.scrollLeft)/r*100;t.value=i/100*25})),(l,o)=>({a:`${t.value}%`})}}),e=l._export_sfc(o,[["__scopeId","data-v-808db072"]]);wx.createComponent(e); diff --git a/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.json b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxml b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxml new file mode 100644 index 0000000..9312083 --- /dev/null +++ b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxss b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxss new file mode 100644 index 0000000..485d100 --- /dev/null +++ b/dist/build/mp-weixin/components/hs-scroll-indicator/hs-scroll-indicator.wxss @@ -0,0 +1 @@ +.scroll_indicator_container.data-v-808db072{width:100%}.scroll_indicator.data-v-808db072{background-color:#eee;width:60rpx;margin:0 auto;margin-top:15rpx}.scroll_indicator .indicator.data-v-808db072{background-color:#304483;border-radius:100rpx;width:35rpx;height:8rpx;position:relative} diff --git a/dist/build/mp-weixin/pages/ai/chat.js b/dist/build/mp-weixin/pages/ai/chat.js new file mode 100644 index 0000000..0d80aad --- /dev/null +++ b/dist/build/mp-weixin/pages/ai/chat.js @@ -0,0 +1 @@ +"use strict";const e=require("../../common/vendor.js");require("../../common/libraries/request.js");const t=require("../../common/store/useWeAppAuthStore.js");require("../../common/store/useWorkStore.js");const o=require("../../common/libraries/upload.js");if(require("../../gen/Apis.js"),require("../../common/libraries/setTabBar.js"),require("../../common/libraries/apiLoading.js"),!Array){e.resolveComponent("uni-icons")()}Math;const a=e.defineComponent({__name:"chat",setup(a){e.dayjs.extend(e.relativeTime),e.dayjs.locale("zh-cn");const s=t.useWeAppAuthStore(),n=e.ref(""),l=e.ref([]),r=e.ref(!1),i=e.ref([{role:"ai",content:"您好!我是物业客服,有什么可以帮助您的吗?",quickQuestions:[]}]),u=e.ref(!1),c=e.ref("正在输入中...");let m=null,g=e.ref(!1);const d="https://kf-api-test.linyikj.com.cn",p=e.ref(1),v=e.ref(10),y=e.ref(!0),_=e.ref(!1),f=e.ref(""),h=e.ref(0),k=e.ref([]),q=e.ref(""),S=(t,o)=>{e.index.previewImage({current:t,urls:o})},C=t=>{t?e.index.setClipboardData({data:t,success:()=>{e.index.showToast({title:"复制成功",icon:"success"})},fail:()=>{e.index.showToast({title:"复制失败",icon:"none"})}}):e.index.showToast({title:"暂无内容可复制",icon:"none"})},w=t=>{if(!t)return"";try{const o=e.dayjs(t);return o.isValid()?o.fromNow():""}catch(o){return console.error("时间格式化错误:",o),""}},A=()=>{e.nextTick$1((()=>{e.index.pageScrollTo({scrollTop:999999,duration:300})}))},x=async(t=1)=>{var o,a;const n=null==(a=null==(o=s.data)?void 0:o.user)?void 0:a.phone;if(n){if(!_.value)try{_.value=!0;const o=(t-1)*v.value;h.value=o,console.log("分页信息 - 页码:",t,"每页数量:",v.value,"计算出的offset:",o);const a=await e.index.request({url:`${d}/api/public/customer/init`,method:"POST",data:{platform:"property",platform_user_id:n,limit:v.value,offset:o}});if(200===a.statusCode){const e=a.data;let s=[];if(e.conversation&&e.conversation.id?(f.value=e.conversation.id,console.log("获取到会话ID:",f.value)):e.conversation_id?(f.value=e.conversation_id,console.log("获取到会话ID:",f.value)):e.conversations&&e.conversations.id&&(f.value=e.conversations.id,console.log("获取到会话ID:",f.value)),e.messages&&Array.isArray(e.messages)?(s=e.messages.map((e=>{const t={role:"customer"===e.sender_type?"user":"ai",content:e.message||e.content||"",created_at:e.created_at||e.timestamp||(new Date).toISOString()};e.message_type&&(t.message_type=e.message_type);let o=null;return e.image_url&&Array.isArray(e.image_url)?o=e.image_url:e.images&&Array.isArray(e.images)?o=e.images:e.metadata&&e.metadata.image_url&&Array.isArray(e.metadata.image_url)&&(o=e.metadata.image_url),o&&(t.image_url=o,t.images=o),e.metadata&&(t.metadata=e.metadata),t})),console.log("从messages字段获取到历史记录:",s.length)):e.data&&Array.isArray(e.data)?(s=e.data.map((e=>{const t={role:"customer"===e.sender_type?"user":"ai",content:e.message||e.content||"",created_at:e.created_at||e.timestamp||(new Date).toISOString()};e.message_type&&(t.message_type=e.message_type);let o=null;return e.image_url&&Array.isArray(e.image_url)?o=e.image_url:e.images&&Array.isArray(e.images)?o=e.images:e.metadata&&e.metadata.image_url&&Array.isArray(e.metadata.image_url)&&(o=e.metadata.image_url),o&&(t.image_url=o,t.images=o),e.metadata&&(t.metadata=e.metadata),t})),console.log("从data字段获取到历史记录:",s.length)):e.list&&Array.isArray(e.list)&&(s=e.list.map((e=>{const t={role:"customer"===e.sender_type?"user":"ai",content:e.message||e.content||"",created_at:e.created_at||e.timestamp||(new Date).toISOString()};e.message_type&&(t.message_type=e.message_type);let o=null;return e.image_url&&Array.isArray(e.image_url)?o=e.image_url:e.images&&Array.isArray(e.images)?o=e.images:e.metadata&&e.metadata.image_url&&Array.isArray(e.metadata.image_url)&&(o=e.metadata.image_url),o&&(t.image_url=o,t.images=o),e.metadata&&(t.metadata=e.metadata),t})),console.log("从list字段获取到历史记录:",s.length)),1===t){s.length>0?(i.value=s,console.log("第一页历史记录已加载,总共",s.length,"条消息")):(console.log("没有历史记录"),i.value=[]);const e=!y.value;Q(e)&&D(e)}else s.length>0&&setTimeout((()=>{i.value.unshift(...s),console.log("加载更多历史记录,新增",s.length,"条消息,总共",i.value.length,"条")}),300);const n=e.total||e.count||e.total_count||0;y.value=n>i.value.length,p.value=t,t>1&&!y.value&&(console.log("分页加载完成,当前页:",t,"hasMore:",y.value,"展示开场白"),setTimeout((()=>{D(!0)}),500)),console.log("第",t,"页数据加载完成,offset:",o,", limit:",v.value,", 总数据:",n,", 当前已加载:",i.value.length,", 是否有更多:",y.value),1===t&&(setTimeout((()=>{A()}),100),console.log("[Customer] 历史记录加载完成,开始建立WebSocket连接"),T())}else console.error("获取历史记录失败,状态码:",a.statusCode)}catch(l){console.error("获取历史记录异常:",l)}finally{_.value=!1}}else console.log("用户未登录,无法获取历史记录")};e.onPullDownRefresh((async()=>{if(_.value||!y.value)return console.log("正在加载或没有更多数据"),void e.index.stopPullDownRefresh();console.log("触发下拉分页,加载更多历史记录");const t=p.value+1;try{await x(t),await new Promise((e=>setTimeout(e,500)))}catch(o){console.error("加载历史记录失败:",o)}finally{e.index.stopPullDownRefresh()}}));const T=()=>{if(!m)try{const t="wss://kf-api-test.linyikj.com.cn/ws/socket.io/?EIO=4&transport=websocket";console.log("正在建立Socket.IO连接"),console.log("连接URL:",t),m=e.index.connectSocket({url:t,header:{"content-type":"application/json"}}),e.index.onSocketOpen((t=>{console.log("[Customer] WebSocket connected",t),g.value=!0;e.index.sendSocketMessage({data:'40/customer,{"jwt":""}',success:()=>{console.log("[Customer] Socket.IO连接包发送成功"),f.value&&setTimeout((()=>{const t=`42/customer,["join_conversation",{"conversation_id":"${f.value}"}]`;console.log("[Customer] 加入会话房间:",t),e.index.sendSocketMessage({data:t,success:()=>{console.log("[Customer] 成功加入会话房间")},fail:e=>{console.error("[Customer] 加入会话房间失败:",e)}})}),200)},fail:e=>{console.error("[Customer] Socket.IO连接包发送失败:",e)}})})),e.index.onSocketError((e=>{console.error("[Customer] WebSocket error:",e),g.value=!1,m=null})),e.index.onSocketClose((e=>{console.log("[Customer] WebSocket disconnected",e),g.value=!1,m=null})),e.index.onSocketMessage((t=>{console.log("收到WebSocket原始消息:",t),console.log("消息数据类型:",typeof t.data),console.log("消息内容:",t.data);const o=t.data;if("string"==typeof o)if(o.startsWith("42/customer,")){const e=o.substring(12);try{const t=JSON.parse(e);console.log("解析Socket.IO事件:",t),b(t)}catch(a){console.error("解析Socket.IO数据失败:",a,"原始数据:",e)}}else if(o.startsWith("40/customer"))console.log("Socket.IO连接确认");else if(o.startsWith("2"))e.index.sendSocketMessage({data:"3"});else if(o.startsWith("0"))console.log("Socket.IO握手成功");else try{const e=JSON.parse(o);console.log("解析JSON消息:",e),O(e)}catch(a){console.log("非JSON格式的字符串消息:",o)}else"object"==typeof o?(console.log("收到对象类型消息:",o),j(o)):console.log("收到其他类型消息,类型:",typeof o)}))}catch(t){console.error("初始化Socket.IO异常:",t),g.value=!1,m=null}},b=e=>{if(Array.isArray(e)&&e.length>=2){const t=e[0],o=e[1];console.log("[Customer] Socket.IO事件类型:",t,"事件数据:",o),"new_message"===t?W(o):"join_conversation_response"===t?console.log("[Customer] Joined room:",o):"message"===t?W(o):"typing"===t?I(o):console.log("[Customer] 未处理的Socket.IO事件类型:",t)}},O=e=>{console.log("[Customer] 处理JSON消息:",e),"message"===e.type||"new_message"===e.event?W(e.data||e):"typing"===e.type||"typing"===e.event?I(e.data||e):e.message||e.content?W(e):console.log("[Customer] 未识别的JSON消息结构:",e)},j=e=>{console.log("[Customer] 处理对象消息:",e),e.message||e.content||e.text?W(e):void 0!==e.is_typing?I(e):console.log("[Customer] 未识别的对象消息结构:",e)},I=e=>{console.log("[Customer] 处理typing指示器:",e),e.is_typing&&"staff"===e.user_type?(u.value=!0,c.value="客服正在输入..."):u.value=!1},Q=(e=!1)=>0!==k.value.length&&(0===i.value.length||!!(e&&i.value.length>0)),D=(e=!1)=>{if(0===k.value.length)return;if(!Q(e))return;if(i.value.find((e=>"ai"===e.role&&e.quickQuestions&&e.quickQuestions.length>0)))return void console.log("[Customer] 开场白已存在,跳过添加");const t={role:"ai",content:"您好!我是物业客服,有什么可以帮助您的吗?",quickQuestions:k.value,created_at:(new Date).toISOString()};console.log("[Customer] 创建开场白消息,是否最后一页:",e),0===i.value.length?(i.value=[t],console.log("[Customer] 设置开场白为第一条消息")):e&&(i.value.unshift(t),console.log("[Customer] 已加载到最后一页,在第一条消息前面插入开场白"))},W=e=>{if(console.log("[Customer] 处理消息数据:",e),"customer"===e.sender_type||"user"===e.role)return void console.log("[Customer] 过滤掉客户消息");let t="";if(t="string"==typeof e?e:e.message||e.content||e.text||e.body||"收到回复","string"==typeof t&&(t=t.replace(/↵/g,"\n").replace(/\\n/g,"\n")),console.log("[Customer] 收到新消息内容:",t),t===q.value)return console.log("[Customer] 消息与HTTP返回的消息相同,跳过显示"),void(q.value="");u.value=!1;const o={role:"ai",content:t,created_at:e.created_at||e.timestamp||e.time||(new Date).toISOString()};e.suggested_questions&&Array.isArray(e.suggested_questions)&&e.suggested_questions.length>0?o.quickQuestions=e.suggested_questions:e.quickQuestions&&Array.isArray(e.quickQuestions)&&e.quickQuestions.length>0&&(o.quickQuestions=e.quickQuestions),void 0!==e.need_confirmation&&(o.needConfirmation=e.need_confirmation),void 0!==e.confirmation_type&&(o.confirmationType=e.confirmation_type),i.value.push(o),A()},P=(e,t)=>{void 0===e.selectedConfirmation&&(e.selectedConfirmation=t,setTimeout((()=>{n.value=t,F()}),300))},z=async()=>{if(l.value.length>=9)e.index.showToast({title:"最多选择9张图片",icon:"none"});else try{const t=9-l.value.length,a=await o.upload(t,2,["image"]);if(a&&Array.isArray(a)&&a.length>0){const t=a.map((e=>e.url));l.value.push(...t),e.index.showToast({title:"图片上传成功",icon:"success"})}}catch(t){console.error("选择图片失败:",t),e.index.showToast({title:"选择图片失败",icon:"none"})}},F=async()=>{var t,o,a,r,m,p;const v=n.value.trim(),y=l.value.length>0;if(!v&&!y||u.value)return;const _=null==(o=null==(t=s.data)?void 0:t.user)?void 0:o.phone,h=null==(r=null==(a=s.data)?void 0:a.selected_house)?void 0:r.asset_projects_id,k=null==(p=null==(m=s.data)?void 0:m.selected_house)?void 0:p.full_name;if(!_||!h)return void e.index.showToast({title:"请先登录并绑定房屋",icon:"none"});g.value||(T(),await new Promise((e=>setTimeout(e,1e3))));const S=l.value;let C="text";y&&v?C="mixed":y&&!v&&(C="image");const w={role:"user",content:v||"",created_at:(new Date).toISOString(),message_type:C,image_url:S.length>0?S:void 0,images:S.length>0?S:void 0};i.value.push(w),n.value="",l.value=[],A(),v&&v.includes("查询")?c.value="正在查询中请稍后...":c.value="正在输入中...",u.value=!0;try{const t=await e.index.request({url:`${d}/api/public/chat`,method:"POST",data:{platform:"property",message:v||"",tenant_project_id:h,tenant_project_name:k||"",platform_user_id:_,conversation_id:f.value,message_type:C,image_url:S.length>0?S:void 0},header:{"Content-Type":"application/json"}});if(200!==t.statusCode)throw new Error("请求失败");{const e=t.data;console.log("[Customer] HTTP接口响应数据:",e);let o=null,a=[],s=!1,n=null;if(e.reply&&"string"==typeof e.reply?(o=e.reply,a=e.suggested_questions||[],s=e.need_confirmation||!1,n=e.confirmation_type||null):e.reply&&e.reply.message?(o=e.reply.message,a=e.reply.suggested_questions||e.suggested_questions||[],s=e.reply.need_confirmation||e.need_confirmation||!1,n=e.reply.confirmation_type||e.confirmation_type||null):e.reply&&e.reply.content?(o=e.reply.content,a=e.reply.suggested_questions||e.suggested_questions||[],s=e.reply.need_confirmation||e.need_confirmation||!1,n=e.reply.confirmation_type||e.confirmation_type||null):e.message?(o=e.message,a=e.suggested_questions||[],s=e.need_confirmation||!1,n=e.confirmation_type||null):e.content?(o=e.content,a=e.suggested_questions||[],s=e.need_confirmation||!1,n=e.confirmation_type||null):e.answer?(o=e.answer,a=e.suggested_questions||[],s=e.need_confirmation||!1,n=e.confirmation_type||null):e.response?(o=e.response,a=e.suggested_questions||[],s=e.need_confirmation||!1,n=e.confirmation_type||null):"string"==typeof e&&(o=e),o){console.log("[Customer] HTTP接口返回回答消息:",o),console.log("[Customer] 建议问题:",a),console.log("[Customer] 需要确认:",s,"确认类型:",n);let e=o;"string"==typeof e&&(e=e.replace(/↵/g,"\n").replace(/\\n/g,"\n")),q.value=e,u.value=!1,i.value.push({role:"ai",content:e,created_at:(new Date).toISOString(),quickQuestions:a.length>0?a:void 0,needConfirmation:s,confirmationType:n}),A()}else console.log("[Customer] 消息发送成功,等待WebSocket回复")}}catch(x){console.error("发送消息失败:",x),e.index.showToast({title:"发送失败,请稍后重试",icon:"none"}),i.value.push({role:"ai",content:"抱歉,网络连接出现问题,请稍后再试。"}),u.value=!1,A()}};return e.onUnmounted((()=>{(m||g.value)&&(e.index.closeSocket(),m=null,g.value=!1,console.log("页面卸载,WebSocket连接已关闭"))})),e.onLoad((async()=>{k.value=await(async()=>{try{const t=await e.index.request({url:`${d}/api/public/quick-questions`,method:"GET"});if(200===t.statusCode){const e=t.data;let o=[];return e.data&&Array.isArray(e.data)?o=e.data.map((e=>e.question||e.title||e.text||e)):e.questions&&Array.isArray(e.questions)?o=e.questions.map((e=>e.question||e.title||e.text||e)):Array.isArray(e)&&(o=e.map((e=>e.question||e.title||e.text||e))),console.log("获取到开场白按钮数据:",o),o}return console.error("获取开场白数据失败,状态码:",t.statusCode),[]}catch(t){return console.error("获取开场白数据异常:",t),[]}})(),console.log("页面加载时已获取开场白按钮数据:",k.value.length),x(1)})),(t,o)=>e.e({a:e.f(i.value,((t,o,a)=>{return e.e({a:"ai"===t.role},(t.role,{}),{b:t.image_url&&t.image_url.length>0},t.image_url&&t.image_url.length>0?{c:e.f(t.image_url,((o,a,s)=>({a:o,b:e.o((e=>S(o,t.image_url)),a),c:a}))),d:1===t.image_url.length?"widthFix":"aspectFill",e:1===t.image_url.length?1:""}:t.images&&t.images.length>0?{g:e.f(t.images,((o,a,s)=>({a:o,b:e.o((e=>S(o,t.images)),a),c:a}))),h:1===t.images.length?"widthFix":"aspectFill",i:1===t.images.length?1:""}:t.metadata&&t.metadata.image_url&&t.metadata.image_url.length>0?{k:e.f(t.metadata.image_url,((o,a,s)=>({a:o,b:e.o((e=>S(o,t.metadata.image_url)),a),c:a}))),l:1===t.metadata.image_url.length?"widthFix":"aspectFill",m:1===t.metadata.image_url.length?1:""}:{},{f:t.images&&t.images.length>0,j:t.metadata&&t.metadata.image_url&&t.metadata.image_url.length>0,n:t.content&&t.content.trim()},t.content&&t.content.trim()?{o:e.f((s=t.content,s&&"string"==typeof s?s.split("\n").filter((e=>""!==e.trim())):[""]),((t,o,a)=>({a:e.t(t),b:"text-"+o})))}:{},{p:t.quickQuestions&&t.quickQuestions.length>0},t.quickQuestions&&t.quickQuestions.length>0?{q:e.f(t.quickQuestions,((t,o,a)=>({a:e.t(t),b:o,c:e.o((e=>(e=>{n.value=e,F()})(t)),o)})))}:{},{r:t.needConfirmation},t.needConfirmation?{s:"是"===t.selectedConfirmation?1:"",t:void 0!==t.selectedConfirmation?1:"",v:e.o((e=>P(t,"是")),o),w:"否"===t.selectedConfirmation?1:"",x:void 0!==t.selectedConfirmation?1:"",y:e.o((e=>P(t,"否")),o)}:{},{z:t.created_at&&!t.quickQuestions},t.created_at&&!t.quickQuestions?e.e({A:"ai"===t.role},"ai"===t.role?{B:e.o((e=>C(t.content)),o)}:{},{C:e.t(w(t.created_at)),D:"user"===t.role},"user"===t.role?{E:e.o((e=>C(t.content)),o)}:{}):{},{F:o,G:`msg-${o}`,H:e.n("user"===t.role?"user-message":"ai-message")});var s})),b:u.value},u.value?{c:e.t(c.value)}:{},{d:l.value.length>0},l.value.length>0?e.e({e:e.f(l.value,((t,o,a)=>({a:t,b:"d335cb2c-0-"+a,c:e.o((e=>(e=>{l.value.splice(e,1)})(o)),o),d:o}))),f:e.p({type:"closeempty",size:"14",color:"#fff"}),g:l.value.length<9},l.value.length<9?{h:e.p({type:"plus",size:"30",color:"#999"}),i:e.o(z)}:{}):{},{j:e.o((e=>r.value=!0)),k:e.o((e=>r.value=!1)),l:e.o(F),m:n.value,n:e.o((e=>n.value=e.detail.value)),o:!n.value.trim()&&!r.value&&0===l.value.length},n.value.trim()||r.value||0!==l.value.length?{}:{p:e.p({type:"camera",size:"24",color:"#666"}),q:e.o(z)},{r:(n.value.trim()||r.value||l.value.length>0)&&!u.value},(n.value.trim()||r.value||l.value.length>0)&&!u.value?{s:e.p({type:"paperplane-filled",size:"20",color:"#fff"}),t:e.o(F)}:{},{v:u.value},u.value?{w:e.p({type:"spinner-cycle",size:"20",color:"#ccc"})}:{})}}),s=e._export_sfc(a,[["__scopeId","data-v-d335cb2c"]]);wx.createPage(s); diff --git a/dist/build/mp-weixin/pages/ai/chat.json b/dist/build/mp-weixin/pages/ai/chat.json new file mode 100644 index 0000000..6a4ac11 --- /dev/null +++ b/dist/build/mp-weixin/pages/ai/chat.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "客服服务", + "enablePullDownRefresh": true, + "backgroundTextStyle": "dark", + "usingComponents": { + "uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons" + } +} \ No newline at end of file diff --git a/dist/build/mp-weixin/pages/ai/chat.wxml b/dist/build/mp-weixin/pages/ai/chat.wxml new file mode 100644 index 0000000..6187251 --- /dev/null +++ b/dist/build/mp-weixin/pages/ai/chat.wxml @@ -0,0 +1 @@ +{{line.a}}{{question.a}}{{message.C}}{{c}}添加图片 \ No newline at end of file diff --git a/dist/build/mp-weixin/pages/ai/chat.wxss b/dist/build/mp-weixin/pages/ai/chat.wxss new file mode 100644 index 0000000..1b3b5e0 --- /dev/null +++ b/dist/build/mp-weixin/pages/ai/chat.wxss @@ -0,0 +1 @@ +.chat-container.data-v-d335cb2c{display:flex;flex-direction:column;min-height:100vh;width:100vw;background-color:#f5f5f5}.chat-messages.data-v-d335cb2c{width:100%;padding:30rpx 20rpx;padding-bottom:calc(200rpx + env(safe-area-inset-bottom));box-sizing:border-box;flex:1}.message-item.data-v-d335cb2c{display:flex;width:100%;box-sizing:border-box;margin-bottom:30rpx;animation:fadeIn-d335cb2c .3s ease-in}.message-item.user-message.data-v-d335cb2c{justify-content:flex-end;align-items:flex-start}.message-item.user-message .message-content-wrapper.data-v-d335cb2c{align-items:flex-end}.message-item.user-message .message-content.data-v-d335cb2c{background-color:#1c64f2;color:#fff;margin-right:0;flex-shrink:0}.message-item.user-message .message-content .message-images.data-v-d335cb2c{margin-bottom:0}.message-item.user-message .message-content .message-images .message-image-item.data-v-d335cb2c{border-radius:8rpx}.message-item.user-message .message-time.data-v-d335cb2c{color:#999;font-size:22rpx;margin-top:6rpx;text-align:right;min-width:80rpx}.message-item.user-message .message-meta.data-v-d335cb2c{display:flex;align-items:center;justify-content:flex-end;margin-top:6rpx;gap:8rpx;flex-direction:row}.message-item.ai-message.data-v-d335cb2c,.message-item.ai-message .message-content-wrapper.data-v-d335cb2c{align-items:flex-start}.message-item.ai-message .message-content.data-v-d335cb2c{background-color:#fff;color:#333;margin-left:12rpx;margin-right:0;flex-shrink:0}.message-item.ai-message .message-time.data-v-d335cb2c{color:#999;font-size:22rpx;min-width:80rpx}.message-item.ai-message .message-meta.data-v-d335cb2c{display:flex;align-items:center;justify-content:flex-start;margin-top:6rpx;gap:8rpx;margin-left:12rpx;flex-direction:row}.message-item.ai-message .message-avatar.data-v-d335cb2c{margin-right:0;flex-shrink:0}@keyframes fadeIn-d335cb2c{0%{opacity:0;transform:translateY(10rpx)}to{opacity:1;transform:translateY(0)}}.message-avatar.data-v-d335cb2c{width:60rpx;height:60rpx;border-radius:50%;overflow:hidden;flex-shrink:0}.message-avatar image.data-v-d335cb2c{width:100%;height:100%}.message-avatar .user-avatar-image.data-v-d335cb2c{width:100%;height:100%;border-radius:50%}.message-avatar .user-avatar.data-v-d335cb2c{width:100%;height:100%;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);display:flex;align-items:center;justify-content:center;color:#fff;font-size:24rpx;font-weight:500}.message-content-wrapper.data-v-d335cb2c{display:flex;flex-direction:column;max-width:calc(100vw - 100rpx)}.message-content.data-v-d335cb2c{padding:20rpx 24rpx;border-radius:16rpx;font-size:28rpx;line-height:1.5;word-wrap:break-word;word-break:break-all;box-sizing:border-box}.message-content.loading.data-v-d335cb2c{opacity:.7}.message-content.data-v-d335cb2c:has(.message-images){padding:12rpx}.user-message .message-content.data-v-d335cb2c:has(.message-images){padding:8rpx;background-color:transparent}.message-content view.data-v-d335cb2c{display:block;width:100%;word-wrap:break-word;word-break:break-all}.message-content view.data-v-d335cb2c:not(:last-child){margin-bottom:8rpx}.message-content text.data-v-d335cb2c{display:inline;word-wrap:break-word;word-break:break-all}.quick-questions.data-v-d335cb2c{margin-top:16rpx;display:flex;flex-direction:column;gap:12rpx;padding-left:12rpx}.question-btn.data-v-d335cb2c{background-color:#f0f7ff;border:1px solid #d0e3ff;border-radius:12rpx;padding:16rpx 20rpx;font-size:26rpx;color:#1c64f2;line-height:1.4;transition:all .2s ease}.question-btn.data-v-d335cb2c:active{background-color:#e6f2ff;transform:scale(.98)}.question-btn text.data-v-d335cb2c{display:block;word-wrap:break-word;word-break:break-all}.confirmation-buttons.data-v-d335cb2c{margin-top:16rpx;display:flex;gap:16rpx;justify-content:flex-start;padding-left:12rpx}.confirmation-btn.data-v-d335cb2c{width:38rpx;border-radius:12rpx;padding:16rpx 32rpx;font-size:28rpx;line-height:1.4;text-align:center;transition:all .2s ease;background-color:#fff;color:#000;border:1px solid #e5e5e5}.confirmation-btn.data-v-d335cb2c:active:not(.disabled){transform:scale(.98)}.confirmation-btn.selected.data-v-d335cb2c{background-color:#1c64f2;color:#fff;border-color:#1c64f2}.confirmation-btn.disabled.data-v-d335cb2c{opacity:.6;cursor:not-allowed}.confirmation-btn text.data-v-d335cb2c{display:block;font-weight:500}.chat-input-area.data-v-d335cb2c{position:fixed;bottom:0;left:0;right:0;background-color:#fff;border-top:1px solid #e5e5e5;padding:20rpx;padding-bottom:calc(20rpx + env(safe-area-inset-bottom));z-index:100}.input-container.data-v-d335cb2c{display:flex;align-items:center;background-color:#f5f5f5;border-radius:50rpx;padding:10rpx 20rpx}.message-input.data-v-d335cb2c{flex:1;height:70rpx;font-size:28rpx;padding:0 20rpx;background-color:transparent}.send-button.data-v-d335cb2c{width:70rpx;height:70rpx;background:linear-gradient(135deg,#1c64f2 0%,#0e4aa7 100%);border-radius:50%;display:flex;align-items:center;justify-content:center;margin-left:20rpx}.send-button.disabled.data-v-d335cb2c{opacity:.5;pointer-events:none}.upload-button.data-v-d335cb2c{width:70rpx;height:70rpx;background-color:#f5f5f5;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-left:20rpx}.image-preview-area.data-v-d335cb2c{margin-bottom:20rpx;padding:0 20rpx}.image-preview-list.data-v-d335cb2c{display:flex;flex-wrap:wrap;gap:16rpx}.image-preview-item.data-v-d335cb2c{position:relative;width:120rpx;height:120rpx;border-radius:12rpx;overflow:hidden}.image-preview-item.add-more-button.data-v-d335cb2c{background-color:#f5f5f5;border:2rpx dashed #ddd;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.image-preview-item.add-more-button.data-v-d335cb2c:active{background-color:#e8e8e8}.preview-image.data-v-d335cb2c{width:100%;height:100%}.add-more-text.data-v-d335cb2c{font-size:22rpx;color:#999;margin-top:8rpx}.remove-image.data-v-d335cb2c{position:absolute;top:8rpx;right:8rpx;width:40rpx;height:40rpx;background-color:rgba(0,0,0,.6);border-radius:50%;display:flex;align-items:center;justify-content:center}.message-images.data-v-d335cb2c{display:flex;flex-wrap:wrap;gap:12rpx;margin-bottom:12rpx;max-width:500rpx}.user-message .message-images.data-v-d335cb2c{margin-bottom:0}.message-image-item.data-v-d335cb2c{border-radius:12rpx;overflow:hidden}.message-image-item.single-image.data-v-d335cb2c{max-width:500rpx;width:500rpx!important;height:auto!important}.user-message .message-image-item.single-image.data-v-d335cb2c{max-width:450rpx;width:450rpx!important}.message-image-item.data-v-d335cb2c:not(.single-image){width:200rpx;height:200rpx}.message-image.data-v-d335cb2c{width:100%;height:100%;display:block}.message-text-line.data-v-d335cb2c{margin-bottom:4rpx}.message-text-line.data-v-d335cb2c:last-child{margin-bottom:0}.copy-button.data-v-d335cb2c{width:32rpx;height:32rpx;display:flex;align-items:center;justify-content:center;border-radius:4rpx;transition:background-color .2s ease}.copy-button.data-v-d335cb2c:active{background-color:rgba(0,0,0,.05)}.copy-button .copy-icon.data-v-d335cb2c{width:26rpx;height:26rpx;opacity:.7} diff --git a/dist/build/mp-weixin/static/svg/copy.svg b/dist/build/mp-weixin/static/svg/copy.svg new file mode 100644 index 0000000..a89dd25 --- /dev/null +++ b/dist/build/mp-weixin/static/svg/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/dev/mp-weixin/app.js b/dist/dev/mp-weixin/app.js index dd43528..35aeff4 100644 --- a/dist/dev/mp-weixin/app.js +++ b/dist/dev/mp-weixin/app.js @@ -14,7 +14,7 @@ if (!Math) { "./pages/transfer/index.js"; "./pages/forward/index.js"; "./pages/me/index.js"; - "./pages/ai/index.js"; + "./pages/ai/chat.js"; "./pages/web_view/index.js"; "./WORKORDER/list/index.js"; "./WORKORDER/add/index.js"; diff --git a/dist/dev/mp-weixin/app.json b/dist/dev/mp-weixin/app.json index 546c583..1b68f9d 100644 --- a/dist/dev/mp-weixin/app.json +++ b/dist/dev/mp-weixin/app.json @@ -5,7 +5,7 @@ "pages/transfer/index", "pages/forward/index", "pages/me/index", - "pages/ai/index", + "pages/ai/chat", "pages/web_view/index" ], "subPackages": [ diff --git a/dist/dev/mp-weixin/common/libraries/request.js b/dist/dev/mp-weixin/common/libraries/request.js index e8175b2..1dcb1ce 100644 --- a/dist/dev/mp-weixin/common/libraries/request.js +++ b/dist/dev/mp-weixin/common/libraries/request.js @@ -3,7 +3,7 @@ const common_vendor = require("../vendor.js"); const common_store_useWeAppAuthStore = require("../store/useWeAppAuthStore.js"); const http = new common_vendor.Request(); http.setConfig((config) => { - config.baseURL = "http://10.39.13.78:8001/api/"; + config.baseURL = "http://10.39.13.78:8002/api/"; config.header = { ...config.header }; diff --git a/dist/dev/mp-weixin/common/vendor.js b/dist/dev/mp-weixin/common/vendor.js index 63a1be1..d0a79b6 100644 --- a/dist/dev/mp-weixin/common/vendor.js +++ b/dist/dev/mp-weixin/common/vendor.js @@ -11045,6 +11045,7 @@ const onLoad = /* @__PURE__ */ createHook(ON_LOAD); const onUnload = /* @__PURE__ */ createHook(ON_UNLOAD); const onPageScroll = /* @__PURE__ */ createHook(ON_PAGE_SCROLL); const onReachBottom = /* @__PURE__ */ createHook(ON_REACH_BOTTOM); +const onPullDownRefresh = /* @__PURE__ */ createHook(ON_PULL_DOWN_REFRESH); const onShareTimeline = /* @__PURE__ */ createHook(ON_SHARE_TIMELINE); const onShareAppMessage = /* @__PURE__ */ createHook(ON_SHARE_APP_MESSAGE); var toString = Object.prototype.toString; @@ -14669,17 +14670,20 @@ exports.isRef = isRef; exports.mixin = mixin; exports.mpMixin = mpMixin; exports.n = n; +exports.nextTick$1 = nextTick$1; exports.o = o; exports.onHide = onHide; exports.onLaunch = onLaunch; exports.onLoad = onLoad; exports.onMounted = onMounted; exports.onPageScroll = onPageScroll; +exports.onPullDownRefresh = onPullDownRefresh; exports.onReachBottom = onReachBottom; exports.onShareAppMessage = onShareAppMessage; exports.onShareTimeline = onShareTimeline; exports.onShow = onShow; exports.onUnload = onUnload; +exports.onUnmounted = onUnmounted; exports.openType = openType; exports.p = p; exports.props = props$k; diff --git a/dist/dev/mp-weixin/pages/ai/chat.js b/dist/dev/mp-weixin/pages/ai/chat.js new file mode 100644 index 0000000..30a3801 --- /dev/null +++ b/dist/dev/mp-weixin/pages/ai/chat.js @@ -0,0 +1,926 @@ +"use strict"; +const common_vendor = require("../../common/vendor.js"); +require("../../common/libraries/request.js"); +const common_store_useWeAppAuthStore = require("../../common/store/useWeAppAuthStore.js"); +require("../../common/store/useWorkStore.js"); +const common_libraries_upload = require("../../common/libraries/upload.js"); +require("../../gen/Apis.js"); +require("../../common/libraries/setTabBar.js"); +require("../../common/libraries/apiLoading.js"); +if (!Array) { + const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons"); + _easycom_uni_icons2(); +} +const _easycom_uni_icons = () => "../../uni_modules/uni-icons/components/uni-icons/uni-icons.js"; +if (!Math) { + _easycom_uni_icons(); +} +const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({ + __name: "chat", + setup(__props) { + common_vendor.dayjs.extend(common_vendor.relativeTime); + common_vendor.dayjs.locale("zh-cn"); + const auth = common_store_useWeAppAuthStore.useWeAppAuthStore(); + const inputMessage = common_vendor.ref(""); + const selectedImages = common_vendor.ref([]); + const isInputFocused = common_vendor.ref(false); + const messages = common_vendor.ref([ + { + role: "ai", + content: "您好!我是物业客服,有什么可以帮助您的吗?", + quickQuestions: [] + } + ]); + const loading = common_vendor.ref(false); + const loadingText = common_vendor.ref("正在输入中..."); + let socketTask = null; + let isConnected = common_vendor.ref(false); + const API_BASE_URL = "https://kf-api-test.linyikj.com.cn"; + const WS_BASE_URL = "wss://kf-api-test.linyikj.com.cn"; + const currentPage = common_vendor.ref(1); + const pageSize = common_vendor.ref(10); + const hasMore = common_vendor.ref(true); + const isLoadingHistory = common_vendor.ref(false); + const conversationId = common_vendor.ref(""); + const currentOffset = common_vendor.ref(0); + const quickQuestionsData = common_vendor.ref([]); + const lastMessageContent = common_vendor.ref(""); + const getQuickQuestions = async () => { + try { + const response = await common_vendor.index.request({ + url: `${API_BASE_URL}/api/public/quick-questions`, + method: "GET" + }); + if (response.statusCode === 200) { + const result = response.data; + let quickQuestions = []; + if (result.data && Array.isArray(result.data)) { + quickQuestions = result.data.map( + (item) => item.question || item.title || item.text || item + ); + } else if (result.questions && Array.isArray(result.questions)) { + quickQuestions = result.questions.map( + (item) => item.question || item.title || item.text || item + ); + } else if (Array.isArray(result)) { + quickQuestions = result.map((item) => item.question || item.title || item.text || item); + } + console.log("获取到开场白按钮数据:", quickQuestions); + return quickQuestions; + } else { + console.error("获取开场白数据失败,状态码:", response.statusCode); + return []; + } + } catch (error) { + console.error("获取开场白数据异常:", error); + return []; + } + }; + const formatMessageContent = (content) => { + if (!content || typeof content !== "string") { + return [""]; + } + return content.split("\n").filter((line) => line.trim() !== ""); + }; + const previewImage = (currentImage, images) => { + common_vendor.index.previewImage({ + current: currentImage, + urls: images + }); + }; + const copyMessage = (content) => { + if (!content) { + common_vendor.index.showToast({ + title: "暂无内容可复制", + icon: "none" + }); + return; + } + common_vendor.index.setClipboardData({ + data: content, + success: () => { + common_vendor.index.showToast({ + title: "复制成功", + icon: "success" + }); + }, + fail: () => { + common_vendor.index.showToast({ + title: "复制失败", + icon: "none" + }); + } + }); + }; + const formatTime = (timeStr) => { + if (!timeStr) + return ""; + try { + const date = common_vendor.dayjs(timeStr); + if (!date.isValid()) { + return ""; + } + return date.fromNow(); + } catch (e) { + console.error("时间格式化错误:", e); + return ""; + } + }; + const scrollToBottom = () => { + common_vendor.nextTick$1(() => { + common_vendor.index.pageScrollTo({ + scrollTop: 999999, + duration: 300 + }); + }); + }; + const getHistoryMessages = async (page = 1) => { + var _a, _b; + const userPhone = (_b = (_a = auth.data) == null ? void 0 : _a.user) == null ? void 0 : _b.phone; + if (!userPhone) { + console.log("用户未登录,无法获取历史记录"); + return; + } + if (isLoadingHistory.value) { + return; + } + try { + isLoadingHistory.value = true; + const offset = (page - 1) * pageSize.value; + currentOffset.value = offset; + console.log("分页信息 - 页码:", page, "每页数量:", pageSize.value, "计算出的offset:", offset); + const response = await common_vendor.index.request({ + url: `${API_BASE_URL}/api/public/customer/init`, + method: "POST", + data: { + platform: "property", + platform_user_id: userPhone, + //userPhone + limit: pageSize.value, + offset + } + }); + if (response.statusCode === 200) { + const result = response.data; + let historyMessages = []; + if (result.conversation && result.conversation.id) { + conversationId.value = result.conversation.id; + console.log("获取到会话ID:", conversationId.value); + } else if (result.conversation_id) { + conversationId.value = result.conversation_id; + console.log("获取到会话ID:", conversationId.value); + } else if (result.conversations && result.conversations.id) { + conversationId.value = result.conversations.id; + console.log("获取到会话ID:", conversationId.value); + } + if (result.messages && Array.isArray(result.messages)) { + historyMessages = result.messages.map((item) => { + const message = { + role: item.sender_type === "customer" ? "user" : "ai", + content: item.message || item.content || "", + created_at: item.created_at || item.timestamp || new Date().toISOString() + }; + if (item.message_type) { + message.message_type = item.message_type; + } + let imageUrlData = null; + if (item.image_url && Array.isArray(item.image_url)) { + imageUrlData = item.image_url; + } else if (item.images && Array.isArray(item.images)) { + imageUrlData = item.images; + } else if (item.metadata && item.metadata.image_url && Array.isArray(item.metadata.image_url)) { + imageUrlData = item.metadata.image_url; + } + if (imageUrlData) { + message.image_url = imageUrlData; + message.images = imageUrlData; + } + if (item.metadata) { + message.metadata = item.metadata; + } + return message; + }); + console.log("从messages字段获取到历史记录:", historyMessages.length); + } else if (result.data && Array.isArray(result.data)) { + historyMessages = result.data.map((item) => { + const message = { + role: item.sender_type === "customer" ? "user" : "ai", + content: item.message || item.content || "", + created_at: item.created_at || item.timestamp || new Date().toISOString() + }; + if (item.message_type) { + message.message_type = item.message_type; + } + let imageUrlData = null; + if (item.image_url && Array.isArray(item.image_url)) { + imageUrlData = item.image_url; + } else if (item.images && Array.isArray(item.images)) { + imageUrlData = item.images; + } else if (item.metadata && item.metadata.image_url && Array.isArray(item.metadata.image_url)) { + imageUrlData = item.metadata.image_url; + } + if (imageUrlData) { + message.image_url = imageUrlData; + message.images = imageUrlData; + } + if (item.metadata) { + message.metadata = item.metadata; + } + return message; + }); + console.log("从data字段获取到历史记录:", historyMessages.length); + } else if (result.list && Array.isArray(result.list)) { + historyMessages = result.list.map((item) => { + const message = { + role: item.sender_type === "customer" ? "user" : "ai", + content: item.message || item.content || "", + created_at: item.created_at || item.timestamp || new Date().toISOString() + }; + if (item.message_type) { + message.message_type = item.message_type; + } + let imageUrlData = null; + if (item.image_url && Array.isArray(item.image_url)) { + imageUrlData = item.image_url; + } else if (item.images && Array.isArray(item.images)) { + imageUrlData = item.images; + } else if (item.metadata && item.metadata.image_url && Array.isArray(item.metadata.image_url)) { + imageUrlData = item.metadata.image_url; + } + if (imageUrlData) { + message.image_url = imageUrlData; + message.images = imageUrlData; + } + if (item.metadata) { + message.metadata = item.metadata; + } + return message; + }); + console.log("从list字段获取到历史记录:", historyMessages.length); + } + if (page === 1) { + if (historyMessages.length > 0) { + messages.value = historyMessages; + console.log("第一页历史记录已加载,总共", historyMessages.length, "条消息"); + } else { + console.log("没有历史记录"); + messages.value = []; + } + const isFirstPageLastPage = !hasMore.value; + if (checkShouldShowQuickQuestions(isFirstPageLastPage)) { + addQuickQuestionsToFirstAIMessage(isFirstPageLastPage); + } + } else { + if (historyMessages.length > 0) { + setTimeout(() => { + messages.value.unshift(...historyMessages); + console.log( + "加载更多历史记录,新增", + historyMessages.length, + "条消息,总共", + messages.value.length, + "条" + ); + }, 300); + } + } + const totalItems = result.total || result.count || result.total_count || 0; + hasMore.value = totalItems > messages.value.length; + currentPage.value = page; + if (page > 1 && !hasMore.value) { + console.log("分页加载完成,当前页:", page, "hasMore:", hasMore.value, "展示开场白"); + setTimeout(() => { + addQuickQuestionsToFirstAIMessage(true); + }, 500); + } + console.log( + "第", + page, + "页数据加载完成,offset:", + offset, + ", limit:", + pageSize.value, + ", 总数据:", + totalItems, + ", 当前已加载:", + messages.value.length, + ", 是否有更多:", + hasMore.value + ); + if (page === 1) { + setTimeout(() => { + scrollToBottom(); + }, 100); + console.log("[Customer] 历史记录加载完成,开始建立WebSocket连接"); + initWebSocket(); + } + } else { + console.error("获取历史记录失败,状态码:", response.statusCode); + } + } catch (error) { + console.error("获取历史记录异常:", error); + } finally { + isLoadingHistory.value = false; + } + }; + common_vendor.onPullDownRefresh(async () => { + if (isLoadingHistory.value || !hasMore.value) { + console.log("正在加载或没有更多数据"); + common_vendor.index.stopPullDownRefresh(); + return; + } + console.log("触发下拉分页,加载更多历史记录"); + const nextPage = currentPage.value + 1; + try { + await getHistoryMessages(nextPage); + await new Promise((resolve) => setTimeout(resolve, 500)); + } catch (error) { + console.error("加载历史记录失败:", error); + } finally { + common_vendor.index.stopPullDownRefresh(); + } + }); + const initWebSocket = () => { + if (socketTask) { + return; + } + try { + const wsUrl = `${WS_BASE_URL}/ws/socket.io/?EIO=4&transport=websocket`; + console.log("正在建立Socket.IO连接"); + console.log("连接URL:", wsUrl); + socketTask = common_vendor.index.connectSocket({ + url: wsUrl, + header: { + "content-type": "application/json" + } + }); + common_vendor.index.onSocketOpen((res) => { + console.log("[Customer] WebSocket connected", res); + isConnected.value = true; + const connectPacket = '40/customer,{"jwt":""}'; + common_vendor.index.sendSocketMessage({ + data: connectPacket, + success: () => { + console.log("[Customer] Socket.IO连接包发送成功"); + if (conversationId.value) { + setTimeout(() => { + const joinMessage = `42/customer,["join_conversation",{"conversation_id":"${conversationId.value}"}]`; + console.log("[Customer] 加入会话房间:", joinMessage); + common_vendor.index.sendSocketMessage({ + data: joinMessage, + success: () => { + console.log("[Customer] 成功加入会话房间"); + }, + fail: (error) => { + console.error("[Customer] 加入会话房间失败:", error); + } + }); + }, 200); + } + }, + fail: (error) => { + console.error("[Customer] Socket.IO连接包发送失败:", error); + } + }); + }); + common_vendor.index.onSocketError((error) => { + console.error("[Customer] WebSocket error:", error); + isConnected.value = false; + socketTask = null; + }); + common_vendor.index.onSocketClose((res) => { + console.log("[Customer] WebSocket disconnected", res); + isConnected.value = false; + socketTask = null; + }); + common_vendor.index.onSocketMessage((res) => { + console.log("收到WebSocket原始消息:", res); + console.log("消息数据类型:", typeof res.data); + console.log("消息内容:", res.data); + const data = res.data; + if (typeof data === "string") { + if (data.startsWith("42/customer,")) { + const payload = data.substring("42/customer,".length); + try { + const parsedData = JSON.parse(payload); + console.log("解析Socket.IO事件:", parsedData); + handleSocketEvent(parsedData); + } catch (e) { + console.error("解析Socket.IO数据失败:", e, "原始数据:", payload); + } + } else if (data.startsWith("40/customer")) { + console.log("Socket.IO连接确认"); + } else if (data.startsWith("2")) { + common_vendor.index.sendSocketMessage({ + data: "3" + }); + } else if (data.startsWith("0")) { + console.log("Socket.IO握手成功"); + } else { + try { + const jsonData = JSON.parse(data); + console.log("解析JSON消息:", jsonData); + handleJsonMessage(jsonData); + } catch (e) { + console.log("非JSON格式的字符串消息:", data); + } + } + } else if (typeof data === "object") { + console.log("收到对象类型消息:", data); + handleObjectMessage(data); + } else { + console.log("收到其他类型消息,类型:", typeof data); + } + }); + } catch (error) { + console.error("初始化Socket.IO异常:", error); + isConnected.value = false; + socketTask = null; + } + }; + const handleSocketEvent = (parsedData) => { + if (Array.isArray(parsedData) && parsedData.length >= 2) { + const eventType = parsedData[0]; + const eventData = parsedData[1]; + console.log("[Customer] Socket.IO事件类型:", eventType, "事件数据:", eventData); + if (eventType === "new_message") { + processMessage(eventData); + } else if (eventType === "join_conversation_response") { + console.log("[Customer] Joined room:", eventData); + } else if (eventType === "message") { + processMessage(eventData); + } else if (eventType === "typing") { + processTypingIndicator(eventData); + } else { + console.log("[Customer] 未处理的Socket.IO事件类型:", eventType); + } + } + }; + const handleJsonMessage = (jsonData) => { + console.log("[Customer] 处理JSON消息:", jsonData); + if (jsonData.type === "message" || jsonData.event === "new_message") { + processMessage(jsonData.data || jsonData); + } else if (jsonData.type === "typing" || jsonData.event === "typing") { + processTypingIndicator(jsonData.data || jsonData); + } else if (jsonData.message || jsonData.content) { + processMessage(jsonData); + } else { + console.log("[Customer] 未识别的JSON消息结构:", jsonData); + } + }; + const handleObjectMessage = (objData) => { + console.log("[Customer] 处理对象消息:", objData); + if (objData.message || objData.content || objData.text) { + processMessage(objData); + } else if (objData.is_typing !== void 0) { + processTypingIndicator(objData); + } else { + console.log("[Customer] 未识别的对象消息结构:", objData); + } + }; + const processTypingIndicator = (typingData) => { + console.log("[Customer] 处理typing指示器:", typingData); + if (typingData.is_typing && typingData.user_type === "staff") { + loading.value = true; + loadingText.value = "客服正在输入..."; + } else { + loading.value = false; + } + }; + const checkShouldShowQuickQuestions = (isLastPage = false) => { + if (quickQuestionsData.value.length === 0) { + return false; + } + if (messages.value.length === 0) { + return true; + } + if (isLastPage && messages.value.length > 0) { + return true; + } + return false; + }; + const addQuickQuestionsToFirstAIMessage = (isLastPage = false) => { + if (quickQuestionsData.value.length === 0) { + return; + } + if (!checkShouldShowQuickQuestions(isLastPage)) { + return; + } + const existingWelcomeMessage = messages.value.find( + (msg) => msg.role === "ai" && msg.quickQuestions && msg.quickQuestions.length > 0 + ); + if (existingWelcomeMessage) { + console.log("[Customer] 开场白已存在,跳过添加"); + return; + } + const welcomeMessage = { + role: "ai", + content: "您好!我是物业客服,有什么可以帮助您的吗?", + quickQuestions: quickQuestionsData.value, + created_at: new Date().toISOString() + }; + console.log("[Customer] 创建开场白消息,是否最后一页:", isLastPage); + if (messages.value.length === 0) { + messages.value = [welcomeMessage]; + console.log("[Customer] 设置开场白为第一条消息"); + } else if (isLastPage) { + messages.value.unshift(welcomeMessage); + console.log("[Customer] 已加载到最后一页,在第一条消息前面插入开场白"); + } + }; + const processMessage = (messageData) => { + console.log("[Customer] 处理消息数据:", messageData); + if (messageData.sender_type === "customer" || messageData.role === "user") { + console.log("[Customer] 过滤掉客户消息"); + return; + } + let messageContent = ""; + if (typeof messageData === "string") { + messageContent = messageData; + } else { + messageContent = messageData.message || messageData.content || messageData.text || messageData.body || "收到回复"; + } + if (typeof messageContent === "string") { + messageContent = messageContent.replace(/↵/g, "\n").replace(/\\n/g, "\n"); + } + console.log("[Customer] 收到新消息内容:", messageContent); + if (messageContent === lastMessageContent.value) { + console.log("[Customer] 消息与HTTP返回的消息相同,跳过显示"); + lastMessageContent.value = ""; + return; + } + loading.value = false; + const newMessage = { + role: "ai", + content: messageContent, + created_at: messageData.created_at || messageData.timestamp || messageData.time || new Date().toISOString() + }; + if (messageData.suggested_questions && Array.isArray(messageData.suggested_questions) && messageData.suggested_questions.length > 0) { + newMessage.quickQuestions = messageData.suggested_questions; + } else if (messageData.quickQuestions && Array.isArray(messageData.quickQuestions) && messageData.quickQuestions.length > 0) { + newMessage.quickQuestions = messageData.quickQuestions; + } + if (messageData.need_confirmation !== void 0) { + newMessage.needConfirmation = messageData.need_confirmation; + } + if (messageData.confirmation_type !== void 0) { + newMessage.confirmationType = messageData.confirmation_type; + } + messages.value.push(newMessage); + scrollToBottom(); + }; + const handleQuickQuestion = (question) => { + inputMessage.value = question; + handleSendMessage(); + }; + const handleConfirmation = (message, confirmation) => { + if (message.selectedConfirmation !== void 0) { + return; + } + message.selectedConfirmation = confirmation; + setTimeout(() => { + inputMessage.value = confirmation; + handleSendMessage(); + }, 300); + }; + const chooseImage = async () => { + const maxImages = 9; + if (selectedImages.value.length >= maxImages) { + common_vendor.index.showToast({ + title: "最多选择9张图片", + icon: "none" + }); + return; + } + try { + const remainingCount = maxImages - selectedImages.value.length; + const uploadResult = await common_libraries_upload.upload(remainingCount, 2, ["image"]); + if (uploadResult && Array.isArray(uploadResult) && uploadResult.length > 0) { + const imageUrls = uploadResult.map((item) => item.url); + selectedImages.value.push(...imageUrls); + common_vendor.index.showToast({ + title: "图片上传成功", + icon: "success" + }); + } + } catch (error) { + console.error("选择图片失败:", error); + common_vendor.index.showToast({ + title: "选择图片失败", + icon: "none" + }); + } + }; + const removeImage = (index) => { + selectedImages.value.splice(index, 1); + }; + const handleSendMessage = async () => { + var _a, _b, _c, _d, _e, _f; + const message = inputMessage.value.trim(); + const hasImages = selectedImages.value.length > 0; + if (!message && !hasImages || loading.value) + return; + const userPhone = (_b = (_a = auth.data) == null ? void 0 : _a.user) == null ? void 0 : _b.phone; + const projectId = (_d = (_c = auth.data) == null ? void 0 : _c.selected_house) == null ? void 0 : _d.asset_projects_id; + const projectName = (_f = (_e = auth.data) == null ? void 0 : _e.selected_house) == null ? void 0 : _f.full_name; + if (!userPhone || !projectId) { + common_vendor.index.showToast({ + title: "请先登录并绑定房屋", + icon: "none" + }); + return; + } + if (!isConnected.value) { + initWebSocket(); + await new Promise((resolve) => setTimeout(resolve, 1e3)); + } + const uploadedImages = selectedImages.value; + let messageType = "text"; + if (hasImages && message) { + messageType = "mixed"; + } else if (hasImages && !message) { + messageType = "image"; + } + const userMessage = { + role: "user", + content: message || "", + // 纯图片时传空字符串 + created_at: new Date().toISOString(), + message_type: messageType, + image_url: uploadedImages.length > 0 ? uploadedImages : void 0, + images: uploadedImages.length > 0 ? uploadedImages : void 0 + }; + messages.value.push(userMessage); + inputMessage.value = ""; + selectedImages.value = []; + scrollToBottom(); + if (message && message.includes("查询")) { + loadingText.value = "正在查询中请稍后..."; + } else { + loadingText.value = "正在输入中..."; + } + loading.value = true; + try { + const response = await common_vendor.index.request({ + url: `${API_BASE_URL}/api/public/chat`, + method: "POST", + data: { + platform: "property", + message: message || "", + // 纯图片时传空字符串 + tenant_project_id: projectId, + tenant_project_name: projectName || "", + platform_user_id: userPhone, + conversation_id: conversationId.value, + message_type: messageType, + image_url: uploadedImages.length > 0 ? uploadedImages : void 0 + }, + header: { + "Content-Type": "application/json" + } + }); + if (response.statusCode === 200) { + const result = response.data; + console.log("[Customer] HTTP接口响应数据:", result); + let httpReplyMessage = null; + let suggestedQuestions = []; + let needConfirmation = false; + let confirmationType = null; + if (result.reply && typeof result.reply === "string") { + httpReplyMessage = result.reply; + suggestedQuestions = result.suggested_questions || []; + needConfirmation = result.need_confirmation || false; + confirmationType = result.confirmation_type || null; + } else if (result.reply && result.reply.message) { + httpReplyMessage = result.reply.message; + suggestedQuestions = result.reply.suggested_questions || result.suggested_questions || []; + needConfirmation = result.reply.need_confirmation || result.need_confirmation || false; + confirmationType = result.reply.confirmation_type || result.confirmation_type || null; + } else if (result.reply && result.reply.content) { + httpReplyMessage = result.reply.content; + suggestedQuestions = result.reply.suggested_questions || result.suggested_questions || []; + needConfirmation = result.reply.need_confirmation || result.need_confirmation || false; + confirmationType = result.reply.confirmation_type || result.confirmation_type || null; + } else if (result.message) { + httpReplyMessage = result.message; + suggestedQuestions = result.suggested_questions || []; + needConfirmation = result.need_confirmation || false; + confirmationType = result.confirmation_type || null; + } else if (result.content) { + httpReplyMessage = result.content; + suggestedQuestions = result.suggested_questions || []; + needConfirmation = result.need_confirmation || false; + confirmationType = result.confirmation_type || null; + } else if (result.answer) { + httpReplyMessage = result.answer; + suggestedQuestions = result.suggested_questions || []; + needConfirmation = result.need_confirmation || false; + confirmationType = result.confirmation_type || null; + } else if (result.response) { + httpReplyMessage = result.response; + suggestedQuestions = result.suggested_questions || []; + needConfirmation = result.need_confirmation || false; + confirmationType = result.confirmation_type || null; + } else if (typeof result === "string") { + httpReplyMessage = result; + } + if (httpReplyMessage) { + console.log("[Customer] HTTP接口返回回答消息:", httpReplyMessage); + console.log("[Customer] 建议问题:", suggestedQuestions); + console.log("[Customer] 需要确认:", needConfirmation, "确认类型:", confirmationType); + let formattedMessage = httpReplyMessage; + if (typeof formattedMessage === "string") { + formattedMessage = formattedMessage.replace(/↵/g, "\n").replace(/\\n/g, "\n"); + } + lastMessageContent.value = formattedMessage; + loading.value = false; + messages.value.push({ + role: "ai", + content: formattedMessage, + created_at: new Date().toISOString(), + quickQuestions: suggestedQuestions.length > 0 ? suggestedQuestions : void 0, + needConfirmation, + confirmationType + }); + scrollToBottom(); + } else { + console.log("[Customer] 消息发送成功,等待WebSocket回复"); + } + } else { + throw new Error("请求失败"); + } + } catch (error) { + console.error("发送消息失败:", error); + common_vendor.index.showToast({ + title: "发送失败,请稍后重试", + icon: "none" + }); + messages.value.push({ + role: "ai", + content: "抱歉,网络连接出现问题,请稍后再试。" + }); + loading.value = false; + scrollToBottom(); + } + }; + common_vendor.onUnmounted(() => { + if (socketTask || isConnected.value) { + common_vendor.index.closeSocket(); + socketTask = null; + isConnected.value = false; + console.log("页面卸载,WebSocket连接已关闭"); + } + }); + common_vendor.onLoad(async () => { + quickQuestionsData.value = await getQuickQuestions(); + console.log("页面加载时已获取开场白按钮数据:", quickQuestionsData.value.length); + getHistoryMessages(1); + }); + return (_ctx, _cache) => { + return common_vendor.e({ + a: common_vendor.f(messages.value, (message, index, i0) => { + return common_vendor.e({ + a: message.role === "ai" + }, message.role === "ai" ? {} : {}, { + b: message.image_url && message.image_url.length > 0 + }, message.image_url && message.image_url.length > 0 ? { + c: common_vendor.f(message.image_url, (image, imgIndex, i1) => { + return { + a: image, + b: common_vendor.o(($event) => previewImage(image, message.image_url), imgIndex), + c: imgIndex + }; + }), + d: message.image_url.length === 1 ? "widthFix" : "aspectFill", + e: message.image_url.length === 1 ? 1 : "" + } : message.images && message.images.length > 0 ? { + g: common_vendor.f(message.images, (image, imgIndex, i1) => { + return { + a: image, + b: common_vendor.o(($event) => previewImage(image, message.images), imgIndex), + c: imgIndex + }; + }), + h: message.images.length === 1 ? "widthFix" : "aspectFill", + i: message.images.length === 1 ? 1 : "" + } : message.metadata && message.metadata.image_url && message.metadata.image_url.length > 0 ? { + k: common_vendor.f(message.metadata.image_url, (image, imgIndex, i1) => { + return { + a: image, + b: common_vendor.o(($event) => previewImage(image, message.metadata.image_url), imgIndex), + c: imgIndex + }; + }), + l: message.metadata.image_url.length === 1 ? "widthFix" : "aspectFill", + m: message.metadata.image_url.length === 1 ? 1 : "" + } : {}, { + f: message.images && message.images.length > 0, + j: message.metadata && message.metadata.image_url && message.metadata.image_url.length > 0, + n: message.content && message.content.trim() + }, message.content && message.content.trim() ? { + o: common_vendor.f(formatMessageContent(message.content), (line, lineIndex, i1) => { + return { + a: common_vendor.t(line), + b: "text-" + lineIndex + }; + }) + } : {}, { + p: message.quickQuestions && message.quickQuestions.length > 0 + }, message.quickQuestions && message.quickQuestions.length > 0 ? { + q: common_vendor.f(message.quickQuestions, (question, qIndex, i1) => { + return { + a: common_vendor.t(question), + b: qIndex, + c: common_vendor.o(($event) => handleQuickQuestion(question), qIndex) + }; + }) + } : {}, { + r: message.needConfirmation + }, message.needConfirmation ? { + s: message.selectedConfirmation === "是" ? 1 : "", + t: message.selectedConfirmation !== void 0 ? 1 : "", + v: common_vendor.o(($event) => handleConfirmation(message, "是"), index), + w: message.selectedConfirmation === "否" ? 1 : "", + x: message.selectedConfirmation !== void 0 ? 1 : "", + y: common_vendor.o(($event) => handleConfirmation(message, "否"), index) + } : {}, { + z: message.created_at && !message.quickQuestions + }, message.created_at && !message.quickQuestions ? common_vendor.e({ + A: message.role === "ai" + }, message.role === "ai" ? { + B: common_vendor.o(($event) => copyMessage(message.content), index) + } : {}, { + C: common_vendor.t(formatTime(message.created_at)), + D: message.role === "user" + }, message.role === "user" ? { + E: common_vendor.o(($event) => copyMessage(message.content), index) + } : {}) : {}, { + F: index, + G: `msg-${index}`, + H: common_vendor.n(message.role === "user" ? "user-message" : "ai-message") + }); + }), + b: loading.value + }, loading.value ? { + c: common_vendor.t(loadingText.value) + } : {}, { + d: selectedImages.value.length > 0 + }, selectedImages.value.length > 0 ? common_vendor.e({ + e: common_vendor.f(selectedImages.value, (image, index, i0) => { + return { + a: image, + b: "b15798f7-0-" + i0, + c: common_vendor.o(($event) => removeImage(index), index), + d: index + }; + }), + f: common_vendor.p({ + type: "closeempty", + size: "14", + color: "#fff" + }), + g: selectedImages.value.length < 9 + }, selectedImages.value.length < 9 ? { + h: common_vendor.p({ + type: "plus", + size: "30", + color: "#999" + }), + i: common_vendor.o(chooseImage) + } : {}) : {}, { + j: common_vendor.o(($event) => isInputFocused.value = true), + k: common_vendor.o(($event) => isInputFocused.value = false), + l: common_vendor.o(handleSendMessage), + m: inputMessage.value, + n: common_vendor.o(($event) => inputMessage.value = $event.detail.value), + o: !inputMessage.value.trim() && !isInputFocused.value && selectedImages.value.length === 0 + }, !inputMessage.value.trim() && !isInputFocused.value && selectedImages.value.length === 0 ? { + p: common_vendor.p({ + type: "camera", + size: "24", + color: "#666" + }), + q: common_vendor.o(chooseImage) + } : {}, { + r: (inputMessage.value.trim() || isInputFocused.value || selectedImages.value.length > 0) && !loading.value + }, (inputMessage.value.trim() || isInputFocused.value || selectedImages.value.length > 0) && !loading.value ? { + s: common_vendor.p({ + type: "paperplane-filled", + size: "20", + color: "#fff" + }), + t: common_vendor.o(handleSendMessage) + } : {}, { + v: loading.value + }, loading.value ? { + w: common_vendor.p({ + type: "spinner-cycle", + size: "20", + color: "#ccc" + }) + } : {}); + }; + } +}); +const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-b15798f7"], ["__file", "/Users/zsq/Sources/github/2025property-pay/pay-customer/src/pages/ai/chat.vue"]]); +wx.createPage(MiniProgramPage); diff --git a/dist/dev/mp-weixin/pages/ai/chat.json b/dist/dev/mp-weixin/pages/ai/chat.json new file mode 100644 index 0000000..832d9d0 --- /dev/null +++ b/dist/dev/mp-weixin/pages/ai/chat.json @@ -0,0 +1,6 @@ +{ + "navigationBarTitleText": "客服服务", + "usingComponents": { + "uni-icons": "../../uni_modules/uni-icons/components/uni-icons/uni-icons" + } +} \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/ai/chat.wxml b/dist/dev/mp-weixin/pages/ai/chat.wxml new file mode 100644 index 0000000..acf2ece --- /dev/null +++ b/dist/dev/mp-weixin/pages/ai/chat.wxml @@ -0,0 +1 @@ +{{line.a}}{{question.a}}{{message.C}}{{c}}添加图片 \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/ai/chat.wxss b/dist/dev/mp-weixin/pages/ai/chat.wxss new file mode 100644 index 0000000..a777aed --- /dev/null +++ b/dist/dev/mp-weixin/pages/ai/chat.wxss @@ -0,0 +1,375 @@ +.chat-container.data-v-b15798f7 { + display: flex; + flex-direction: column; + min-height: 100vh; + width: 100vw; + background-color: #f5f5f5; +} +.chat-messages.data-v-b15798f7 { + width: 100%; + padding: 30rpx 20rpx; + padding-bottom: calc(200rpx + env(safe-area-inset-bottom)); + box-sizing: border-box; + flex: 1; +} +.message-item.data-v-b15798f7 { + display: flex; + width: 100%; + box-sizing: border-box; + margin-bottom: 30rpx; + animation: fadeIn-b15798f7 0.3s ease-in; +} +.message-item.user-message.data-v-b15798f7 { + justify-content: flex-end; + align-items: flex-start; +} +.message-item.user-message .message-content-wrapper.data-v-b15798f7 { + align-items: flex-end; +} +.message-item.user-message .message-content.data-v-b15798f7 { + background-color: #1c64f2; + color: #fff; + margin-right: 0; + flex-shrink: 0; +} +.message-item.user-message .message-content .message-images.data-v-b15798f7 { + margin-bottom: 0; +} +.message-item.user-message .message-content .message-images .message-image-item.data-v-b15798f7 { + border-radius: 8rpx; +} +.message-item.user-message .message-time.data-v-b15798f7 { + color: #999; + font-size: 22rpx; + margin-top: 6rpx; + text-align: right; + min-width: 80rpx; +} +.message-item.user-message .message-meta.data-v-b15798f7 { + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: 6rpx; + gap: 8rpx; + flex-direction: row; +} +.message-item.ai-message.data-v-b15798f7 { + align-items: flex-start; +} +.message-item.ai-message .message-content-wrapper.data-v-b15798f7 { + align-items: flex-start; +} +.message-item.ai-message .message-content.data-v-b15798f7 { + background-color: #fff; + color: #333; + margin-left: 12rpx; + margin-right: 0; + flex-shrink: 0; +} +.message-item.ai-message .message-time.data-v-b15798f7 { + color: #999; + font-size: 22rpx; + min-width: 80rpx; +} +.message-item.ai-message .message-meta.data-v-b15798f7 { + display: flex; + align-items: center; + justify-content: flex-start; + margin-top: 6rpx; + gap: 8rpx; + margin-left: 12rpx; + flex-direction: row; +} +.message-item.ai-message .message-avatar.data-v-b15798f7 { + margin-right: 0; + flex-shrink: 0; +} +@keyframes fadeIn-b15798f7 { +from { + opacity: 0; + transform: translateY(10rpx); +} +to { + opacity: 1; + transform: translateY(0); +} +} +.message-avatar.data-v-b15798f7 { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + overflow: hidden; + flex-shrink: 0; +} +.message-avatar image.data-v-b15798f7 { + width: 100%; + height: 100%; +} +.message-avatar .user-avatar-image.data-v-b15798f7 { + width: 100%; + height: 100%; + border-radius: 50%; +} +.message-avatar .user-avatar.data-v-b15798f7 { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 24rpx; + font-weight: 500; +} +.message-content-wrapper.data-v-b15798f7 { + display: flex; + flex-direction: column; + max-width: calc(100vw - 100rpx); +} +.message-content.data-v-b15798f7 { + padding: 20rpx 24rpx; + border-radius: 16rpx; + font-size: 28rpx; + line-height: 1.5; + word-wrap: break-word; + word-break: break-all; + box-sizing: border-box; +} +.message-content.loading.data-v-b15798f7 { + opacity: 0.7; +} +.message-content.data-v-b15798f7:has(.message-images) { + padding: 12rpx; +} +.user-message .message-content.data-v-b15798f7:has(.message-images) { + padding: 8rpx; + background-color: transparent; +} +.message-content view.data-v-b15798f7 { + display: block; + width: 100%; + word-wrap: break-word; + word-break: break-all; +} +.message-content view.data-v-b15798f7:not(:last-child) { + margin-bottom: 8rpx; +} +.message-content text.data-v-b15798f7 { + display: inline; + word-wrap: break-word; + word-break: break-all; +} +.quick-questions.data-v-b15798f7 { + margin-top: 16rpx; + display: flex; + flex-direction: column; + gap: 12rpx; + padding-left: 12rpx; +} +.question-btn.data-v-b15798f7 { + background-color: #f0f7ff; + border: 1px solid #d0e3ff; + border-radius: 12rpx; + padding: 16rpx 20rpx; + font-size: 26rpx; + color: #1c64f2; + line-height: 1.4; + transition: all 0.2s ease; +} +.question-btn.data-v-b15798f7:active { + background-color: #e6f2ff; + transform: scale(0.98); +} +.question-btn text.data-v-b15798f7 { + display: block; + word-wrap: break-word; + word-break: break-all; +} +.confirmation-buttons.data-v-b15798f7 { + margin-top: 16rpx; + display: flex; + gap: 16rpx; + justify-content: flex-start; + padding-left: 12rpx; +} +.confirmation-btn.data-v-b15798f7 { + width: 38rpx; + border-radius: 12rpx; + padding: 16rpx 32rpx; + font-size: 28rpx; + line-height: 1.4; + text-align: center; + transition: all 0.2s ease; + background-color: #fff; + color: #000; + border: 1px solid #e5e5e5; +} +.confirmation-btn.data-v-b15798f7:active:not(.disabled) { + transform: scale(0.98); +} +.confirmation-btn.selected.data-v-b15798f7 { + background-color: #1c64f2; + color: #fff; + border-color: #1c64f2; +} +.confirmation-btn.disabled.data-v-b15798f7 { + opacity: 0.6; + cursor: not-allowed; +} +.confirmation-btn text.data-v-b15798f7 { + display: block; + font-weight: 500; +} +.chat-input-area.data-v-b15798f7 { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background-color: #fff; + border-top: 1px solid #e5e5e5; + padding: 20rpx 20rpx; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); + z-index: 100; +} +.input-container.data-v-b15798f7 { + display: flex; + align-items: center; + background-color: #f5f5f5; + border-radius: 50rpx; + padding: 10rpx 20rpx; +} +.message-input.data-v-b15798f7 { + flex: 1; + height: 70rpx; + font-size: 28rpx; + padding: 0 20rpx; + background-color: transparent; +} +.send-button.data-v-b15798f7 { + width: 70rpx; + height: 70rpx; + background: linear-gradient(135deg, #1c64f2 0%, #0e4aa7 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-left: 20rpx; +} +.send-button.disabled.data-v-b15798f7 { + opacity: 0.5; + pointer-events: none; +} +.upload-button.data-v-b15798f7 { + width: 70rpx; + height: 70rpx; + background-color: #f5f5f5; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-left: 20rpx; +} +.image-preview-area.data-v-b15798f7 { + margin-bottom: 20rpx; + padding: 0 20rpx; +} +.image-preview-list.data-v-b15798f7 { + display: flex; + flex-wrap: wrap; + gap: 16rpx; +} +.image-preview-item.data-v-b15798f7 { + position: relative; + width: 120rpx; + height: 120rpx; + border-radius: 12rpx; + overflow: hidden; +} +.image-preview-item.add-more-button.data-v-b15798f7 { + background-color: #f5f5f5; + border: 2rpx dashed #ddd; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; +} +.image-preview-item.add-more-button.data-v-b15798f7:active { + background-color: #e8e8e8; +} +.preview-image.data-v-b15798f7 { + width: 100%; + height: 100%; +} +.add-more-text.data-v-b15798f7 { + font-size: 22rpx; + color: #999; + margin-top: 8rpx; +} +.remove-image.data-v-b15798f7 { + position: absolute; + top: 8rpx; + right: 8rpx; + width: 40rpx; + height: 40rpx; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} +.message-images.data-v-b15798f7 { + display: flex; + flex-wrap: wrap; + gap: 12rpx; + margin-bottom: 12rpx; + max-width: 500rpx; +} +.user-message .message-images.data-v-b15798f7 { + margin-bottom: 0; +} +.message-image-item.data-v-b15798f7 { + border-radius: 12rpx; + overflow: hidden; +} +.message-image-item.single-image.data-v-b15798f7 { + max-width: 500rpx; + width: 500rpx !important; + height: auto !important; +} +.user-message .message-image-item.single-image.data-v-b15798f7 { + max-width: 450rpx; + width: 450rpx !important; +} +.message-image-item.data-v-b15798f7:not(.single-image) { + width: 200rpx; + height: 200rpx; +} +.message-image.data-v-b15798f7 { + width: 100%; + height: 100%; + display: block; +} +.message-text-line.data-v-b15798f7 { + margin-bottom: 4rpx; +} +.message-text-line.data-v-b15798f7:last-child { + margin-bottom: 0; +} +.copy-button.data-v-b15798f7 { + width: 32rpx; + height: 32rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4rpx; + transition: background-color 0.2s ease; +} +.copy-button.data-v-b15798f7:active { + background-color: rgba(0, 0, 0, 0.05); +} +.copy-button .copy-icon.data-v-b15798f7 { + width: 26rpx; + height: 26rpx; + opacity: 0.7; +} \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/ai/index.js b/dist/dev/mp-weixin/pages/ai/index.js deleted file mode 100644 index e9078af..0000000 --- a/dist/dev/mp-weixin/pages/ai/index.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -const common_vendor = require("../../common/vendor.js"); -require("../../common/libraries/request.js"); -require("../../common/store/useWeAppAuthStore.js"); -require("../../common/store/useWorkStore.js"); -require("../../gen/Apis.js"); -require("../../common/libraries/setTabBar.js"); -require("../../common/libraries/apiLoading.js"); -const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({ - __name: "index", - setup(__props) { - common_vendor.onShow(async (e) => { - const curPages = getCurrentPages()[0]; - if (typeof curPages.getTabBar === "function" && curPages.getTabBar()) { - curPages.getTabBar().setData({ - selected: 1 - // 表示当前菜单的索引,该值在不同的页面表示不同 - }); - } - }); - return (_ctx, _cache) => { - return {}; - }; - } -}); -const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__file", "/Users/zsq/Sources/github/2025property-pay/pay-customer/src/pages/ai/index.vue"]]); -wx.createPage(MiniProgramPage); diff --git a/dist/dev/mp-weixin/pages/ai/index.json b/dist/dev/mp-weixin/pages/ai/index.json deleted file mode 100644 index ca79faf..0000000 --- a/dist/dev/mp-weixin/pages/ai/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "navigationBarTitleText": "智能管家", - "usingComponents": {} -} \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/ai/index.wxml b/dist/dev/mp-weixin/pages/ai/index.wxml deleted file mode 100644 index 9966095..0000000 --- a/dist/dev/mp-weixin/pages/ai/index.wxml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/ai/index.wxss b/dist/dev/mp-weixin/pages/ai/index.wxss deleted file mode 100644 index c8f5c6f..0000000 --- a/dist/dev/mp-weixin/pages/ai/index.wxss +++ /dev/null @@ -1,26 +0,0 @@ -#dify-chatbot-bubble-button { - background-color: #1c64f2 !important; -} -#dify-chatbot-bubble-window { - width: 24rem !important; - height: 40rem !important; -} -page { - background-color: #f8f8f8; -} -.contact_btn { - border: none !important; - background-color: transparent; - padding: 0; - line-height: 1; - color: inherit; - font-size: inherit; - display: inline-flex; - align-items: center; - justify-content: center; - outline: none; - box-shadow: none; -} -button:after { - border: none !important; -} \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/index/components/AiInput.js b/dist/dev/mp-weixin/pages/index/components/AiInput.js index e02c590..6c8ce93 100644 --- a/dist/dev/mp-weixin/pages/index/components/AiInput.js +++ b/dist/dev/mp-weixin/pages/index/components/AiInput.js @@ -1,6 +1,5 @@ "use strict"; const common_vendor = require("../../../common/vendor.js"); -const common_libraries_naviHelper = require("../../../common/libraries/naviHelper.js"); require("../../../common/libraries/getPageConfig.js"); require("../../../common/store/useWeAppAuthStore.js"); require("../../../gen/Apis.js"); @@ -20,16 +19,18 @@ const _sfc_main = /* @__PURE__ */ common_vendor.defineComponent({ __name: "AiInput", setup(__props) { const handleInputChange = () => { - common_libraries_naviHelper.showToast("该功能在当前项目暂未开放,敬请期待!"); + common_vendor.index.navigateTo({ + url: "/pages/ai/chat" + }); }; return (_ctx, _cache) => { return { - a: common_vendor.o(handleInputChange), - b: common_vendor.p({ + a: common_vendor.p({ type: "arrow-right", size: "20", color: "#666" - }) + }), + b: common_vendor.o(handleInputChange) }; }; } diff --git a/dist/dev/mp-weixin/pages/index/components/AiInput.wxml b/dist/dev/mp-weixin/pages/index/components/AiInput.wxml index 6cf4ab7..4dbddc8 100644 --- a/dist/dev/mp-weixin/pages/index/components/AiInput.wxml +++ b/dist/dev/mp-weixin/pages/index/components/AiInput.wxml @@ -1 +1 @@ -HI,遇到什么问题了,可以问我哦~ 房屋缴费 工单查询 房屋绑定 钱包 \ No newline at end of file +HI,遇到什么问题了,可以问我哦~ 点击联系在线客服 房屋缴费 工单查询 房屋绑定 钱包 \ No newline at end of file diff --git a/dist/dev/mp-weixin/pages/index/components/AiInput.wxss b/dist/dev/mp-weixin/pages/index/components/AiInput.wxss index d4ec827..f96e216 100644 --- a/dist/dev/mp-weixin/pages/index/components/AiInput.wxss +++ b/dist/dev/mp-weixin/pages/index/components/AiInput.wxss @@ -42,8 +42,8 @@ justify-content: space-between; background-color: #fff; border-radius: 100rpx; - padding: 0 20rpx; - width: 100%; + padding: 15rpx 20rpx; + margin: 5rpx 30rpx; } .ai_contents .ai_contents_input .ai_input.data-v-b68fefb8 { flex: 1; diff --git a/dist/dev/mp-weixin/static/svg/copy.svg b/dist/dev/mp-weixin/static/svg/copy.svg new file mode 100644 index 0000000..a89dd25 --- /dev/null +++ b/dist/dev/mp-weixin/static/svg/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/pages.json b/src/pages.json index b59f5b5..10e7f8f 100644 --- a/src/pages.json +++ b/src/pages.json @@ -35,9 +35,9 @@ } }, { - "path": "pages/ai/index", + "path": "pages/ai/chat", "style": { - "navigationBarTitleText": "智能管家" + "navigationBarTitleText": "客服服务" } }, { diff --git a/src/pages/index/components/AiInput.vue b/src/pages/index/components/AiInput.vue index a8aa2dc..4757f6d 100644 --- a/src/pages/index/components/AiInput.vue +++ b/src/pages/index/components/AiInput.vue @@ -5,20 +5,16 @@ HI,遇到什么问题了,可以问我哦~ - + @@ -55,7 +51,10 @@ import { showToast } from '@/common/libraries/naviHelper' import { getStyleColorInfo, getStyleColorValueInfo } from '@/common/libraries/getPageConfig' const handleInputChange = () => { - showToast('该功能在当前项目暂未开放,敬请期待!') + uni.navigateTo({ + url: '/pages/ai/chat' + }) + // showToast('该功能在当前项目暂未开放,敬请期待!') } @@ -113,8 +112,8 @@ const handleInputChange = () => { justify-content: space-between; background-color: #fff; border-radius: 100rpx; - padding: 0 20rpx; - width: 100%; + padding: 15rpx 20rpx; + margin: 5rpx 30rpx; .ai_input { flex: 1; font-size: 28rpx; diff --git a/src/static/svg/copy.svg b/src/static/svg/copy.svg new file mode 100644 index 0000000..a89dd25 --- /dev/null +++ b/src/static/svg/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file