| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965 |
- <!-- eslint-disable vue/no-v-html -->
- <template>
- <div class="btn" @click="showAIChat">
- <div style="display: flex; flex-direction: row" class="btn-header">
- <img src="@/assets/images/vent/home/wakeBtn.png" />
- </div>
- </div>
- <div v-if="isShowChatBroad" class="mini-chat">
- <!-- 左侧折叠区域 -->
- <div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
- <div
- v-if="isFold"
- class="historyBtn"
- :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/history.svg' : '/src/assets/images/vent/home/history.svg'})` }"
- @click="addNew"
- ></div>
- <div v-else class="historyBtn1">
- <span
- class="btn-text-bg"
- :style="{
- backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/history.svg' : ''})`,
- }"
- ></span>
- <span v-if="!isFold" class="btn-text">历史对话</span>
- <a-list style="width: 130px" :split="false" :data-source="sessionHistory" :scroll="200" class="custom-list">
- <template #renderItem="{ item }">
- <a-list-item
- class="session-item"
- :style="{
- padding: '8px 10px 0 8px',
- color: '#5e7081',
- fontSize: '10px',
- position: 'relative', // 新增定位
- }"
- >
- <!-- 新增flex布局容器 -->
- <div style="width: 100%">
- <div v-if="editingId !== item.id" class="text-container">
- <span class="edit-text" @click="sessionsHistory(item.id)">{{ item.name }}</span>
- <div class="btn-container">
- <EditOutlined class="edit-icon" @click="startEditing(item)" />
- <DeleteOutlined class="delete-icon" @click="startDelete(item)" />
- </div>
- </div>
- <!-- 输入框 -->
- <a-input
- size="small"
- v-else
- v-model:value="editText"
- v-focus
- @blur="handleSave(item)"
- @keyup.enter="handleSave(item)"
- class="edit-input"
- />
- </div>
- </a-list-item>
- </template>
- </a-list>
- </div>
- <div
- class="foldBtn"
- :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/Fold.svg' : '/src/assets/images/vent/home/unfold.svg'})` }"
- @click="fold"
- ></div>
- </div>
- <!-- 右侧对话框 -->
- <div class="right-side">
- <!-- 对话区域 -->
- <div class="dialog-area">
- <div
- v-for="message in messageHistory"
- :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="message-wrapper user-message-wrapper">
- <div class="ask-message">{{ message.content }}</div>
- <CopyOutlined class="copy-icon" @click="copyToClipboard(message.content)" title="复制消息" />
- </div>
- </template>
- <template v-else>
- <SvgIcon size="80" class="ml-2px mr-2px" name="ai-logo" />
- <div class="message-wrapper ai-message-wrapper">
- <div class="answer-message">
- <div v-if="message.contentR1" class="color-gray font-size-12px" v-html="message.contentR1"> </div>
- <div v-else v-html="message.content"> </div>
- </div>
- <CopyOutlined class="copy-icon" @click="copyToClipboard(message.contentR1 || message.content)" title="复制消息" />
- </div>
- </template>
- </div>
- </div>
- <!-- 底部输入区 -->
- <div class="input-area">
- <a-input v-model:value="inputText" placeholder="请输入你的问题" @keyup.enter="handleSend(inputText)" class="ant-input" />
- <div class="ctrl-btn">
- <div class="input-controls">
- <button class="control-btn">深度学习</button>
- <button class="control-btn" @click="stopReq()">停止响应</button>
- </div>
- <div class="action-bar">
- <Space>
- <Button class="control-btn1" size="small" @click="showModal()">
- <template #icon>
- <SvgIcon name="send-file" />
- </template>
- </Button>
- <Button class="control-btn2" size="small" @click="handleSend(inputText)">
- <template #icon>
- <SvgIcon name="send" />
- </template>
- </Button>
- </Space>
- </div>
- </div>
- <!-- 右侧文件上传区 -->
- <div v-if="open" class="file-upload">
- <!-- 输入框区域,包含确认按钮 -->
- <div class="input-container">
- <a-input v-model:value="filePath" placeholder="输入文件连接" class="file-input" @pressEnter="handlePathConfirm" />
- <button class="confirm-btn" @click="handlePathConfirm">确认</button>
- </div>
- <!-- 上传按钮 -->
- <!-- <a-upload> <button class="upload-btn" @click="customUpload">从本地上传</button></a-upload> -->
- <a-upload class="custom-upload" name="file" :multiple="false">
- <a-button class="upload-btn" @click="customUpload">
- <UploadOutlined></UploadOutlined>
- 从本地上传
- </a-button>
- </a-upload>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { ref, onMounted } from 'vue';
- import { SvgIcon } from '../Icon';
- import { Space, Button, Modal, Input, message } from 'ant-design-vue';
- // import AIChat from './index.vue';
- import { useUserStore } from '/@/store/modules/user';
- import { EditOutlined, DeleteOutlined, UploadOutlined, CopyOutlined } from '@ant-design/icons-vue';
- import { createVNode } from 'vue';
- const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
- const inputText = ref(''); // 输入框内容
- const sessionHistory = ref([]);
- const isShowChatBroad = ref(false);
- const editingId = ref<number | null>(null);
- const editText = ref('');
- const currentSessionID = ref('');
- const taskID = ref('');
- const open = ref<boolean>(false);
- interface ListItem {
- id: number;
- name?: string;
- }
- interface Message {
- id: string; // 唯一标识(可用时间戳生成)
- type: 'user' | 'system' | 'response';
- content: string;
- /** 深度思考时的文本 */
- contentR1: string;
- timestamp: number; // 排序依据
- }
- // 定义消息历史数组类型
- const messageHistory = ref<Message[]>([]);
- const isFold = ref(true); // 是否折叠
- const userid = useUserStore().getUserInfo.id as string;
- const filePath = ref(''); // 绑定输入框值
- const showConfirmBtn = ref(false); // 控制确认按钮显示状态
- const fileList = ref([]);
- function showAIChat() {
- isShowChatBroad.value = !isShowChatBroad.value;
- }
- //获取消息列表
- // async function handleSend(data) {
- // messageHistory.value.push({
- // id: `user_${Date.now()}`,
- // type: 'user',
- // content: data,
- // contentR1: '',
- // timestamp: Date.now(),
- // });
- // // 发送 POST 请求
- // fetch('http://39.97.59.228:8000/v1/chat-messages', {
- // method: 'POST',
- // headers: {
- // 'Content-Type': 'application/json',
- // Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- // },
- // body: JSON.stringify({
- // conversation_id: currentSessionID.value,
- // query: data,
- // response_mode: 'streaming',
- // user: userid,
- // inputs: {},
- // }),
- // }).then((response) => {
- // const decoder = new TextDecoder('utf-8');
- // let buffer = [];
- // // 获取可读流
- // const reader = response.body.getReader();
- // const newMessage = {
- // id: `response_${Date.now()}`,
- // type: 'response' as any,
- // content: '',
- // contentR1: '',
- // timestamp: Date.now(),
- // };
- // messageHistory.value.push(newMessage);
- // // 读取数据
- // function read() {
- // return reader.read().then(({ done, value }) => {
- // if (done) {
- // return buffer;
- // }
- // // 解码数据块
- // const chunk = decoder.decode(value, { stream: false });
- // // 处理每段数据
- // const processedData = processStreamChunk(chunk);
- // buffer = buffer.concat(processedData);
- // // 继续读取
- // return read();
- // });
- // }
- // // 开始读取
- // return read();
- // function processStreamChunk(chunk) {
- // try {
- // // 移除 "data: " 前缀
- // const jsonStr = chunk.replace('data: ', '');
- // const data = JSON.parse(jsonStr);
- // const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
- // if (!targetMessage) return;
- // // 根据事件类型分发处理
- // switch (data.event) {
- // case 'message':
- // if (!taskID.value && !currentSessionID.value) {
- // taskID.value = data.task_id;
- // currentSessionID.value = data.conversation_id;
- // }
- // targetMessage.content += data.answer; // 追加内容
- // break;
- // }
- // return data;
- // } catch (error) {
- // // 请求失败时设置系统消息
- // return null;
- // }
- // }
- // });
- // inputText.value = '';
- // }
- // 复制消息
- function copyToClipboard(text) {
- if (!text || text.trim() === '') {
- message.warn('没有可复制的内容');
- return;
- }
- // 2. 创建临时textarea 元素
- const textarea = document.createElement('textarea');
- textarea.value = text;
- textarea.style.position = 'fixed';
- textarea.style.top = '-999px';
- textarea.style.left = '-999px';
- textarea.style.width = '200px';
- textarea.style.height = '200px';
- document.body.appendChild(textarea);
- try {
- textarea.select();
- textarea.setSelectionRange(0, text.length);
- const isSuccessful = document.execCommand('copy');
- if (isSuccessful) {
- message.success('复制成功!');
- } else {
- throw new Error('复制命令执行失败');
- }
- } catch (err) {
- console.error('复制失败:', err);
- message.error('复制失败,请手动复制');
- } finally {
- document.body.removeChild(textarea);
- }
- }
- async function handleSend(data) {
- inputText.value = '';
- messageHistory.value.push({
- id: `user_${Date.now()}`,
- type: 'user',
- content: data,
- contentR1: '',
- timestamp: Date.now(),
- });
- try {
- const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: JSON.stringify({
- conversation_id: currentSessionID.value,
- query: data,
- response_mode: 'streaming',
- user: userid,
- inputs: {},
- }),
- });
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- const decoder = new TextDecoder('utf-8');
- const reader = response.body.getReader();
- let textBuffer = ''; // 使用字符串缓冲区来累积数据
- const newMessage = {
- id: `response_${Date.now()}`,
- type: 'response',
- content: '',
- contentR1: '',
- timestamp: Date.now(),
- };
- messageHistory.value.push(newMessage);
- while (true) {
- const { done, value } = await reader.read();
- if (done) {
- if (textBuffer) {
- processLine(textBuffer);
- }
- break;
- }
- textBuffer += decoder.decode(value, { stream: true });
- // 处理每一行数据
- let lineIndex;
- while ((lineIndex = textBuffer.indexOf('\n')) !== -1) {
- const line = textBuffer.substring(0, lineIndex).trim();
- textBuffer = textBuffer.substring(lineIndex + 1);
- if (line) {
- processLine(line);
- }
- }
- }
- function processLine(line) {
- if (line.startsWith('data: ')) {
- try {
- const jsonStr = line.substring('data: '.length);
- const data = JSON.parse(jsonStr);
- switch (data.event) {
- case 'message':
- if (data.answer) {
- const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
- if (targetMessage) {
- targetMessage.content += data.answer;
- }
- }
- if (data.task_id && !taskID.value) taskID.value = data.task_id;
- if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
- break;
- }
- } catch (error) {
- console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
- }
- }
- }
- } catch (error) {
- console.error('Error in handleSend:', error);
- // 在 UI 上显示错误信息
- messageHistory.value.push({
- id: `system_${Date.now()}`,
- type: 'system',
- content: '请求错误',
- contentR1: '',
- timestamp: Date.now(),
- });
- }
- }
- // 上传文件
- const showModal = () => {
- open.value = !open.value;
- };
- async function customUpload(data) {
- const formData = new FormData();
- if (!data) {
- return message.warn('请选择文件');
- }
- // 添加文件参数
- formData.append('file', data.file);
- // 添加用户标识参数
- formData.append('user', userid);
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
- method: 'POST',
- headers: {
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: formData,
- });
- // if (response) {
- // message.success('上传成功');
- // }
- console.log(response, '123');
- if (!response) {
- throw new Error('Network response was not ok');
- }
- } catch (error) {
- console.error('保存失败:', error);
- }
- }
- //停止响应
- async function stopReq() {
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID}/stop`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: JSON.stringify({
- user: userid,
- }),
- });
- if (!response) {
- throw new Error('Network response was not ok');
- }
- } catch (error) {
- console.error('保存失败:', error);
- }
- }
- //获取具体会话记录
- async function sessionsHistory(id: string) {
- console.log(id, '123');
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/messages?conversation_id=${id}&user=${userid}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- });
- const data = await response.json();
- console.log(data, '123');
- if (data.data.length > 0) {
- messageHistory.value = [];
- data.data.forEach((item: any) => {
- messageHistory.value.push({
- id: `user_${Date.now()}`,
- type: 'user',
- content: item.query,
- contentR1: '',
- timestamp: Date.now(),
- });
- messageHistory.value.push({
- id: `system_${Date.now()}`,
- type: 'system',
- content: item.answer,
- contentR1: '',
- timestamp: Date.now(),
- });
- });
- }
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- } catch (error) {
- console.error('保存失败:', error);
- }
- editingId.value = null;
- }
- //编辑标题
- const startEditing = (item: ListItem) => {
- editingId.value = item.id;
- editText.value = item.name || '';
- };
- // 输入框确认按钮点击事件
- async function handlePathConfirm() {
- const formData = new FormData();
- // 添加文件参数
- formData.append('file', filePath.value);
- // 添加用户标识参数
- formData.append('user', userid);
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
- method: 'POST',
- headers: {
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: formData,
- });
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- } catch (error) {
- console.error('保存失败:', error);
- }
- console.log('确认的文件路径:', filePath.value);
- // 这里可以添加路径验证、保存等逻辑
- filePath.value = ''; // 可选:确认后清空输入框
- showConfirmBtn.value = false;
- }
- // 保存修改
- const handleSave = async (item: ListItem) => {
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}/name`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: JSON.stringify({
- name: editText.value,
- user: userid,
- }),
- });
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- item.name = editText.value;
- } catch (error) {
- console.error('保存失败:', error);
- }
- editingId.value = null;
- };
- // 删除会话
- const startDelete = async (item: ListItem) => {
- Modal.confirm({
- title: '确认删除',
- content: `确定要删除会话 "${item.name || '新会话'}" 吗?此操作不可撤销。`,
- okText: '确认',
- cancelText: '取消',
- onOk: async () => {
- // 原有删除逻辑不变
- try {
- let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}`, {
- method: 'DELETE',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- body: JSON.stringify({
- user: userid,
- }),
- });
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- getHistoryList();
- } catch (error) {
- console.error('删除失败:', error);
- Modal.error({
- title: '删除失败',
- content: '删除会话时出现错误,请稍后重试。',
- });
- }
- },
- });
- };
- const fold = () => {
- isFold.value = !isFold.value;
- if (!isFold.value) {
- getHistoryList();
- }
- };
- // 获取历史会话列表
- async function getHistoryList() {
- let response = await fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
- method: 'get',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
- },
- });
- const data = await response.json();
- sessionHistory.value = data.data;
- console.log(sessionHistory.value, '123');
- }
- // 初始化按钮定位
- onMounted(() => {
- getHistoryList();
- });
- </script>
- <style lang="less" scoped>
- .btn-header {
- width: 40px;
- height: 40px;
- margin-right: 5px;
- margin-bottom: 5px;
- }
- .mini-chat {
- display: flex;
- width: 500px;
- height: 400px;
- border-radius: 4px;
- position: fixed;
- top: 60px;
- right: 20px;
- background-color: rgb(255, 255, 255);
- background: url('../../assets/images/warn-dialog-bg.png') no-repeat center;
- background-size: 100% 100%;
- z-index: 9999999;
- color: #fff;
- }
- .left-side {
- background: #0c2842;
- transition: width 0.5s ease; /* 平滑过渡动画 */
- width: 140px; /* 展开时宽度 */
- position: relative; /* 用于按钮定位 */
- }
- .left-side.collapsed {
- width: 40px; /* 折叠时宽度 */
- }
- .custom-list {
- height: 325px;
- overflow-y: auto;
- }
- .text-container {
- display: flex;
- justify-content: space-between;
- width: 100%;
- overflow: hidden;
- }
- .btn-container {
- display: flex;
- }
- .jeecg-layout-header-action span[role='img'] {
- padding: 0;
- }
- .text-ellipsis {
- flex: 1;
- }
- .edit-text {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- width: 90px;
- color: #fff;
- font-size: 12px;
- cursor: pointer;
- }
- .edit-icon {
- flex-shrink: 0;
- margin-left: auto;
- line-height: 23px;
- }
- .delete-icon {
- flex-shrink: 0;
- margin-left: auto;
- line-height: 23px;
- }
- .edit-icon:hover {
- color: #1890ff !important;
- cursor: pointer;
- }
- .delete-icon:hover {
- color: #1890ff !important;
- cursor: pointer;
- }
- .edit-input {
- font-size: 10px;
- }
- .btn-text-bg {
- width: 14px;
- height: 14px;
- position: absolute;
- background-size: 100% 100%;
- right: 10px;
- top: 10px;
- left: 10px;
- bottom: 10px;
- }
- .btn-text {
- margin-left: 3px;
- font-size: 12px;
- color: #fff;
- white-space: nowrap;
- margin-left: 30px;
- line-height: 35px;
- }
- .historyBtn {
- width: 20px;
- height: 20px;
- position: absolute;
- background-size: 100% 100%;
- background-position: center;
- padding: 2px;
- right: 10px;
- top: 10px;
- }
- .historyBtn1 {
- width: 20px;
- height: 20px;
- position: absolute;
- background-size: 100% 100%;
- background-position: center;
- left: 3px;
- }
- .divider0 {
- border-bottom: 1px solid #1074c1;
- width: auto;
- margin: 0 10px;
- height: 13%;
- display: block;
- background: transparent;
- }
- .foldBtn {
- width: 20px;
- height: 20px;
- position: absolute;
- background-size: 100% 100%;
- background-position: center;
- padding: 2px;
- right: 10px;
- bottom: 10px;
- cursor: pointer;
- }
- .right-side {
- flex: 1; /* 占据剩余空间 */
- 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 {
- margin: 10px 10px 20px 10px;
- padding: 10px;
- background-color: #043256;
- border: 1px solid #2cb6ff;
- border-radius: 5px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- gap: 5px;
- height: 25%;
- }
- /* 文件列表容器 */
- .uploaded-files {
- padding: 8px;
- background-color: #f5f5f5;
- border-radius: 4px;
- min-height: 40px;
- }
- /* 单个文件项 */
- .file-item {
- display: inline-flex;
- align-items: center;
- padding: 4px 8px;
- margin-right: 8px;
- margin-bottom: 8px;
- background-color: #fff;
- border: 1px solid #e9e9e9;
- border-radius: 4px;
- }
- /* 文件名 */
- .file-name {
- margin-left: 8px;
- margin-right: 8px;
- max-width: 150px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .ant-input {
- background-color: rgba(255, 255, 255, 0) !important;
- border: none;
- outline: none;
- }
- .ant-input:focus {
- border: none; /* 聚焦时无边框 */
- outline: none; /* 聚焦时无轮廓 */
- box-shadow: none; /* 移除可能存在的阴影效果 */
- }
- .ctrl-btn {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- }
- .question-input {
- background-color: #1e293b !important;
- border-color: #334155 !important;
- color: #e2e8f0 !important;
- border-radius: 8px !important;
- padding: 12px 16px !important;
- font-size: 14px !important;
- }
- .question-input::placeholder {
- color: #64748b !important;
- }
- .control-btn {
- height: 25px;
- background-color: #043256;
- border: 1px solid #2cb6ff;
- color: #fff;
- font-size: 10px;
- margin-right: 10px;
- cursor: pointer;
- transition: all 0.2s;
- }
- .control-btn:hover {
- background-color: #043256;
- color: #e2e8f0;
- }
- .control-btn1 {
- height: 20px;
- background-color: #234a6b;
- border: 1px solid #234a6b;
- color: #fff;
- font-size: 10px;
- margin-right: 10px;
- cursor: pointer;
- transition: all 0.2s;
- }
- .control-btn2 {
- height: 20px;
- background-color: #2cb6ff;
- border: 1px solid #2cb6ff;
- color: #fff;
- font-size: 10px;
- margin-right: 10px;
- cursor: pointer;
- transition: all 0.2s;
- }
- /* 文件上传区 */
- .file-upload {
- position: absolute;
- right: 20px;
- bottom: 70px;
- width: 180px;
- display: flex;
- flex-direction: column;
- gap: 10px;
- border: 1px solid #2cb6ff;
- background-color: #234a6b;
- border-radius: 6px;
- padding: 10px;
- }
- .input-container {
- position: relative;
- display: flex;
- align-items: center;
- width: 100%;
- }
- .file-input {
- flex: 1;
- background-color: #234a6b;
- border-color: #2cb6ff !important;
- color: #e2e8f0 !important;
- border-radius: 6px !important;
- font-size: 10px !important;
- padding-right: 70px !important;
- height: 36px !important;
- width: 100% !important;
- }
- .confirm-btn {
- position: absolute;
- right: 5px;
- background-color: #2cb6ff;
- border: none;
- color: #fff;
- border-radius: 4px;
- font-size: 12px;
- padding: 4px 10px;
- cursor: pointer;
- transition: all 0.2s;
- height: 28px;
- }
- .confirm-btn:hover {
- background-color: #2cb6ff;
- }
- .custom-upload {
- width: 100%;
- padding: 0 !important;
- }
- .upload-btn {
- background-color: #234a6b !important;
- border: 1px solid #2188c3 !important;
- color: #dbeafe !important;
- border-radius: 6px !important;
- font-size: 12px !important;
- cursor: pointer;
- transition: all 0.2s;
- padding: 8px 0 !important;
- width: 190% !important;
- height: 36px !important;
- box-sizing: border-box !important;
- }
- .upload-btn:hover {
- background-color: #1f84bd !important;
- color: #fff !important;
- }
- .custom-upload .ant-upload-select:hover .ant-btn {
- border-color: #1f84bd !important;
- }
- }
- </style>
- <style scoped>
- .zxm-popover-inner-content {
- padding: 1px;
- }
- .message-wrapper {
- display: flex;
- align-items: flex-start;
- position: relative;
- }
- .user-message-wrapper {
- flex-direction: row-reverse;
- }
- .ai-message-wrapper {
- flex-direction: row;
- }
- /* 复制图标样式 */
- .copy-icon {
- font-size: 16px;
- cursor: pointer;
- margin: 4px 8px;
- color: #fff;
- }
- .message-wrapper:hover .copy-icon {
- opacity: 1;
- }
- .copy-icon:hover {
- color: #1890ff;
- }
- </style>
|