|
@@ -0,0 +1,408 @@
|
|
|
+<template>
|
|
|
+ <s-layout ref="layoutRef" class="chat-wrap" title="ai" navbar="inner">
|
|
|
+ <EasyHover :iconUrl="sheep.$url.static('/static/home/xq.png')" :circle="false" :height="80" :initMarginTop="1000"
|
|
|
+ :width="80" @taped="triged" :stickSide="true" initSide="right">
|
|
|
+ </EasyHover>
|
|
|
+ <!-- <view class="dropdownClass">
|
|
|
+ <u-dropdown>
|
|
|
+ <u-dropdown-item v-model="value1" :title="options1[value1 - 1].label" :options="options1"></u-dropdown-item>
|
|
|
+ </u-dropdown>
|
|
|
+ </view> -->
|
|
|
+ <!-- 覆盖头部导航栏背景颜色 -->
|
|
|
+ <view class="page-bg" :style="{ height: sys_navBar + 'px' }"></view>
|
|
|
+ <!-- 聊天区域 -->
|
|
|
+ <MessageList ref="messageListRef" :queryShow="false">
|
|
|
+ <template #bottom>
|
|
|
+ <message-input :loading="loadingInput" v-model="chat.msg" @on-tools="onTools"
|
|
|
+ @send-message="onSendMessage"></message-input>
|
|
|
+ </template>
|
|
|
+ </MessageList>
|
|
|
+ <!-- 聊天工具 -->
|
|
|
+ <tools-popup :show-tools="chat.showTools" :tools-mode="chat.toolsMode" @close="handleToolsClose" @on-emoji="onEmoji"
|
|
|
+ @image-select="onSelect" @on-show-select="onShowSelect">
|
|
|
+ <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
|
|
|
+ </tools-popup>
|
|
|
+ <!-- 产品订单选择 -->
|
|
|
+ <SelectPopup :mode="chat.selectMode" :show="chat.showSelect" @select="onSelect" @close="chat.showSelect = false" />
|
|
|
+ <EventSource ref="EventSourceRef" :url="eventSourceUrl" :options="eventSourceOptions" @callback="handleCallback" />
|
|
|
+ <template #right>
|
|
|
+ <view class="lsdhClass">
|
|
|
+ <view class="lsdhDetailClass addClass" style="" @click="addChat">
|
|
|
+ <u-icon name="plus"></u-icon>
|
|
|
+ 新增对话
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view @click="detailCLick(item)" class="lsdhDetailClass" v-for="item in lsjlList" :key="item">
|
|
|
+ {{ item.title }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </s-layout>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import MessageList from '@/pages/chat/components/messageList.vue';
|
|
|
+import EventSource from './eventSource.vue';
|
|
|
+import { reactive, ref, toRefs, computed, provide, onMounted } from 'vue';
|
|
|
+import sheep from '@/sheep';
|
|
|
+import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
|
|
|
+import MessageInput from '@/pages/chat/components/messageInput.vue';
|
|
|
+import SelectPopup from '@/pages/chat/components/select-popup.vue';
|
|
|
+import EasyHover from '@/components/easy-hover/easy-hover.vue'
|
|
|
+
|
|
|
+import {
|
|
|
+ KeFuMessageContentTypeEnum
|
|
|
+} from '../util/constants';
|
|
|
+import FileApi from '@/sheep/api/infra/file';
|
|
|
+import KeFuApi from '@/sheep/api/promotion/kefu';
|
|
|
+import { useWebSocket } from '@/sheep/hooks/useWebSocket';
|
|
|
+import { jsonParse } from '@/sheep/util';
|
|
|
+import { onLoad } from '@dcloudio/uni-app';
|
|
|
+
|
|
|
+
|
|
|
+const EventSourceRef = ref(null); //AIref
|
|
|
+const chatMsgData = ref({});
|
|
|
+const messageListRef = ref();
|
|
|
+const eventSourceUrl = import.meta.env.SHOPRO_BASE_URL + '/app-api/ai/chat/message/dify-stream'; //ai客服URL
|
|
|
+// const eventSourceUrl = "https://192.168.10.17:9095/app-api/infra/ai-dify/chat-messages-stream"; //ai客服URL
|
|
|
+// ai客服流式上传参数
|
|
|
+const eventSourceOptions = computed(() => {
|
|
|
+ return {
|
|
|
+ headers: {
|
|
|
+ 'content-type': 'application/json',
|
|
|
+ Accept: 'text/event-stream',
|
|
|
+ 'tenant-id': 1,
|
|
|
+ authorization: uni.getStorageSync('token'),
|
|
|
+ },
|
|
|
+ method: 'POST',
|
|
|
+ body: JSON.stringify({
|
|
|
+ contentType: 1,
|
|
|
+ content: chatMsgData.value.content || chat.msg,
|
|
|
+ relUserId: route.value.relUserId,
|
|
|
+ stateId: messageListRef.value ? messageListRef.value.messageList.length + 2 : 2,
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ difyConversationId: difyConversationId.value,
|
|
|
+ // type: "律师咨询",
|
|
|
+ // query: chat.msg
|
|
|
+ }), // 请求体
|
|
|
+ };
|
|
|
+});
|
|
|
+const layoutRef = ref(null); // 布局组件引用
|
|
|
+
|
|
|
+const lsjlList = ref([]);
|
|
|
+const detailCLick = (item) => {
|
|
|
+ KeFuApi.conversationId({
|
|
|
+ tenantId: item.id,
|
|
|
+ conversationId: item.id,
|
|
|
+ }).then((res) => {
|
|
|
+ messageListRef.value.messageList = res.data.reverse().map((item) => {
|
|
|
+ if (item.type == 'user') {
|
|
|
+ item.senderId = userInfo.value.id;
|
|
|
+ }
|
|
|
+ return item;
|
|
|
+ });
|
|
|
+ layoutRef.value.show = false;
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const lsjlClick = () => {
|
|
|
+ KeFuApi.conversationMyList().then((res) => {
|
|
|
+ lsjlList.value = res.data;
|
|
|
+ layoutRef.value.show = true;
|
|
|
+ console.log(layoutRef.value, 222221111)
|
|
|
+ });
|
|
|
+};
|
|
|
+const triged = () => {
|
|
|
+ console.log(7777)
|
|
|
+ lsjlClick()
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+const answerArr = ref('');
|
|
|
+const loadingInput = ref(false); // 加载状态
|
|
|
+provide('loadingInput', loadingInput); // 依赖注入加载状态
|
|
|
+const EventSourceFun = async (data, is = true) => {
|
|
|
+ console.log('张耀文2', data)
|
|
|
+ loadingInput.value = true;
|
|
|
+ chatMsgData.value = data;
|
|
|
+ const avatarObj = messageListRef.value.getAvatar() || {};
|
|
|
+ const params = {
|
|
|
+ // id: 1,
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ senderId: userInfo.value.id,
|
|
|
+ senderAvatar: avatarObj.senderAvatar || '',
|
|
|
+ senderType: 1,
|
|
|
+ stateId: messageListRef.value.messageList.length + 2,
|
|
|
+ receiverId: route.value.relUserId,
|
|
|
+ receiverAvatar: avatarObj.receiverAvatar || '',
|
|
|
+ receiverType: null,
|
|
|
+ contentType: 1,
|
|
|
+ content: is ? JSON.stringify({ text: chat.msg }) : data.content,
|
|
|
+ readStatus: true,
|
|
|
+ createTime: 1745546275000,
|
|
|
+ };
|
|
|
+ if (data.content != 'ecologicalValueCalculate') {
|
|
|
+ await messageListRef.value.refreshMessageList(JSON.parse(JSON.stringify(params)));
|
|
|
+ }
|
|
|
+ const params1 = {
|
|
|
+ ids: 1,
|
|
|
+ isAi: true,
|
|
|
+ isLoading: true,
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ senderId: route.value.relUserId,
|
|
|
+ senderAvatar: avatarObj.receiverAvatar || '',
|
|
|
+ stateId: messageListRef.value.messageList.length + 2,
|
|
|
+ senderType: 1,
|
|
|
+ receiverId: userInfo.value.id,
|
|
|
+ receiverAvatar: avatarObj.senderAvatar || '',
|
|
|
+ receiverType: null,
|
|
|
+ contentType: 22,
|
|
|
+ content: '',
|
|
|
+ readStatus: true,
|
|
|
+ createTime: 1745546275000,
|
|
|
+ };
|
|
|
+ await messageListRef.value.refreshMessageList(JSON.parse(JSON.stringify(params1)));
|
|
|
+ await EventSourceRef.value.send(data);
|
|
|
+};
|
|
|
+provide('EventSourceFun', EventSourceFun); // 依赖注入加载状态
|
|
|
+
|
|
|
+const loadingId = ref(''); // 加载的id
|
|
|
+// ai客服发送消息接收回调
|
|
|
+const difyConversationId = ref('');
|
|
|
+
|
|
|
+const handleCallback = async (e) => {
|
|
|
+ const { type, msg, data } = e || {};
|
|
|
+ if (type == 'onmessage') {
|
|
|
+ const datas = JSON.parse(data);
|
|
|
+ console.log(datas, 66666);
|
|
|
+ difyConversationId.value = datas.data.receive.difyConversationId;
|
|
|
+ if (datas.data.receive.event === null) {
|
|
|
+ await messageListRef.value.updateMessage({ ...datas.data.receive, contentType: 1 });
|
|
|
+ } else if (datas.data.receive.event == 'message') {
|
|
|
+ await messageListRef.value.updateMessage(datas.data.receive);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type == 'onclose') {
|
|
|
+ loadingInput.value = false;
|
|
|
+ // await messageListRef.value.updateMessage({}, loadingId.value);
|
|
|
+ answerArr.value = '';
|
|
|
+ }
|
|
|
+};
|
|
|
+const sys_navBar = sheep.$platform.navbar;
|
|
|
+const options1 = [
|
|
|
+ {
|
|
|
+ label: 'ai',
|
|
|
+ value: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '律师咨询',
|
|
|
+ value: 2,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '金融相关',
|
|
|
+ value: 3,
|
|
|
+ },
|
|
|
+];
|
|
|
+const value1 = ref(1);
|
|
|
+const chat = reactive({
|
|
|
+ msg: '',
|
|
|
+ scrollInto: '',
|
|
|
+ showTools: false,
|
|
|
+ toolsMode: '',
|
|
|
+ showSelect: false,
|
|
|
+ selectMode: '',
|
|
|
+});
|
|
|
+const route = ref({});
|
|
|
+const reateDifyParams = ref({});
|
|
|
+const init = async () => {
|
|
|
+ const res = await KeFuApi.sendCreateDify({
|
|
|
+ roleId: 666,
|
|
|
+ knowledgeId: 1204,
|
|
|
+ });
|
|
|
+ reateDifyParams.value = res.data;
|
|
|
+};
|
|
|
+
|
|
|
+onLoad((options) => {
|
|
|
+ route.value = options;
|
|
|
+});
|
|
|
+onMounted(async () => {
|
|
|
+ await init();
|
|
|
+ loadingInput.value = true;
|
|
|
+ const data = {
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ contentType: KeFuMessageContentTypeEnum.TEXT,
|
|
|
+ stateId: messageListRef.value ? messageListRef.value.messageList.length + 2 : 2,
|
|
|
+ content: 'ecologicalValueCalculate',
|
|
|
+ relUserId: route.value.relUserId,
|
|
|
+ };
|
|
|
+ await EventSourceFun(data, false);
|
|
|
+});
|
|
|
+const userInfo = computed(() => sheep.$store('user').userInfo);
|
|
|
+const addChat = async () => {
|
|
|
+ await init();
|
|
|
+ loadingInput.value = true;
|
|
|
+ messageListRef.value.messageList = [];
|
|
|
+ layoutRef.value.show = false;
|
|
|
+ difyConversationId.value = '';
|
|
|
+ const data = {
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ contentType: KeFuMessageContentTypeEnum.TEXT,
|
|
|
+ stateId: messageListRef.value ? messageListRef.value.messageList.length + 2 : 2,
|
|
|
+ content: 'ecologicalValueCalculate',
|
|
|
+ relUserId: route.value.relUserId,
|
|
|
+ };
|
|
|
+ await EventSourceFun(data, false);
|
|
|
+}
|
|
|
+// 发送消息
|
|
|
+async function onSendMessage() {
|
|
|
+ if (!chat.msg) return;
|
|
|
+ try {
|
|
|
+ loadingInput.value = true;
|
|
|
+ const data = {
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ contentType: KeFuMessageContentTypeEnum.TEXT,
|
|
|
+ stateId: messageListRef.value.messageList.length + 2,
|
|
|
+ content: JSON.stringify({ text: chat.msg }),
|
|
|
+ relUserId: route.value.relUserId,
|
|
|
+ };
|
|
|
+ await EventSourceFun(data);
|
|
|
+ chat.msg = '';
|
|
|
+ } finally {
|
|
|
+ chat.showTools = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//======================= 聊天工具相关 start =======================
|
|
|
+
|
|
|
+function handleToolsClose() {
|
|
|
+ chat.showTools = false;
|
|
|
+ chat.toolsMode = '';
|
|
|
+}
|
|
|
+
|
|
|
+function onEmoji(item) {
|
|
|
+ chat.msg += item.name;
|
|
|
+}
|
|
|
+
|
|
|
+// 点击工具栏开关
|
|
|
+function onTools(mode) {
|
|
|
+ if (!chat.toolsMode || chat.toolsMode === mode) {
|
|
|
+ chat.showTools = !chat.showTools;
|
|
|
+ }
|
|
|
+ chat.toolsMode = mode;
|
|
|
+ if (!chat.showTools) {
|
|
|
+ chat.toolsMode = '';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onShowSelect(mode) {
|
|
|
+ chat.showTools = false;
|
|
|
+ chat.showSelect = true;
|
|
|
+ chat.selectMode = mode;
|
|
|
+}
|
|
|
+
|
|
|
+async function onSelect({ type, data }) {
|
|
|
+ console.log(data, 555222233);
|
|
|
+ let msg;
|
|
|
+ switch (type) {
|
|
|
+ case 'image':
|
|
|
+ const res = await FileApi.uploadFile(data.tempFiles[0].path);
|
|
|
+ msg = {
|
|
|
+ contentType: KeFuMessageContentTypeEnum.IMAGE,
|
|
|
+ content: JSON.stringify({ picUrl: res.data }),
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ case 'goods':
|
|
|
+ msg = {
|
|
|
+ contentType: KeFuMessageContentTypeEnum.PRODUCT,
|
|
|
+ content: JSON.stringify(data),
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ case 'order':
|
|
|
+ msg = {
|
|
|
+ contentType: KeFuMessageContentTypeEnum.ORDER,
|
|
|
+ content: JSON.stringify(data),
|
|
|
+ conversationId: reateDifyParams.value.id,
|
|
|
+ };
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (msg) {
|
|
|
+ // 发送消息
|
|
|
+ // scrollBottom();
|
|
|
+ // await KeFuApi.sendKefuMessage(msg);
|
|
|
+ await KeFuApi.listSendNew(msg);
|
|
|
+ await messageListRef.value.refreshMessageList();
|
|
|
+ chat.showTools = false;
|
|
|
+ chat.showSelect = false;
|
|
|
+ chat.selectMode = '';
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+:deep(.z-paging-content-fixed) {
|
|
|
+ // background: red;
|
|
|
+ // top: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.zp-paging-container-content) {
|
|
|
+ padding: 0px 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.dropdownClass {
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.chat-wrap {
|
|
|
+ .page-bg {
|
|
|
+ width: 100%;
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ background-color: #3a74f2;
|
|
|
+ z-index: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status {
|
|
|
+ position: relative;
|
|
|
+ box-sizing: border-box;
|
|
|
+ z-index: 3;
|
|
|
+ height: 70rpx;
|
|
|
+ padding: 0 30rpx;
|
|
|
+ background: var(--ui-BG-Main-opacity-1);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 30rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ color: var(--ui-BG-Main);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.lsdhClass {
|
|
|
+ margin: 20px 0;
|
|
|
+ height: calc(100vh - 40px);
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .addClass {
|
|
|
+ background: #C1D6F7;
|
|
|
+ border: #C1D6F7 2px solid;
|
|
|
+ color: #4076FE;
|
|
|
+ border-radius: 20px;
|
|
|
+ font-weight: 800;
|
|
|
+ padding: 16px;
|
|
|
+ margin: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .lsdhDetailClass {
|
|
|
+ background: #f5f7f9;
|
|
|
+ padding: 16px;
|
|
|
+ margin: 20px;
|
|
|
+ width: 60vw;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+a {
|
|
|
+ color: red !important;
|
|
|
+}
|
|
|
+</style>
|