feature/zoujing #4
@@ -1,17 +1,18 @@
|
||||
<template>
|
||||
<!-- 页面根 -->
|
||||
<div class="h-full overflow-hidden flex flex-col">
|
||||
<div class="flex flex-col h-full my-6 px-6" :class="isGuidePage ? 'overflow-auto' : 'overflow-hidden'">
|
||||
|
||||
<div class="border-box px-6 pt-40 pb-6 ">
|
||||
<!-- 引导页顶部 welcome(仅在引导页显示) -->
|
||||
<div v-if="isGuidePage" class="border-box pt-30">
|
||||
<h1 class="text-[28px] font-bold mb-7 leading-tight">
|
||||
你好,<br />
|
||||
我今天能帮你什么?
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 消息列表(唯一滚动区) -->
|
||||
<div ref="listRef" class="flex-1 overflow-y-auto px-6 py-6 space-y-6">
|
||||
<!-- 主体滚动区:聊天或引导页内容 -->
|
||||
<div v-if="!isGuidePage" ref="listRef" class="flex-1 overflow-y-auto py-6 space-y-6">
|
||||
<!-- 聊天消息列表 -->
|
||||
<div v-for="msg in chatMsgList" :key="msg.messageId" class="flex items-start gap-3"
|
||||
:class="msg.messageRole === MessageRole.ME ? 'justify-end' : 'justify-start'">
|
||||
|
||||
@@ -45,32 +46,31 @@
|
||||
</template>
|
||||
</ChatRoleAI>
|
||||
|
||||
<!-- User avatar -->
|
||||
<ChatAvatar v-if="msg.messageRole === MessageRole.ME" :src="userAvatar" />
|
||||
<!-- User avatar -->
|
||||
<ChatAvatar v-if="msg.messageRole === MessageRole.ME" :src="userAvatar" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入区(固定底部,不滚) -->
|
||||
<div class="shrink-0 px-6 py-4 gap-3">
|
||||
<!-- 输入区 -->
|
||||
<div class="flex flex-col py-4 gap-3" :class="isGuidePage ? 'mt-16' : ''">
|
||||
<div class="inline-flex items-center justify-center w-[108px]
|
||||
px-3 py-1.5 rounded-2xl border border-[#E5E8EE]
|
||||
text-[13px] text-[#333]">
|
||||
px-3 py-1.5 rounded-2xl border border-[#E5E8EE]
|
||||
text-[13px] text-[#333]">
|
||||
智能问数
|
||||
</div>
|
||||
|
||||
<ChatInputArea v-model="inputMessage" :isSendingMessage="isSendingMessage" @send="sendMessageAction"
|
||||
<ChatInputArea v-model="inputMessage" :isSendingMessage="isSendingMessage" @send="onGuideSend"
|
||||
@attach="addAttachmentAction" />
|
||||
</div>
|
||||
|
||||
<!-- 任务中心 -->
|
||||
<TaskCenter />
|
||||
|
||||
<!-- 任务中心(仅在引导页显示) -->
|
||||
<TaskCenter v-if="isGuidePage" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onMounted, nextTick, onUnmounted } from "vue";
|
||||
import { ref, defineProps, defineEmits, watch, nextTick } from 'vue'
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { WebSocketManager } from "@common/WebSocketManager";
|
||||
import { MessageRole, ChatMessage } from "./model/ChatModel";
|
||||
import { IdUtils } from "@common/index";
|
||||
@@ -89,8 +89,27 @@ import { Session } from '../../utils/storage';
|
||||
import userAvatar from '@assets/images/login/user_icon.png';
|
||||
import aiAvatar from '@assets/images/login/blue_logo.png';
|
||||
|
||||
/// 是否是引导页
|
||||
const isGuidePage = ref(true);
|
||||
// 支持外部通过 prop 控制是否为引导页
|
||||
const props = defineProps({
|
||||
guide: { type: Boolean, default: true }
|
||||
});
|
||||
const emit = defineEmits(['update:guide']);
|
||||
|
||||
/// 是否是引导页(内部响应式)
|
||||
const isGuidePage = ref(props.guide);
|
||||
|
||||
// 同步外部变化到内部
|
||||
watch(() => props.guide, (v) => {
|
||||
isGuidePage.value = v;
|
||||
});
|
||||
// 将内部变化通知父组件
|
||||
watch(isGuidePage, (v) => {
|
||||
emit('update:guide', v);
|
||||
if (v) {
|
||||
// 当切换到引导页时,重置/清理会话状态
|
||||
resetConversation();
|
||||
}
|
||||
});
|
||||
|
||||
// 列表滚动容器引用
|
||||
const listRef = ref<HTMLElement | null>(null);
|
||||
@@ -189,9 +208,16 @@ const handleReplyInstruct = async (message: string, type: string) => {
|
||||
setTimeoutScrollToBottom();
|
||||
};
|
||||
|
||||
/// 选择标签事件
|
||||
/// 选择标签事件:切换到聊天页并发送
|
||||
const onTagSelect = (text: string) => {
|
||||
handleReplyText(text);
|
||||
isGuidePage.value = false;
|
||||
nextTick(() => handleReplyText(text));
|
||||
};
|
||||
|
||||
// 在引导页中按发送:切换到聊天页再发送
|
||||
const onGuideSend = () => {
|
||||
isGuidePage.value = false;
|
||||
nextTick(() => sendMessageAction());
|
||||
};
|
||||
|
||||
/// 添加附件按钮事件
|
||||
@@ -747,5 +773,33 @@ const resetConfig = () => {
|
||||
pendingMap.clear();
|
||||
};
|
||||
|
||||
// 清空会话并停止相关活动(保留 websocket 连接以便继续使用)
|
||||
const resetConversation = () => {
|
||||
try {
|
||||
// 如果正在发送,尝试发送停止请求
|
||||
try {
|
||||
if (isSendingMessage.value) sendStopAction();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// 清理 pendingTimeouts
|
||||
for (const t of pendingTimeouts.values()) {
|
||||
clearTimeout(t);
|
||||
}
|
||||
pendingTimeouts.clear();
|
||||
pendingMap.clear();
|
||||
|
||||
// 清理消息与状态
|
||||
chatMsgList.value = [];
|
||||
inputMessage.value = '';
|
||||
isSendingMessage.value = false;
|
||||
isSessionActive.value = false;
|
||||
currentSessionMessageId = null;
|
||||
} catch (e) {
|
||||
console.warn('resetConversation failed', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<!-- 唯一滚动容器 -->
|
||||
<div class="h-full overflow-y-auto">
|
||||
|
||||
<!-- Hero:吸顶 -->
|
||||
<div class="bg-white border-box px-6 pt-40 pb-6 max-[800px]:px-5">
|
||||
<h1 class="text-[28px] font-bold mb-7 leading-tight">
|
||||
你好,<br />
|
||||
我今天能帮你什么?
|
||||
</h1>
|
||||
|
||||
<!-- input -->
|
||||
<div class="flex flex-col mt-20 gap-3">
|
||||
<div class="inline-flex items-center justify-center w-[108px]
|
||||
px-3 py-1.5 rounded-2xl border border-[#E5E8EE]
|
||||
text-[13px] text-[#333]">
|
||||
智能问数
|
||||
</div>
|
||||
|
||||
<ChatInputArea v-model="inputMessage" @send="sendMessageAction" @attach="addAttachmentAction" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TaskCenter />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ChatInputArea from './components/ChatInputArea.vue';
|
||||
import TaskCenter from './TaskCenter.vue';
|
||||
import { ref } from 'vue';
|
||||
const inputMessage = ref<string>('');
|
||||
const sendMessageAction = () => {
|
||||
console.log('发送消息:', inputMessage.value);
|
||||
// 这里可以添加发送消息的逻辑
|
||||
};
|
||||
const addAttachmentAction = () => {
|
||||
console.log('添加附件');
|
||||
// 这里可以添加附件的逻辑
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -36,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, defineEmits } from 'vue'
|
||||
import { RiAddLine, RiArrowRightSLine, RiArrowDownSLine } from '@remixicon/vue'
|
||||
|
||||
/// 记录选择的历史消息ID
|
||||
@@ -124,8 +124,11 @@ const selectGroupKey = (index: number) => {
|
||||
}
|
||||
|
||||
/// TODO: 添加新对话
|
||||
const emit = defineEmits(['new-chat'])
|
||||
|
||||
const addNewChat = () => {
|
||||
console.log('add new chat')
|
||||
emit('new-chat')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex-1 px-6 pb-6">
|
||||
<div class="flex-1 pb-6">
|
||||
<div class="flex justify-between items-center py-4">
|
||||
<h3 class="text-base font-semibold">任务中心</h3>
|
||||
<a class="text-[#3b82f6] text-[13px] cursor-pointer">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="h-[174px] bg-white rounded-lg border border-[#eef2f6] shadow-[0_1px_0_rgba(0,0,0,0.03)] p-[18px] mt-[8px] flex flex-col justify-between">
|
||||
<textarea
|
||||
rows="2"
|
||||
placeholder="给我发布或者布置任务xx"
|
||||
placeholder="给我发布或者布置任务"
|
||||
class="flex-1 resize-none outline-none text-sm"
|
||||
:value="modelValue"
|
||||
@input="onInput"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<layout>
|
||||
<div class="flex h-full w-full flex-col md:flex-row ">
|
||||
<chat-history class="flex-none w-50" />
|
||||
<ChatHistory class="flex-none w-50" @new-chat="guide = true" />
|
||||
<div class="flex-1 mr-2 overflow-hidden bg-white rounded-xl">
|
||||
<!-- <chat-guide /> -->
|
||||
<chat-box />
|
||||
<ChatBox v-model:guide="guide" />
|
||||
</div>
|
||||
<TaskList />
|
||||
</div>
|
||||
@@ -14,6 +13,8 @@
|
||||
<script setup lang="ts">
|
||||
import TaskList from '@renderer/components/TaskList/index.vue'
|
||||
import ChatHistory from './ChatHistory.vue'
|
||||
import ChatGuide from './ChatGuide.vue'
|
||||
import ChatBox from './ChatBox.vue'
|
||||
import { ref } from 'vue'
|
||||
const guide = ref(true)
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user