gerson
2024-06-30 2a2c1177eef03dc2387950c59596704a0390622e
chat
已修改7个文件
已添加2个文件
296 ■■■■ 文件已修改
src/api/ai/chat.ts 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/Chat.vue 171 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/SummaryCom.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/model/types.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/component/sidebar/waterLeftAside/asideNew.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/component/sidebar/waterLeftAside/types.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/chatRoom.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/ai/chat.ts
@@ -123,21 +123,18 @@
};
export const GetHistoryGroups = async (params, req:any = request) => {
export const GetHistoryGroups = async ( req:any = request) => {
    return req({
      url: "/history/get_history_groups",
      method: "POST",
      data: params,
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
      },
    });
  };
  
export const QueryHistoryGroup = async (params, req:any = request) => {
export const QueryHistoryDetail = async (params, req:any = request) => {
    return req({
      url: "/history/query_history_detail",
      method: "POST",
@@ -147,3 +144,16 @@
      },
    });
  };
export const GetHistoryAnswer= async (params, req:any = request) => {
    return req({
      url: "/history/get_history_answer",
      method: "POST",
      data: params,
      headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
      },
    });
  };
src/components/chat/Chat.vue
@@ -2,18 +2,12 @@
    <div class="flex flex-col h-full">
        <div class="h-full flex flex-col items-center overflow-y-auto">
            <div ref="chatListDom" class="h-full w-[100ch]">
                <div
                    class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg"
                    v-for="(item, index) of messageList.filter((v) => v.role !== 'system')"
                    :key="index"
                >
                <div class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg" v-for="(item, index) of computedMessageList" :key="index">
                    <img class="rounded-full size-12 mr-4" :src="roleImageMap[item.role]" alt="" srcset="" />
                    <div class="flex">
                        <div class="relative" v-if="item.content?.values">
                            <div
                                class="text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch] bg-white"
                            >
                            <div class="text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch] bg-white">
                                <component class="max-w-[100ch]" :is="answerTypeMapCom[item.content.type]" :data="item.content.values" />
                            </div>
@@ -37,17 +31,18 @@
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from 'vue';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import useClipboard from 'vue-clipboard3';
import Loding from './components/Loding.vue';
import { RecordSet } from './model/Record';
import type { ChatContent } from './model/types';
import { AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types';
import { QuestionAi } from '/@/api/ai/chat';
import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi } from '/@/api/ai/chat';
import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
import router from '/@/router';
import { activeChatRoom } from '/@/stores/chatRoom';
import { activeChatRoom, activeRoomId } from '/@/stores/chatRoom';
import { ElMessage } from 'element-plus';
import { content } from 'html2canvas/dist/types/css/property-descriptors/content';
let isTalking = ref(false);
let messageContent = ref<ChatContent>({
@@ -56,39 +51,77 @@
});
const chatListDom = ref<HTMLDivElement>();
const messageList = ref<ChatMessage[]>([]);
onMounted(() => {
    if (!activeChatRoom.value) {
        router.replace({
            name: 'Home',
        });
        return;
    }
    messageContent.value = {
        type: AnswerType.Text,
        values: activeChatRoom.value.title,
    };
    sendChatMessage();
const computedMessageList = computed(() => {
    return messageList.value.filter((v) => v.role !== RoleEnum.system);
});
// onMounted(() => {
//     if (!activeChatRoom.value) {
//         router.replace({
//             name: 'Home',
//         });
//         return;
//     }
//     messageContent.value = {
//         type: AnswerType.Text,
//         values: activeChatRoom.value.title,
//     };
//     sendChatMessage();
// });
const questionAi = async (text) => {
    // const res = await QuestionAi({
    //     question: text,
    // });
    const res = {
        json_ok: true,
        question: '昨日五一广场压力',
        answer_type: 'recordset',
        values: {
            names: ['yesterday', 'max_pressure'],
            values: [
                ['2024-06-28 00:00:00', 24.378],
                ['2024-06-29 00:00:00', 24.276],
            ],
            type: 'records',
            title: '昨日五一广场(D_GW_04)的最大压力值',
        },
    };
const getAnswerById = async (historyId: string) => {
    return await GetHistoryAnswer({
        history_id: historyId,
    });
};
let currentSectionId = '';
watch(
    () => activeRoomId.value,
    async (val) => {
        if (!val) {
            router.replace({
                name: 'Home',
            });
            return;
        }
        const res = await QueryHistoryDetail({
            history_group_id: activeRoomId.value,
        });
        messageList.value = (res.details ?? []).map((item) => {
            return {
                role: RoleEnum.user,
                content: {
                    type: AnswerType.Text,
                    values: item.question,
                },
            } as ChatMessage;
        });
        currentSectionId = res?.details?.[0]?.section_a_id;
        const resList = await Promise.all((res.details ?? []).map((item) => getAnswerById(item.history_id)));
        let i = 0;
        resList.map((item, index) => {
            const insertIndex = index + 1 + i;
            messageList.value.splice(insertIndex, 0, {
                role: RoleEnum.assistant,
                content: parseContent(item.answer),
            });
            i++;
        });
        // messageContent.value = {
        //     type: AnswerType.Text,
        //     values: activeChatRoom.value.title,
        // };
        // sendChatMessage();
        console.log("🚀 ~ messageList.value:", messageList.value)
    },
    {
        immediate: true,
    }
);
const parseContent = (res) => {
    let content: ChatContent = {
        type: AnswerType.Text,
        values: '发生错误!',
@@ -103,12 +136,51 @@
        case AnswerType.Text:
            content = {
                type: AnswerType.Text,
                values: res.values,
                values: res.values ?? res.answer,
            };
            break;
        case AnswerType.Knowledge:
            content = {
                type: AnswerType.Knowledge,
                values: res.knowledge,
            };
            break;
        default:
            content = {
                type: AnswerType.Text,
                values: '其他',
            };
            break;
    }
    return content;
};
const questionAi = async (text) => {
    const res = await QuestionAi({
        question: text,
        section_a_id: currentSectionId,
        history_group_id: activeRoomId.value,
    });
    // const res = {
    //     json_ok: true,
    //     question: '昨日五一广场压力',
    //     answer_type: 'recordset',
    //     values: {
    //         names: ['yesterday', 'max_pressure'],
    //         values: [
    //             ['2024-06-28 00:00:00', 24.378],
    //             ['2024-06-29 00:00:00', 24.276],
    //         ],
    //         type: 'records',
    //         title: '昨日五一广场(D_GW_04)的最大压力值',
    //     },
    // };
    const content =  parseContent(res);
    console.log("🚀 ~ content:", content)
    return content;
};
@@ -152,11 +224,18 @@
const scrollToBottom = () => {
    if (!chatListDom.value) return;
    chatListDom.value.lastElementChild.scrollIntoView();
    // scrollTo(0, chatListDom.value.scrollHeight);
    chatListDom.value.lastElementChild?.scrollIntoView();
};
watch(messageList.value, () => nextTick(() => scrollToBottom()));
watch(
    messageList,
    () => {
        nextTick(() => scrollToBottom());
    },
    {
        deep: true,
    }
);
//#region ====================== èŠå¤©å†…容操作 ======================
src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
<template>
    <div class="whitespace-pre-line space-y-7">
        <div v-for="(item, index) in data" :key="index">
            <div>{{ item.answer }}</div>
            <div class="space-y-1 mt-2">
                <div v-for="(cItem, index) in item.contexts" :key="index">
                    <div class="text-blue-500 cursor-pointer inline-block" @click="pageLinkClick(cItem)">
                        <SvgIcon name="ele-Link" />
                        <span class="ml-2">{{ cItem.metadata?.Title }}</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
defineProps(['data'])
const pageLinkClick = (item) => {
    const nwin = window.open('');
    nwin.document.write(`<p style="white-space:pre-line">${item.page_content}</p>`)
    nwin.focus();
    if(item.metadata.Title){
        nwin.document.title = item.metadata.Title
    }
};
</script>
<style scoped lang="scss"></style>
src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue
@@ -102,7 +102,9 @@
onMounted(() => {
    setTimeout(() => {
        const parent = chartRef.value.parentElement;
        const parentBound = parent.getBoundingClientRect();
        chartInstance = echarts.init(chartRef.value,undefined,{
            width:parentBound.width,
src/components/chat/chatComponents/summaryCom/SummaryCom.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
<template>
    <div></div>
</template>
<script setup lang="ts"></script>
<style scoped lang="scss"></style>
src/components/chat/model/types.ts
@@ -1,5 +1,6 @@
import RecordSetCom from '../chatComponents/recordSetCom/RecordSetCom.vue';
import NormalTextCom from '../chatComponents/normalTextCom/NormalTextCom.vue';
import knowledgeCom from '../chatComponents/knowledgeCom/KnowledgeCom.vue';
import assistantPic from '../images/assistant.jpg';
import userPic from '../images/user.svg';
@@ -10,7 +11,7 @@
}
export const answerTypeMapCom = {
    [AnswerType.Knowledge]: RecordSetCom,
    [AnswerType.Knowledge]: knowledgeCom,
    [AnswerType.RecordSet]: RecordSetCom,
    [AnswerType.Text]:NormalTextCom
};
src/layout/component/sidebar/waterLeftAside/asideNew.vue
@@ -35,7 +35,7 @@
                    @click="roomClick(item)"
                >
                    <div class="ywicon icon-xiaoxi flex-0 mr-2.5"></div>
                    <div class="flex-auto text-ellipsis text-nowrap text-sm group-hover:text-[#0084ff]">chat room</div>
                    <div class="flex-auto text-ellipsis text-nowrap text-sm group-hover:text-[#0084ff]">{{ item.title }}</div>
                    <div class="text-gray-100 flex items-center space-x-2 ml-1">
                        <div class="ywicon invisible icon-bianji visible group-hover:visible !text-sm"></div>
@@ -56,7 +56,8 @@
import { nextTick, onMounted, reactive, ref } from 'vue';
import type { ChatRoomItem } from './types';
import router from '/@/router';
import { activeRoomId, chatRoomList } from '/@/stores/chatRoom';
import { CreateHistoryGroup, GetHistoryGroups } from '/@/api/ai/chat';
import { chatRoomList, activeRoomId } from '/@/stores/chatRoom';
let state = reactive({
    searchInput: '',
    selectDateOption: [
@@ -101,9 +102,13 @@
    }
};
const newChatRoomClick = () => {
const newChatRoomClick = async () => {
    const res = await CreateHistoryGroup({
        group_title: 'chat room',
    });
    const newRoom = {
        id: new Date().getTime() + '' + Math.floor(Math.random() * 1000),
        id: res.history_group_id,
        isInitial: true,
        title: 'chat room',
    };
@@ -134,13 +139,22 @@
    chatRoomRef.value.firstElementChild?.scrollIntoView();
};
onMounted(() => {
    if (router.currentRoute.value.name === 'Home') {
        if (!chatRoomList.value || chatRoomList.value.length === 0) {
            newChatRoomClick();
        } else {
            roomClick(chatRoomList.value[0]);
        }
onMounted(async () => {
    const res = await GetHistoryGroups();
    const resData = (res?.groups || []) as any[];
    chatRoomList.value = resData?.map((item) => {
        return {
            id: item.group_id,
            title: item.group_title,
            createTime: item.create_time,
            isInitial: false,
        };
    });
    if (!chatRoomList.value || chatRoomList.value.length === 0) {
        newChatRoomClick();
    } else {
        roomClick(chatRoomList.value[0]);
    }
});
</script>
src/layout/component/sidebar/waterLeftAside/types.ts
@@ -1,8 +1,6 @@
export type ChatRoomItem = {
    id:string;
    isInitial:boolean,
    title:string,
    content?:string
}
    id: string;
    isInitial:boolean,
    title: string;
    createTime?: string;
};
src/stores/chatRoom.ts
@@ -2,17 +2,9 @@
import type { ChatRoomItem } from '../layout/component/sidebar/waterLeftAside/types';
import { Local } from '../utils/storage';
export const chatRoomList = ref<ChatRoomItem[]>(Local.get('chatRoomList'));
export const chatRoomList = ref<ChatRoomItem[]>([]);
watch(
    () => chatRoomList.value,
    (val) => {
        Local.set('chatRoomList', val);
    },
    {
        deep: true,
    }
);
export const activeRoomId = ref(null);
export const activeChatRoom = computed(() => chatRoomList.value?.find((item) => item.id === activeRoomId.value));