| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- <!-- eslint-disable vue/no-v-html -->
- <template>
- <div class="mini-chat">
- <!-- 左侧折叠区域 -->
- <div class="left-side">
- <SvgIcon name="add" size="20" />
- <Popover trigger="click" :overlay-inner-style="{ padding: '1px' }">
- <template #content>
- <AIChat style="width: 700px; height: 500px" :visible="true" />
- </template>
- <SvgIcon :name="dialogVisible ? 'zoom-out' : 'zoom-in'" size="20" @click="openDialog" />
- </Popover>
- </div>
- <!-- 右侧对话框 -->
- <div class="right-side">
- <!-- 对话区域 -->
- <div ref="dialogRef" class="dialog-area">
- <div
- v-for="message in store.getMessageHistory"
- :key="message.id"
- class="flex items-center w-100%"
- :style="{ alignSelf: message.type === 'user' ? 'flex-end' : 'flex-start' }"
- >
- <template v-if="message.type === 'user'">
- <div class="flex-grow-1"></div>
- <div class="ask-message">{{ message.content }}</div>
- </template>
- <template v-else>
- <SvgIcon size="30" class="ml-2px mr-2px" name="ai-logo" />
- <div class="answer-message">
- <div v-if="message.contentR1" class="color-gray font-size-12px" v-html="formatMessage(message.contentR1)"> </div>
- <div v-html="formatMessage(message.content)"> </div>
- </div>
- </template>
- </div>
- </div>
- <!-- 底部操作栏 -->
- <div class="input-area">
- <TextArea v-model:value="inputText" placeholder="请输入你的问题" />
- <div class="action-bar">
- <!-- 左侧深度思考按钮 -->
- <div class="think-btn" :class="{ active: store.deepseekR1Enable }" @click="toggleThinking"> <span>深度思考</span> </div>
- <!-- 右侧操作按钮 -->
- <Space>
- <SvgIcon name="send-image" />
- <SvgIcon name="send-file" />
- <Button type="primary" shape="circle" size="small" :loading="store.streaming" @click="handleSend">
- <template #icon>
- <SvgIcon name="send" />
- </template>
- </Button>
- </Space>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, onMounted } from 'vue';
- import { SvgIcon } from '../Icon';
- import { Space, Button, Popover, Input } from 'ant-design-vue';
- import AIChat from './index.vue';
- import { useAIChat } from '/@/store/modules/AIChat';
- const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
- const dialogRef = ref<HTMLElement | null>(null);
- const dialogVisible = ref(true);
- const inputText = ref(''); // 输入框内容
- const store = useAIChat(); //获取用户信息
- const openDialog = () => {
- dialogVisible.value = !dialogVisible.value;
- };
- //启用深度思考
- const toggleThinking = () => {
- store.deepseekR1Enable = !store.deepseekR1Enable;
- };
- //获取消息列表
- async function handleSend() {
- store
- .sendQuestion(inputText.value, () => {
- if (dialogRef.value) {
- dialogRef.value.scrollTop = dialogRef.value.scrollHeight;
- }
- })
- .then(() => {
- inputText.value = '';
- });
- }
- //格式化消息
- function formatMessage(text: string) {
- let formatted = text
- // 处理换行
- .replace(/\n\n/g, '<br>')
- .replace(/\n###/g, '<br> ')
- .replace(/###/g, '')
- .replace(/---/g, '')
- // 处理粗体
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
- // 处理斜体
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
- // 处理行内代码
- .replace(/`([^`]+)`/g, '<code>$1</code>');
- return formatted;
- }
- // 初始化按钮定位
- onMounted(() => {
- // store.getSessionHistory();
- });
- </script>
- <style lang="less" scoped>
- .mini-chat {
- display: flex;
- }
- .left-side {
- width: 40px; /* 折叠时宽度 */
- background: #0c2842;
- transition: width 0.5s ease; /* 平滑过渡动画 */
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- align-items: center;
- }
- .right-side {
- flex: 1; /* 占据剩余空间 */
- background: #09172c;
- display: flex;
- flex-direction: column;
- .dialog-area {
- flex: 1; /* 占据剩余空间 */
- gap: 10px; /* 消息块间隔统一控制 */
- overflow-y: auto; /* 垂直滚动条 */
- padding: 5px;
- display: flex;
- flex-direction: column;
- color: #fff;
- .ask-message {
- padding: 10px;
- border-radius: 5px;
- background: #0c2842;
- max-width: 80%;
- }
- .answer-message {
- padding: 10px;
- border-radius: 5px;
- background: #0c2842;
- max-width: 90%;
- }
- }
- .input-area {
- background-color: #043256;
- padding: 5px;
- textarea {
- background-color: transparent;
- width: 100%;
- height: 40px;
- border: none;
- resize: none;
- outline: none;
- overflow: hidden;
- padding: 10px; /* 统一内边距 */
- color: #fff;
- }
- .action-bar {
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- .think-btn {
- border: 1px solid #ccc;
- width: 100px;
- height: 20px;
- line-height: 20px;
- text-align: center;
- border-radius: 5px;
- cursor: pointer;
- background: transparent;
- color: white;
- transition: background 0.3s;
- }
- .think-btn.active {
- background: #1890ff;
- color: white;
- border-color: #1890ff;
- }
- }
- }
- }
- </style>
- <style>
- .zxm-popover-inner-content {
- padding: 1px;
- }
- </style>
|