92d65447c6681635068b212fc5d9f72c8a665d2c..dd58c1d3a27ba48a5df050aab7c586bb9b988914
2025-04-09 wujingjing
activeTopMenuStyle
dd58c1 对比 | 目录
2025-04-09 wujingjing
资源占用中,请检查后再试~
f17a10 对比 | 目录
2025-04-09 wujingjing
asr open close
678b13 对比 | 目录
2025-04-09 wujingjing
完善数字人
28706d 对比 | 目录
2025-04-09 wujingjing
frontQuestion
c669ca 对比 | 目录
已修改9个文件
已添加6个文件
581 ■■■■ 文件已修改
src/components/chat/Chat.vue 191 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/contentCbCom/InputSelect.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/contentCbCom/Select.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/contentCbCom/TimeRange.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/contentCbCom/index.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/contentCbCom/index.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/chatComponents/summaryCom/SummaryCom.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/components/ChatContainer.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/components/playBar/hook/useDigitalHuman.ts 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/hooks/types.ts 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/model/types.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/component/header/Header.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/chatRoom.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/stores/global.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/chat/Chat.vue
@@ -48,22 +48,25 @@
<script setup lang="ts">
import type { CancelTokenSource } from 'axios';
import axios from 'axios';
import { ElMessage } from 'element-plus';
import moment from 'moment';
import { v4 as uuidv4 } from 'uuid';
import { computed, nextTick, onActivated, onMounted, ref } from 'vue';
import { loadAmisSource } from '../amis/load';
import ChatContainer from './components/ChatContainer.vue';
import ShareLinkDlg from './components/shareLink/index.vue';
import type { SendMsg } from './hooks/types';
import { useLoadData } from './hooks/useLoadData';
import { useScrollLoad } from './hooks/useScrollLoad';
import { useSyncMsg } from './hooks/useSyncMsg';
import MessageList from './messageList/index.vue';
import type { ChatContent } from './model/types';
import { AnswerState, AnswerType, RoleEnum, type ChatMessage } from './model/types';
import { getShareChatJsonByPost, questionStreamByPost } from '/@/api/ai/chat';
import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
import CustomDrawer from '/@/components/drawer/CustomDrawer.vue';
import { Logger } from '/@/model/logger/Logger';
import { triggerRef } from 'vue';
import {  ElMessage } from 'element-plus';
import ChatContainer from './components/ChatContainer.vue';
import ShareLinkDlg from './components/shareLink/index.vue';
import router from '/@/router';
import MessageList from './messageList/index.vue';
import {
    activeChatRoom,
    activeGroupType,
@@ -73,14 +76,12 @@
    isSharePage,
    roomConfig,
} from '/@/stores/chatRoom';
import { ParentRegister } from '/@/stores/global';
import emitter from '/@/utils/mitt';
import { deepClone } from '/@/utils/other';
import { useCompRef } from '/@/utils/types';
import { toFormData, toMyFixed } from '/@/utils/util';
import { useLoadData } from './hooks/useLoadData';
import { useSyncMsg } from './hooks/useSyncMsg';
import { getCurrentPosition } from '/@/utils/brower';
import { deepClone } from '/@/utils/other';
import { ParentRegister } from '/@/stores/global';
const containerRef = useCompRef(ChatContainer);
const chatListDom = computed(() => containerRef.value?.chatListDom);
@@ -234,7 +235,7 @@
                    return;
                    // chunkRes.value = '准备数据分析';
                }
                if (chunkRes.mode === 'main_frame') {
                    const jsonObj = JSON.parse(chunkRes.value);
                    if (!enableCallback.value) {
@@ -309,7 +310,6 @@
                        lastReport.conclusion = chunkRes.value;
                    }
                    chunkRes.value = '分析结束';
                }
                const getLastGroup = () => {
                    const lastGroup = computedMessageList.value.at(-1).stepGroup[0];
@@ -431,12 +431,27 @@
let currentLLMId = null;
const stopGenClick = () => {
const resetTalking = () => {
    lastAxiosSource?.cancel();
    isTalking.value = false;
    chatListLoading.value = false;
    streamOutputIsStart = false;
};
const stopGenClick = () => {
    resetTalking();
    if (isFrontQuestion) {
        ParentRegister.notify?.({
            type: 'msg_stop',
        });
    }
    computedMessageList.value.at(-1).isStopMsg = true;
};
const finishFrontQuestion = () => {
    resetTalking();
    ParentRegister.updateChildCallObj('sendMsg', null);
};
const checkCanSend = (content: ChatContent = messageContent.value) => {
@@ -457,6 +472,7 @@
        role: RoleEnum.assistant,
        content: {
            type: AnswerType.Report,
            values: [],
        },
        state: AnswerState.Null,
        stepGroup: [
@@ -484,10 +500,57 @@
    playBarRef.value?.clearAttach();
};
const updateUserInfo = (userItem: ChatMessage, other: { historyId: string; question: string }) => {
    userItem.historyId = other.historyId;
    const current = moment().format('YYYY-MM-DD HH:mm:ss');
    userItem.createTime = current;
    userItem.content.values = other.question ?? userItem.content.values;
};
const updateAssistantInfo = (
    assistantItem: ChatMessage,
    resMsgContent: ChatContent,
    other: { historyId: string; question: string }
) => {
    const current = moment().format('YYYY-MM-DD HH:mm:ss');
    assistantItem.historyId = other.historyId;
    const currentTime = formatShowTimeYear(current);
    assistantItem.createTime = currentTime;
    assistantItem.content = resMsgContent;
};
const updateInfo = (
    userItem: ChatMessage,
    assistantItem: ChatMessage,
    resMsgContent: ChatContent,
    other: {
        historyId: string;
        question: string;
    }
) => {
    updateUserInfo(userItem, other);
    updateAssistantInfo(assistantItem, resMsgContent, other);
};
const handleAfterQuestion = (
    userItem: ChatMessage,
    assistantItem: ChatMessage,
    resMsgContent: ChatContent,
    other: { historyId: string; question: string }
) => {
    updateLoadIndex();
    updateInfo(userItem, assistantItem, resMsgContent, other);
    setTimeout(() => {
        // æ”¶åˆ°å›žå¤ï¼Œç»§ç»­æ»š
        scrollToBottom();
    }, 300);
};
const sendChatMessage = async (content: ChatContent = messageContent.value) => {
    if (!checkCanSend(content)) {
        return;
    }
    isFrontQuestion = false;
    const isNewChat = messageList.value.length === 0;
    if (isNewChat) {
        if (activeSampleId.value) {
@@ -503,22 +566,96 @@
    try {
        const [userItem, assistantItem] = addChatItem(content);
        resMsgContent = await questionAi(content.values);
        updateLoadIndex();
        userItem.historyId = questionRes?.history_id;
        const current = moment().format('YYYY-MM-DD HH:mm:ss');
        userItem.createTime = current;
        userItem.content.values = questionRes?.question ?? userItem.content.values;
        assistantItem.historyId = questionRes?.history_id;
        const currentTime = formatShowTimeYear(current);
        assistantItem.createTime = currentTime;
        assistantItem.content = resMsgContent;
        setTimeout(() => {
            // æ”¶åˆ°å›žå¤ï¼Œç»§ç»­æ»š
            scrollToBottom();
        }, 300);
        handleAfterQuestion(userItem, assistantItem, resMsgContent, {
            historyId: questionRes?.history_id,
            question: questionRes?.question,
        });
    } catch (error: any) {}
};
let isFrontQuestion = false;
const sendFrontChatMessage = async (content: ChatContent = messageContent.value): Promise<any> => {
    isFrontQuestion = true;
    const [userItem, assistantItem] = addChatItem(content);
    const promise = new Promise((resolve, reject) => {
        const receiveMsg = (msg: SendMsg) => {
            console.log('receiveMsg', msg);
            if (!assistantItem.content?.values) {
                assistantItem.content.values = [];
            }
            switch (msg.type) {
                case 'text':
                    // å¼€å§‹å¢žåŠ æ–°çš„ stepGroup,后续的 stepGroup å¹¶æ²¡æœ‰å®žé™…作用,只是为了做迭代用,迭代出组件,屎山代码实在太难改了!!!
                    assistantItem.stepGroup.push({
                        value: [],
                        isShow: true,
                    });
                    assistantItem.content.values.push({
                        content: {
                            type: 'knowledge',
                            values: [
                                {
                                    answer: msg.value,
                                },
                            ],
                        },
                    });
                    break;
                case 'select':
                case 'confirm':
                case 'input':
                    // å¼€å§‹å¢žåŠ æ–°çš„ stepGroup,后续的 stepGroup å¹¶æ²¡æœ‰å®žé™…作用,只是为了做迭代用,迭代出组件,屎山代码实在太难改了!!!
                    assistantItem.stepGroup.push({
                        value: [],
                        isShow: true,
                    });
                    assistantItem.content.values.push({
                        content: {
                            type: 'content_cb',
                            values: msg,
                        },
                    });
                    break;
                case 'info':
                    if (msg.value === 'finish') {
                        finishFrontQuestion();
                        return resolve({ userItem, assistantItem, resMsgContent: assistantItem.content });
                    }
                    break;
                default:
                    break;
            }
            scrollToBottom();
            triggerRefresh();
        };
        ParentRegister.updateChildCallObj('sendMsg', receiveMsg);
    });
    return promise;
};
const questionSelf = (content: string) => {
    const myContent = { type: AnswerType.Text, values: content };
    if (!checkCanSend(myContent)) {
        return false;
    }
    sendFrontChatMessage(myContent).then(({ userItem, assistantItem, resMsgContent }) => {
        handleAfterQuestion(userItem, assistantItem, resMsgContent, {
            historyId: uuidv4(),
            question: content,
        });
    });
    return true;
};
const backQuestion = (content: { question: string; data: any }) => {
    sendChatMessage({ type: AnswerType.Text, values: content.question });
};
ParentRegister.updateChildCallObj('frontQuestion', questionSelf);
ParentRegister.updateChildCallObj('backQuestion', backQuestion);
const sendClick = () => {
    sendChatMessage(messageContent.value);
src/components/chat/chatComponents/contentCbCom/InputSelect.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
<template>
    <div class="flex flex-col gap-2">
        <span class="text-gray-600 font-normal">{{ `${item?.data?.title}` }}</span>
        <div  class="flex-items-center gap-5">
            <!-- <span
                @click="select(subItem)"
                v-for="(subItem, index) in item?.data?.options"
                :key="index"
                class="flex w-fit items-center cursor-pointer border-solid border px-3 border-gray-300 hover:text-blue-400 rounded-lg"
                :class="{ 'cursor-not-allowed': disabled, 'bg-blue-400': subItem === activeOption, 'text-white': subItem === activeOption }"
                >{{ subItem }}</span
            > -->
            <el-select v-if="isSelect" v-model="activeOption" placeholder="请选择" @change="selectChange">
                <el-option v-for="item in item?.data?.options" :key="item" :label="item" :value="item"></el-option>
            </el-select>
            <div v-else class="flex items-center gap-3">
                <el-input   v-model="inputValue" />
                <el-button type="primary" @click="submitInput">提交</el-button>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { question_stream_reply } from '/@/api/ai/chat';
import { debounce } from '/@/utils/util';
const props = defineProps(['item', 'disabled']);
// :class="[...(subItem === activeOption ? ['bg-blue-400', 'text-white'] : []), disabled ? 'cursor-not-allowed' : '']"
// 'bg-blue-400': subItem === activeOption, 'text-white': subItem === activeOption.value
const isSelect = computed(() => props.item?.data?.options?.length > 0);
const inputValue = ref('');
const activeOption = ref();
const selectChange = async (option) => {
    if (props.disabled) return;
    const res = await question_stream_reply({
        select: option,
        reply_id: props.item?.data?.reply_id,
    });
    if (res.json_ok) {
        activeOption.value = option;
    }
};
const submitInput = () => {
    selectChange(inputValue.value);
};
</script>
<style scoped lang="scss"></style>
src/components/chat/chatComponents/contentCbCom/Select.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<template>
    <div class="flex flex-col gap-1">
        <span class="text-gray-600 font-normal">{{ `${item?.label}` }}</span>
        <div v-if="item?.options?.length > 0" class="flex-items-center gap-5">
            <span
                @click="select(subItem)"
                v-for="(subItem, index) in item?.options"
                :key="index"
                class="flex w-fit items-center cursor-pointer border-solid border px-3 border-gray-300 hover:text-white rounded-lg"
                :class="{
                    'cursor-not-allowed': disabled,
                    'bg-blue-400': subItem.value === item.value,
                    'text-white': subItem.value === item.value,
                }"
                >{{ subItem.label }}</span
            >
        </div>
    </div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { ParentRegister } from '/@/stores/global';
const props = defineProps(['item', 'disabled']);
let hasExecute = false;
const select = (option) => {
    if (hasExecute) return;
    props.item.value = option.value;
    hasExecute = true;
    ParentRegister.notify?.({
        type: 'msg_callback',
        value: {
            type: props.item.type,
            value: option.value,
            event: props.item.event,
        },
    });
};
</script>
<style scoped lang="scss"></style>
src/components/chat/chatComponents/contentCbCom/TimeRange.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
<template>
    <div class="flex flex-col gap-1">
        <span class="text-gray-600 font-normal">{{ `${item?.data?.title}` }}</span>
        <TimeRange :disabled="disabled" @change="timeRangeChange" ></TimeRange>
    </div>
</template>
<script lang="ts" setup>
import { definePropType } from 'element-plus/es/utils';
import type { PropType } from 'vue';
import TimeRange from '../summaryCom/components/recordSet/components/TimeRange.vue';
const props = defineProps({
    item: {
        type: Object as PropType<any>,
    },
    disabled: {
        type: Boolean,
        default: false,
    },
});
const emit = defineEmits(['change']);
// const timeRangeValue = defineModel({
//     type: definePropType<[string, string]>(Array),
// });
const timeRangeChange = (val: [string, string]) => {
    if (props.disabled) return;
    emit(
        'change',
        props.item?.data?.reply_id,
        {
            start_time: val[0],
            end_time: val[1],
        }
    );
};
</script>
src/components/chat/chatComponents/contentCbCom/index.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
import Select from './Select.vue';
import InputSelect from './InputSelect.vue';
import TimeRange from './TimeRange.vue';
export const enum ContentCbType {
    Select = 'select',
    InputSelect = 'input-select',
    TimeRange = 'time_range',
}
export const contentCbTypeMapCom = {
    [ContentCbType.Select]: Select,
    [ContentCbType.InputSelect]: InputSelect,
    [ContentCbType.TimeRange]: TimeRange,
};
src/components/chat/chatComponents/contentCbCom/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
<template>
    <div>
        <component :is="contentCbTypeMapCom[data.type]" :item="data"  />
    </div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { contentCbTypeMapCom } from '.';
import { chatComProps } from '../common';
const props = defineProps(chatComProps);
</script>
<style scoped lang="scss"></style>
src/components/chat/chatComponents/summaryCom/SummaryCom.vue
@@ -107,19 +107,5 @@
const toggleReport = (item) => {
    item.reportIsShow = !item.reportIsShow;
};
// const infoDlgIsShow = ref(false);
// const report = ref(null);
// const agentKey = ref('');
// const conclusion = computed(() => props.originData?.content?.origin?.conclusion ?? []);
// const showAgentReport = (agentStartIndex) => {
//     const agent = parsedData.value[agentStartIndex];
//     agentKey.value = agent.agent_key;
//     const foundReport = conclusion.value.find((item) => item.agent_key === agentKey.value);
//     report.value = foundReport?.report;
//     infoDlgIsShow.value = true;
// };
</script>
<style scoped lang="scss"></style>
src/components/chat/components/ChatContainer.vue
@@ -22,7 +22,7 @@
                    <i class="ywifont ywicon-xiangxiajiantou !text-[20px]" />
                </div>
            </div>
            <div v-show="digitalHumanIsShow" v-loading="humanIsLoading" class="absolute right-28 bottom-[250px] w-[400px] h-[540px]">
            <div v-show="digitalHumanIsShow" v-loading="humanIsLoading" class="absolute right-28 bottom-[250px] w-[303.75px] h-[540px]">
                <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-2 right-2 z-[1]" @click="closeDigitalHuman"></span>
                <div class="duix-container h-full w-full"></div>
            </div>
src/components/chat/components/playBar/hook/useDigitalHuman.ts
@@ -1,10 +1,12 @@
import { nextTick, onDeactivated, onMounted, ref } from 'vue';
import { SignJWT } from 'jose';
import { nextTick, onDeactivated, onMounted, ref } from 'vue';
import { markdownToTxt } from 'markdown-to-txt';
import './libs/duix.js';
import { questionStreamByPost } from '/@/api/ai/chat';
import { activeGroupType, activeRoomId } from '/@/stores/chatRoom';
import { markdownToTxt } from 'markdown-to-txt';
import axios from 'axios';
import { ElMessage } from 'element-plus';
export type UseDigitalHumanProps = {
    container: string;
@@ -29,6 +31,32 @@
    const closeDigitalHuman = () => {
        digitalHumanIsShow.value = false;
        resetDuixStatus();
    };
    /**
     * æ£€æŸ¥æ•°å­—人是否可用
     */
    const checkIsUseable = async () => {
        const config = {
            method: 'get',
            url: `https://duix.guiji.ai/duix-openapi-v2/v1/getconcurrentNumber?appId=${duixConfig.appId}`,
            headers: {
                priority: 'u=1, i',
                sig: duixConfig.sign,
            },
        };
        const response = await axios(config);
        const data = response.data.data;
        const total = data.totalConcurrentNumber;
        const user = data.userConcurrentNumber;
        if (total === null || total === 0) {
            return false;
        }
        if (total !== null && total === user) {
            return false;
        }
        return true;
    };
    const resetDuixStatus = () => {
@@ -59,6 +87,29 @@
    };
    let isWaitingSpeak = false;
    const speakContent = (content: string) => {
        // æ‰“断之前的已收到xxx
        duix.break();
        isWaitingSpeak = false;
        duix.speak({
            content,
        });
    };
    const startDuix = () => {
        const conversationId = duixConfig.conversationId; // duix平台会话id
        duix
            .start({ conversationId, openAsr: true, wipeGreen: true })
            .then((res) => {
                console.info('start', res);
            })
            .catch((err) => {
                console.error('start error', err);
            });
    };
    const initDuix = () => {
        const sign = duixConfig.sign; // sign由服务端生成
        const conversationId = duixConfig.conversationId; // duix平台会话id
@@ -71,9 +122,7 @@
        duix.on('intialSucccess', () => {
            console.info('intialSucccess');
            // æ­¤æ—¶åˆå§‹åŒ–成功,可调用start
            duix.start({ conversationId, openAsr: true }).then((res) => {
                console.info('start', res);
            });
            startDuix();
        });
        duix.on('bye', (data) => {
            console.info('bye', data);
@@ -98,7 +147,10 @@
            console.info('speakStart', data);
        });
        duix.on('speakEnd', (data) => {
            isReceiveRes.value = false;
            if (!isWaitingSpeak) {
                isReceiveRes.value = false;
                duix.openAsr().then((...a) => {});
            }
        });
        duix.on('speakSection', (data) => {
            console.info('speakSection', data);
@@ -108,18 +160,18 @@
        });
        duix.on('asrResult', (data) => {
            console.info('asrResult', data);
            if (isReceiveRes.value) {
                return;
            }
            duix.closeAsr().then((...a) => {});
            let hasResult = false;
            isReceiveRes.value = true;
            try {
                // isWaitingSpeak = true;
                // duix.speak({
                //     content: '已收到您的问题,正在思考中...请稍等',
                // });
                isWaitingSpeak = true;
                duix.speak({
                    content: '已收到您的问题,正在思考中...请稍等',
                });
                questionStreamByPost(
                    {
                        question: data,
@@ -132,26 +184,16 @@
                        if (chunkRes.mode === 'result' && chunkRes.value?.answer_type === 'knowledge') {
                            const plainText = getPlainText(chunkRes.value);
                            hasResult = true;
                            duix.speak({
                                content: plainText,
                            });
                        }
                        if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') {
                            speakContent(plainText);
                        } else if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') {
                            if (hasResult) return;
                            hasResult = true;
                            isWaitingSpeak = false;
                            duix.speak({
                                content: chunkRes.value.json_msg,
                            });
                            speakContent(chunkRes.value.json_msg);
                        }
                        if (chunkRes.mode === 'finish') {
                            if (!hasResult) {
                                isWaitingSpeak = false;
                                duix.speak({
                                    content: '暂时无法口头描述你所说的问题',
                                });
                                speakContent('暂时无法口头描述你所说的问题');
                            } else {
                                hasResult = false;
                            }
@@ -179,20 +221,23 @@
    let hasInitDuix = false;
    let duix: any;
    const openDigitalHuman = () => {
    const openDigitalHuman = async () => {
        duixConfig.sign = await createSig(duixConfig.appId, duixConfig.appKey, 60 * 60 * duixConfig.expired);
        const isUsable = await checkIsUseable();
        if (!isUsable) {
            ElMessage.warning('"资源占用中,请检查后再试~"');
            return;
        }
        digitalHumanIsShow.value = true;
        nextTick(async () => {
            duixConfig.sign = await createSig(duixConfig.appId, duixConfig.appKey, 60 * 60 * duixConfig.expired);
        nextTick(() => {
            if (!hasInitDuix) {
                hasInitDuix = true;
                duix = new DUIX();
                initDuix();
            } else {
                duix.start({ conversationId: duixConfig.conversationId, openAsr: true }).then((res) => {
                    console.info('start', res);
                });
                startDuix();
            }
        });
    };
src/components/chat/hooks/types.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
type TextMsg = {
    type: 'text';
    value: string;
    event: string;
};
type SelectMsg = {
    type: 'select';
    value: string;
    options: {
        label: string;
        value: string;
    }[];
    event: string;
};
type ConfirmMsg = {
    type: 'confirm';
    title: string;
    event: string;
};
type InputMsg = {
    type: 'input';
    label: string;
    /** @description è¾“入默认值 */
    value: string;
    event: string;
};
type InfoMsg = {
    type: 'info';
    /** @description ç»“束当前前端提问 */
    value: 'finish';
};
export type SendMsg = TextMsg | SelectMsg | ConfirmMsg | InputMsg | InfoMsg;
src/components/chat/model/types.ts
@@ -6,6 +6,10 @@
import type { Attach } from '../components/playBar/hook/useAttach';
import assistantPic from '/static/images/role/assistant-200x192.png';
import userPic from '/static/images/role/user-200x206.png';
import ContentCb from '../chatComponents/contentCbCom/index.vue';
import InputSelect from '../chatComponents/contentCbCom/InputSelect.vue';
import TimeRange from '../chatComponents/contentCbCom/TimeRange.vue';
export const enum AnswerType {
    Knowledge = 'knowledge',
    RecordSet = 'recordset',
@@ -15,6 +19,8 @@
    Map = 'map',
    Report = 'report_list',
    Script = 'script',
    ContentCb = 'content_cb',
}
export const answerTypeMapCom = {
@@ -23,6 +29,8 @@
    [AnswerType.Text]: NormalTextCom,
    [AnswerType.Summary]: SummaryCom,
    [AnswerType.Script]: ScriptMode,
    [AnswerType.ContentCb]: ContentCb,
};
export const enum RoleEnum {
src/layout/component/header/Header.vue
@@ -2,19 +2,10 @@
    <div class="top_text flex justify-between px-6 items-center pl-[unset] pr-6">
        <div class="flex-items-center h-full">
            <div class="nav-menu">
                <router-link :to="firstToPath" class="nav-item" active-class="active">
                <router-link v-for="item in menuList" :to="item.path" :key="item.label" class="nav-item" active-class="active">
                    <i class="icon-park-outline-robot"></i>
                    æ™ºèƒ½åŠ©æ‰‹
                    {{ item.label }}
                </router-link>
                <router-link to="/workspace/situation" class="nav-item" active-class="active">
                    <i class="icon-park-outline-workbench"></i>
                    ä¸ªäººå·¥ä½œå°
                </router-link>
                <!-- <router-link to="/gis/situation" class="nav-item" active-class="active">
                    <i class="icon-park-outline-system"></i>
                    GIS系统
                </router-link> -->
            </div>
        </div>
        <el-dialog
@@ -35,7 +26,7 @@
            </template>
        </el-dialog>
    </div>
    <div class="notice " v-if="!isSharePage">
    <div class="notice" v-if="!isSharePage">
        <el-badge :value="`${state.announcementList.length}`" :hidden="announcementIsRead || state.announcementList?.length === 0">
            <el-button link size="small" icon="ele-Message" class="set-notice" @click="handleAnnouncementClick">系统公告</el-button>
        </el-badge>
@@ -66,7 +57,7 @@
import { systemNotifyList } from '/@/api/ai/chat';
import router from '/@/router';
import pinia from '/@/stores';
import { activeChatRoom, activeRoomId, isSharePage, newChatRoomClick } from '/@/stores/chatRoom';
import { activeChatRoom, activeRoomId, activeTopMenuStyle, isSharePage, newChatRoomClick, TopMenuStyle } from '/@/stores/chatRoom';
import { useThemeConfig } from '/@/stores/themeConfig';
import emitter from '/@/utils/mitt';
import { userInfoKey } from '/@/utils/request';
@@ -80,6 +71,32 @@
    announcementContent: '',
    announcementTime: '',
});
const menuList = computed(() => {
    const smartAssistant = {
        path: firstToPath.value,
        label: '智能助手',
    };
    const personalWorkbench = {
        path: '/workspace/situation',
        label: '个人工作台',
    };
    const gis = {
        path: '/gis/situation',
        label: 'GIS系统',
    };
    switch (activeTopMenuStyle.value) {
        case TopMenuStyle.Normal:
            return [smartAssistant, personalWorkbench];
        case TopMenuStyle.Gis:
            return [smartAssistant, gis];
        default:
            return [smartAssistant];
    }
});
const firstToPath = computed(() => {
    if (!activeChatRoom.value)
        return {
src/stores/chatRoom.ts
@@ -304,3 +304,11 @@
    isLoginStatus.value = false;
    LoginInfo.remove();
};
/** @description é¡¶éƒ¨èœå•布局方式 */
export const enum TopMenuStyle {
    Normal = 'normal',
    Gis = 'gis',
}
export const activeTopMenuStyle = ref<TopMenuStyle>(TopMenuStyle.Normal);
src/stores/global.ts
@@ -161,4 +161,11 @@
            return childCallObj;
        };
    }
}
    static updateChildCallObj(key:string,callback:Function) {
        childCallObj[key] = callback;
    }
}
window.callObj = childCallObj;
vite.config.ts
@@ -58,7 +58,7 @@
            host: '0.0.0.0',
            port: env.VITE_PORT as unknown as number,
            open: JSON.parse(env.VITE_OPEN),
            hmr: true,
            hmr: false,
            proxy: {
                '/events': {
                    target: 'http://localhost:3000',