Files
Situation-Awareness-Platfor…/src/views/Jyh/AI/Home/index.vue

1087 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="home-container">
<div class="home-content">
<div v-if="startChat" ref="conversationRef" class="conversation-area">
<template v-for="(msg, idx) in messages" :key="idx">
<McBubble v-if="msg.role === 'user'" :content="msg.content" :align="'right'" ></McBubble>
<McBubble v-else :loading="msg.loading ?? false" >
<div class="think-toggle-btn" @click="toggleThink(idx)">
<div class="flex align-center">
<img style="height: 15px;margin-right: 8px" width="16" src="@/assets/imgs/jyh/ai/icon_skgc.png" />
<span class="span1">思考过程</span>
<i style="margin-left: auto" :class="btnIcon"></i>
</div>
<!-- 思考过程展示区域 -->
<div class="mc-think-block" :style="{ display: msg.thinkExpanded ? 'block' : 'none' }">
<div class="think-process">
<div v-for="(step, index) in msg.thinkProcess" :key="index" class="think-step flex align-center">
<i v-if="step.status === 1" style="font-size: 16px;margin-right: 8px" class="icon icon-right"></i>
<i v-else style="font-size: 16px;margin-right: 8px" class="icon icon-right"></i>
<span class="step-name span1">{{ step.name }}</span>
<span v-if="step.status === 1 || step.status === '成功'" style="margin-left: 8px" class="step-status green">成功</span>
<span v-else-if="step.status === 0 || step.status === '失败'" style="margin-left: 8px" class="step-status red">失败</span>
<span v-else style="margin-left: 8px" class="step-status">{{ step.status }}</span>
</div>
<div v-for="(item, index) in msg.steps" :key="index" class="think-step flex align-center" style="margin-left: 24px">
<span class="step-name span1">{{ item.name }}</span>
<span v-if="item.status === 1 || item.status === '成功'" style="margin-left: 8px" class="step-status green">成功</span>
<span v-else-if="item.status === 0 || item.status === '失败'" style="margin-left: 8px" class="step-status red">失败</span>
<span v-else style="margin-left: 8px" class="step-status">{{ item.status }}</span>
</div>
</div>
</div>
</div>
<div v-if="msg.type==='error'" class="flex align-center" style="margin-top: 6px">
<i style="font-size: 16px;margin-right: 8px" class="icon icon-info-o"></i>
<span class="span1">回答异常</span>
</div>
<div v-else class="flex align-center" style="margin-top: 6px">
<i style="font-size: 16px;margin-right: 8px" class="icon icon-right"></i>
<span class="span1">完成回答</span>
</div>
<!--开源软件列表-->
<OssList v-if="msg.type===1"/>
<!--预警列表-->
<WarningList v-else-if="msg.type===2"/>
<!--软件详情-->
<OssDetail :id="msg.id" :software_name="msg.software_name||''" v-else-if="msg.type==='critical_software_info'"/>
<!--列表展示-->
<AIList v-else-if="msg.type==='df'" :content="msg.content"/>
<!--text或者富文本展示-->
<Other v-else :content="msg.content"></Other>
<div class="bubble-bottom-operations">
<img style="cursor: pointer" @click="()=>handleRefresh(idx)" src="@/assets/imgs/jyh/ai/icon_generate.png" />
<i class="icon-copy-new" title="复制" @click="()=>handleFile(idx)"></i>
<i :class="msg.islike==='like'?'icon-like-solid':'icon-like'" title="点赞" @click="() => handleLike(idx, 'like')" ></i>
<i :class="msg.islike==='dislike'?'icon-like-solid icon-dislike-solid':'icon-dislike'" title="踩" @click="() => handleLike(idx, 'dislike')" ></i>
<!-- <i class="icon-clever-customer" title="智能客服" onclick="handleCustomerService()"></i>-->
<!-- <i class="icon-more-operate" title="更多操作" onclick="handleMore()"></i>-->
</div>
</McBubble>
<!-- <McBubble v-else :loading="msg.loading ?? false" :avatarConfig="msg.avatarConfig">-->
<!-- <McMarkdownCard :content="msg.content" :theme="theme"></McMarkdownCard>-->
<!-- <template #bottom>-->
<!-- <div class="bubble-bottom-operations">-->
<!-- <i class="icon-copy-new"></i>-->
<!-- <i class="icon-like"></i>-->
<!-- <i class="icon-dislike"></i>-->
<!-- </div>-->
<!-- </template>-->
<!-- </McBubble>-->
</template>
<div v-if="followupQuestions.length>0" class="conversation-cnxw">
<a @click="handleResetQuestions" class="flex conversation-cnxw-item">
<span class="span2">猜你想问</span>
<img style="height: 15px;margin-right: 16px" width="16" src="@/assets/imgs/jyh/ai/icon_generate.png" />
</a>
<a
v-for="(q, index) in followupQuestions"
:key="index"
class="g-header-copilot"
@click="onSubmit(q)"
>
<img src="@/assets/imgs/jyh/ai/icon_cnxw.png" style="height: 15px; margin-right: 8px" />
<span class="ml-xs">{{ q.question }}</span>
</a>
</div>
</div>
<div v-else class="welcome-page">
<div class="home-title">
<img src="@/assets/imgs/jyh/ai/aiLogo2.png" alt="logo" />
<h1>TcodeAI助手</h1>
</div>
<div class="home-detail">
<div class="home-detail-1">Hi欢迎使用TcodeAI助手</div>
<div class="home-detail-2">TcodeAI助手 可以辅助研发人员进行软件查询批量校验查看软件详情等</div>
<div class="home-detail-2">作为AI模型TcodeAI助手 提供的答案可能不总是确定或准确的但您的反馈可以帮助 TcodeAI助手 做得更好</div>
</div>
<div style="width: 100%;margin-top: -20px">
<SwiperComponent />
</div>
<!--猜你想问-->
<div style="width: 100%;margin-top: -20px">
<GuessYouWantToAsk @submit="onSubmit"/>
</div>
</div>
<!--操作按钮-->
<div class="new-convo-button">
<d-button icon="add" shape="circle" title="新建对话" size="sm" @click="onNewConvo" />
</div>
<!--输入框-->
<div style="padding: 0 12px 12px 12px;width: 100%">
<McInput :placeholder="placeholder" :value="inputValue" :maxLength="maxLength" @change="(e) => (inputValue = e)" @submit="onSubmit">
<template #extra>
<div class="input-foot-wrapper">
<div class="input-foot-left">
<span class="input-foot-maxlength">{{ inputValue.length }}/{{maxLength}}</span>
</div>
<div class="input-foot-right">
<d-button icon="op-clearup" shape="round" :disabled="!inputValue" @click="inputValue = ''">清空输入</d-button>
</div>
</div>
</template>
</McInput>
</div>
<!-- 添加使用条款部分 -->
<div class="usage-terms">
<p>内容由AI生成无法确保准确性和完整性仅供参考使用条款 隐私声明</p>
</div>
</div>
<div class="back-in-btn">
<div class="back-in-time-btn" @click="toggleHistory" :class="{ 'active': isHistoryVisible }">
<i class="icon icon-history" title="查看历史"></i>
</div>
</div>
<!-- <ViewHistoryAside ref="viewHistoryRef" :CONV_ID="CONV_ID" :username="username" v-show="isHistoryVisible" @select-conv="handleSelectConv"/>-->
<ViewHistoryAsideNew ref="viewHistoryRef" :CONV_ID="CONV_ID" :username="username" v-show="isHistoryVisible" @onNewConvo="onNewConvo" @select-conv="handleSelectConv"/>
</div>
</template>
<script setup lang="ts">
import { ref, defineComponent, nextTick, onMounted } from 'vue';
import { introPrompt, simplePrompt, mockAnswer, guessQuestions } from './mock.constants';
import SwiperComponent from './SwiperComponent.vue';
import GuessYouWantToAsk from './GuessYouWantToAsk.vue';
import OssList from './components/OssList.vue';
import WarningList from './components/WarningList.vue';
import OssDetail from './components/OssDetail.vue';
import Other from './components/Other.vue';
import AIList from './components/AIList.vue';
import ViewHistoryAside from './components/ViewHistoryAside/ViewHistoryAside.vue';
import ViewHistoryAsideNew from './components/ViewHistoryAside/ViewHistoryAsideNew.vue';
import axios from 'axios';
import { useDiscussGetUserInfo } from '@/api/discussion/hook';
import { Message } from 'vue-devui/message';
import { fetchChatUseSql, fetchConversationDetail, fetchCreateConversation, FetchFollowupQuestions } from '@/api/jyh';
// 获取当前用户信息 & 是否登录
const { isLogin = false, userInfo = {}} = useDiscussGetUserInfo();
const {username=''} = userInfo
const inputValue = ref('');
const startChat = ref(false);
const conversationRef = ref();
const isAgentOpen = ref(false);
const theme = ref('light');
const maxLength = ref(8000);
const placeholder = ref('请输入问题描述,描述越详细 DevMate 回复越准确( Enter发送Shift+Enter 换行,↑ 查看最近发出的消息 )');
let themeService;
const agentList = ref([
{ label: 'MateChat', value: 'matechat', active: true },
{ label: 'InsCode', value: 'inscode' },
]);
const selectedAgent = ref(agentList.value[0]);
const aiModelAvatar = {
imgSrc: 'https://matechat.gitcode.com/logo.svg',
width: 32,
height: 32,
};
const customerAvatar = {
imgSrc: 'https://matechat.gitcode.com/png/demo/userAvatar.svg',
width: 32,
height: 32,
};
const inputFootIcons = [
{ icon: 'icon-at', text: '智能体' },
{ icon: 'icon-standard', text: '词库' },
{ icon: 'icon-add', text: '附件' },
];
const messages = ref([]);
const followupQuestions = ref([]);
// 控制 ViewHistoryAside 组件的显示状态
const isHistoryVisible = ref(true);
// 切换 ViewHistoryAside 组件的显示与隐藏
const toggleHistory = () => {
isHistoryVisible.value =!isHistoryVisible.value;
};
const onInputIconClick = (e) => {
if (e.icon === 'icon-at') {
inputValue.value += `@${selectedAgent.value.label}`;
}
};
const onSubmit = async (e, answer = undefined) => {
debugger
let obj = {
role: 'user',
avatarPosition: 'side-right',
}
if(e === '') {
return;
}
if (e?.question) {
obj.content = e.question
if(e?.sql) {
obj.sql = e.sql
}
} else {
obj.content = e
}
inputValue.value = '';
if (!messages.value.length) {
startChat.value = true;
}
messages.value.push(obj);
followupQuestions.value = [];
// getAIAnswer(answer ?? e);
// 判断是否有会话id
if(!CONV_ID.value) {
// 调用创建会话接口
await createConversation()
}
await getAIAnswer(e,1);
nextTick(() => {
conversationRef.value?.scrollTo({
top: conversationRef.value.scrollHeight,
behavior: 'smooth',
});
});
};
const handleRefresh = (index: number) => {
const currentMsg = messages.value[index-1];
if (currentMsg.role === 'user' && currentMsg.content) { // 假设AI回答的question字段存储了用户原始问题
if(currentMsg.sql) {
onSubmit({question:currentMsg.content,sql:currentMsg.sql}); // 调用原有发送方法
} else {
onSubmit(currentMsg.content); // 调用原有发送方法
}
}
};
// 复制回答内容
const handleFile = (index: number) => {
const currentMsg = messages.value[index];
if (currentMsg.role === 'ai-model' || currentMsg.role === 'system') {
const content = currentMsg.content || '';
// 处理富文本或特殊格式(示例中直接提取文本)
const textContent = typeof content === 'string' ? content : JSON.stringify(content);
// 执行复制操作
const textarea = document.createElement('textarea');
textarea.value = textContent;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
Message.success('内容已复制');
}
};
// 点赞/点踩处理
const handleLike = (index: number, type: 'like' | 'dislike') => {
const currentMsg = messages.value[index];
if (currentMsg.role === 'ai-model' || currentMsg.role === 'system') {
messages.value[index].islike = type;
// // 调用接口示例中需替换为实际API
// axios.post('/api/rateConversation', {
// conversationId: CONV_ID.value, // 会话ID
// messageId: currentMsg.id, // 消息ID
// type: type === 'like' ? 1 : 2 // 1=点赞2=点踩
// }).then(() => {
// Message.success(type === 'like' ? '点赞成功' : '点踩成功');
// }).catch(() => {
// messageReactions.value[currentMsg.id] = null; // 失败时回滚状态
// Message.error('操作失败');
// });
}
};
// 智能客服与更多操作(示例中暂留空逻辑,可根据需求扩展)
const handleCustomerService = () => {
Message.info('智能客服功能即将上线');
};
const handleMore = () => {
Message.info('更多操作功能待开发');
};
const VITE_AI_API_HOST = (import.meta as any).env.VITE_AI_API_HOST;
// AI对话接口对接
const getAIAnswer = async (content) => {
if(CONV_ID.value) {
let obj = {
role: 'ai-model',
type: '',
content: '',
avatarPosition: 'side-left',
avatarConfig: { ...aiModelAvatar },
loading: true,
question: content
}
if (content?.question) {
obj.question = content.question
if(content?.sql) {
obj.sql = content.sql
}
} else {
obj.question = content
}
messages.value.push(obj);
// const response = await axios.post(`${VITE_AI_API_HOST}/api/v0/chat_use_sql`, {
// userId: username, // 使用 username 作为 userId
// conversationId: CONV_ID.value,
// question: content,
// });
const {data} = await fetchChatUseSql({
userId: username, // 使用 username 作为 userId
conversationId: CONV_ID.value,
question: obj.question,
sql: obj?.sql || '',
});
if(data.code===200) {
const row = data.data
if(row.type) {
const {text,type,id,table_name=[], states={}} = row;
messages.value[messages.value.length - 1].id = id;
// 处理思考过程
const { thinkProcess,steps } = processThinkStates(states);
messages.value[messages.value.length - 1].thinkProcess = thinkProcess;
messages.value[messages.value.length - 1].steps = steps;
messages.value[messages.value.length - 1].thinkExpanded = true;
if(type==='text') {
messages.value[messages.value.length - 1].content = text;
messages.value[messages.value.length - 1].loading = false;
messages.value[messages.value.length - 1].type = 'text';
} else if(type==='df') {
messages.value[messages.value.length - 1].content = row[type];
messages.value[messages.value.length - 1].loading = false;
messages.value[messages.value.length - 1].type = 'df';
// 判断是否软件详情
if(table_name.length===1&&table_name[0]==='critical_software_info') {
let arr = JSON.parse(row[type]);
if(arr&&arr.length===1) {
messages.value[messages.value.length - 1].type = 'critical_software_info';
messages.value[messages.value.length - 1].id = arr[0]?.id || arr[0]?.ID;
messages.value[messages.value.length - 1].software_name = arr[0]?.software_name || arr[0]['软件名称'];
}
}
} else if(type==='sql_error') {
messages.value[messages.value.length - 1].content = row.error;
messages.value[messages.value.length - 1].loading = false;
messages.value[messages.value.length - 1].type = 'error';
}
fetchFollowupQuestions(content,id)
} else {
Message.warning('接口异常!')
}
// messages.value.push({
// from: 'ai-model',
// content: '',
// avatarPosition: 'side-left',
// avatarConfig: { ...aiModelAvatar },
// loading: true,
// });
// /* 模拟流式数据返回 */
// setTimeout(async () => {
// messages.value.at(-1).loading = false;
// for (let i = 0; i < content.length;) {
// await new Promise(r => setTimeout(r, 300 * Math.random()));
// messages.value[messages.value.length - 1].content = content.slice(0, i += Math.random() * 10);
// nextTick(() => {
// conversationRef.value?.scrollTo({
// top: conversationRef.value.scrollHeight
// });
// });
// }
// }, 1000);
}
} else {
// Message.error('请先创建会话!');
}
};
// 思考步骤定义
const thinkStepsOrder = ['意图分析', 'sql有效性判断', 'sql执行'];
// 处理思考状态
const processThinkStates = (states) => {
let arr = [];
let steps = []
let arStates = {...states}
// if(arStates.steps) {
// Object.keys(arStates.steps).map(i=>{
// arr.push({
// name: i,
// status: arStates.steps[i]
// })
// })
// delete arStates.steps
// }
Object.keys(arStates).map(i=>{
if(i==='steps') {
Object.keys(arStates[i]).map(j=>{
steps.push({
name: j,
status: arStates[i][j]
})
})
} else {
arr.push({
name: i,
status: arStates[i]
})
}
})
return {thinkProcess:arr,steps}
};
const viewHistoryRef = ref(null);
// 当前会话id
const CONV_ID = ref('');
// 创建对话接口
const createConversation = async () => {
try {
if (!username) {
Message.error('请先登录!');
return;
}
// const response = await axios.post(`${VITE_AI_API_HOST}/api/createConversation`, {
// userId: username,
// });
const {data} = await fetchCreateConversation({
userId: username,
});
if(data.code===200) {
CONV_ID.value = data.data.conversationId;
console.log('创建会话成功会话ID:', CONV_ID.value);
nextTick(() => {
// 刷新会话列表
if (viewHistoryRef.value) {
viewHistoryRef.value.getQuestionHistory(); // 调用子组件方法
}
})
} else {
Message.error(data.msg || '创建会话失败,请重试');
}
} catch (error) {
console.error('创建会话失败:', error);
Message.error(error || '创建会话失败,请重试');
}
};
// 猜你想问接口
const fetchFollowupQuestions = async (question,df_id,sql_id='') => {
debugger
try {
// 构造请求参数(需根据实际文档补充 df_id 和 sql_id示例中使用空字符串占位
let params = {
question,
sql:'',
df_id: df_id || '', // 使用当前会话ID若无则为空
sql_id: sql_id || '' // 同上
};
if (question?.question) {
params.question = question.question
if(question?.sql) {
params.sql = question?.sql || ''
}
}
// const response = await axios.post(
// `${VITE_AI_API_HOST}/api/v0/provide_followup_questions`,
// params
// );
const {data} = await FetchFollowupQuestions(params);
debugger
if(data.code===200) {
const createRes = data.data;
if (createRes) {
followupQuestions.value = createRes
} else {
// Message.error('创建会话失败未获取到会话ID');
}
} else {
// Message.error('创建会话失败未获取到会话ID');
}
} catch (error) {
console.error('查询相关问题失败:', error);
// Message.error('创建会话失败,请重试');
}
nextTick(() => {
conversationRef.value?.scrollTo({
top: conversationRef.value.scrollHeight,
behavior: 'smooth',
});
});
nextTick(() => {
// 刷新会话列表
if (viewHistoryRef.value) {
viewHistoryRef.value.getQuestionHistory(); // 调用子组件方法
}
})
};
// 刷新猜你想问
const handleResetQuestions = () => {
const lastItem = messages.value[messages.value.length - 1]
if(lastItem.role = 'system') {
fetchFollowupQuestions(lastItem.question,lastItem.id); // 调用详情接口
}
};
const handleSelectConv = (convId: string) => {
CONV_ID.value = convId; // 更新为选中的会话 ID
getConversationDetail(convId); // 调用详情接口
};
// 定义获取会话详情的接口(根据实际接口调整)
const getConversationDetail = async (convId: string) => {
try {
// const response = await axios.get(`${VITE_AI_API_HOST}/api/conversationDetail`, {
// params: {
// userId: username,
// conversationId: convId,
// },
// });
const response = await fetchConversationDetail({
userId: username,
conversationId: convId,
});
if(response.data.code!==200) {
// Message.error(response.data?.error);
} else {
debugger
const detail = response.data.data;
// 处理详情数据(如回显消息列表)
handleDetailResponse(detail);
}
} catch (error) {
console.error('获取会话详情失败:', error);
// Message.error('获取会话详情失败,请重试');
}
};
// 处理详情数据(示例:更新消息列表)
const handleDetailResponse = (detail: any) => {
// 假设接口返回消息列表为 messagesList
messages.value = detail.map(i=>{
let messages = i;
if(i.role=== 'user') {
return messages
} else {
let type = i.type;
let table_name = i.table_name;
if(type==='text') {
messages.content = i.text || i.content;
messages.loading = false;
messages.type = 'text';
} else if(type==='df') {
messages.content = i.content;
messages.loading = false;
messages.type = 'df';
// 判断是否软件详情
if(table_name?.[0]==='critical_software_info'&&table_name.length===1) {
let arr = JSON.parse(i.content);
if(arr&&arr.length===1) {
messages.type = 'critical_software_info';
messages.id = arr[0]?.id || arr[0]?.ID;
messages.software_name = arr[0]?.software_name || arr[0]['软件名称'];
}
}
}
return messages
}
})
startChat.value = true; // 显示聊天界面
nextTick(() => {
conversationRef.value?.scrollTo({
top: conversationRef.value.scrollHeight,
behavior: 'smooth',
});
});
};
const onNewConvo = () => {
startChat.value = false;
messages.value = [];
createConversation()
};
const onItemClick = (item) => {
if (mockAnswer[item.value]) {
// 使用 mock 数据
onSubmit(item.label, mockAnswer[item.value]);
}
};
const themeChange = () => {
if (themeService) {
theme.value = themeService.currentTheme.id === 'infinity-theme' ? 'light' : 'dark';
}
}
const isLoading = ref(false);
let interval = null;
const thinkBtnText = ref('思考过程');
const btnIcon = ref('icon-chevron-down-2');
const toggleThink = (idx) => {
if (isLoading.value) {
return
}
const targetNode = document.querySelectorAll('.mc-bubble-content-container')[idx];
if (targetNode) {
const thinkBlock = targetNode.querySelector('.mc-think-block');
if (thinkBlock) {
const currentDisplay = getComputedStyle(thinkBlock).display;
thinkBlock.style.display = currentDisplay === 'none' ? 'block' : 'none';
btnIcon.value = currentDisplay === 'none' ? 'icon-chevron-up-2' :'icon-chevron-down-2'
}
}
}
onMounted(() => {
if(typeof window !== 'undefined'){
themeService = window['devuiThemeService'];
}
themeChange();
if (themeService && themeService.eventBus) {
themeService.eventBus.add('themeChanged', themeChange);
}
});
</script>
<style scoped lang="scss">
.home-container {
width: 100%;
height: calc(100vh - 56px);
display: flex;
justify-content: center;
align-items: center;
position: relative;
overflow: hidden;
.home-content {
font-size: 14px;
padding-top: 3%;
padding-bottom: 1%;
height: 100%;
width: 60%;
display: flex;
flex-direction: column;
justify-self: center;
align-items: center;
gap: 20px;
}
.home-title {
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
gap: 20px;
h1 {
color: #191919;
font-family: Huawei Sans;
font-weight: bold;
font-size: 32px;
line-height: 48px;
letter-spacing: 0px;
text-align: center;
}
img {
width: 64px;
//filter: brightness(0);
}
}
.home-detail {
.home-detail-1 {
color: #191919;
font-family: HarmonyOS Sans SC;
font-weight: medium;
font-size: 24px;
line-height: 32px;
letter-spacing: 0px;
text-align: center;
margin-bottom: 24px;
}
.home-detail-2 {
color: #777777;
font-family: HarmonyOS Sans SC;
font-weight: regular;
font-size: 14px;
line-height: 22px;
letter-spacing: 0px;
text-align: center;
}
}
.conversation-area,
.welcome-page {
//height: 90%;
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
overflow: auto;
padding: 0 12px;
}
.conversation-area {
gap: 8px;
:deep(.mc-bubble-right) {
.mc-bubble-content {
background: #DFDFDF;
}
}
:deep(.mc-bubble-left) {
.mc-bubble-content-container {
position: relative;
background: transparent;
width: 100%;
.span1{
color: #777777;
font-family: HarmonyOS Sans SC;
font-weight: regular;
font-size: 14px;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
.mc-bubble-content.filled {
background: transparent;
}
.bubble-bottom-operations {
position: absolute;
right: 20px;
bottom: 20px;
display: flex;
gap: 8px; /* 图标之间的间隔 */
align-items: center; /* 垂直居中图标 */
justify-content: end;
font-size: 14px;
}
}
}
.conversation-cnxw {
margin-top: 8px;
display: flex;
flex-direction: column; /* 改为垂直排列 */
align-items: flex-end; /* 内容右对齐 */
.conversation-cnxw-item {
cursor: pointer;
align-items: center;
//justify-content: end;
//margin-right: 12px;
margin-bottom: 8px;
}
.span2{
color: black;
font-family: HarmonyOS Sans SC;
font-weight: regular;
font-size: 14px;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
margin-right: 8px;
}
.g-header-copilot {
margin-right: 16px;
margin-top: 4px;
background: #fff;
color: black;
padding: 0 12px;
border-radius: 48px;
height: 32px;
display: flex;
align-items: center;
cursor: pointer;
//justify-content: center;
&:hover {
background: rgba(41, 81, 224, 0.8);
}
}
.g-header-copilot:hover {
background: #f0f0f0;
}
}
}
.welcome-page {
gap: 16px;
justify-content: center;
}
.guess-question {
width: 100%;
padding: 16px 12px;
border-radius: var(--devui-border-radius-card);
background-color: var(--devui-gray-form-control-bg);
.guess-title {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--devui-text);
margin-bottom: 12px;
& > div:first-child {
font-weight: 700;
font-size: var(--devui-font-size);
}
& > div:last-child {
font-size: var(--devui-font-size-sm);
cursor: pointer;
span {
margin-left: 4px;
}
}
}
.guess-content {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
span {
font-size: var(--devui-font-size-sm);
color: var(--devui-text);
padding: 4px 12px;
border-radius: var(--devui-border-radius-full);
background-color: var(--devui-gray-form-control-hover-bg);
cursor: pointer;
}
}
}
.bubble-bottom-operations {
margin-top: 8px;
i {
padding: 4px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: var(--devui-icon-hover-bg);
}
}
}
.new-convo-button {
width: 100%;
padding: 0 12px;
display: flex;
justify-content: flex-end;
align-items: center;
height: 39px;
gap: 4px;
}
:deep(.simple-prompt .mc-list) {
justify-content: unset;
}
:deep(.intro-prompt .mc-list) {
justify-content: center;
}
.operations {
i {
padding: 4px;
border-radius: 4px;
cursor: pointer;
&:hover {
background: var(--devui-global-bg);
}
}
}
.agent-knowledge {
flex: 1;
display: flex;
align-items: center;
.agent-wrapper {
display: flex;
align-items: center;
padding: 4px 8px;
border-radius: var(--devui-border-radius-full);
background-color: var(--devui-area);
cursor: pointer;
img {
width: 16px;
height: 16px;
margin-right: 4px;
}
span {
font-size: var(--devui-font-size);
color: var(--devui-text);
margin-right: 8px;
}
i {
font-size: var(--devui-font-size);
color: var(--devui-text);
transition: transform 0.3s ease-in-out;
&:last-child {
margin-left: 4px;
}
}
.is-open {
transform: rotate(180deg);
}
}
.agent-knowledge-dividing-line {
width: 1px;
height: 14px;
margin: 0 12px;
background-color: var(--devui-line);
}
.knowledge-wrapper {
font-size: var(--devui-font-size);
color: var(--devui-text);
cursor: pointer;
span {
margin-left: 4px;
}
}
}
.input-foot-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 100%;
margin-right: 8px;
.input-foot-left {
display: flex;
align-items: center;
gap: 8px;
span {
font-size: var(--devui-font-size-sm);
color: var(--devui-text);
cursor: pointer;
}
.input-foot-dividing-line {
width: 1px;
height: 14px;
background-color: var(--devui-line);
}
.input-foot-maxlength {
font-size: var(--devui-font-size-sm);
color: var(--devui-aide-text);
}
}
.input-foot-right {
& > *:not(:first-child) {
margin-left: 8px;
}
}
}
:deep(.mc-input) {
background: #ffffff;
.mc-textarea {
height: 100px;
}
}
}
.usage-terms {
position: absolute;
bottom: 0;
color: #AEAEAE;
font-family: HarmonyOS Sans SC;
font-weight: regular;
font-size: 12px;
line-height: 20px;
letter-spacing: 0px;
text-align: left;
}
.back-in-btn {
position: fixed;
left: 20px;
bottom: 20px;
display: flex;
gap: 12px;
.active {
background-color: #f0f0f0; /* 灰色背景 */
}
.active .icon {
transform: translateY(-4px); /* 图标上移4px */
}
.back-in-time-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
background-color: var(--el-color-primary);
//color: white;
border-radius: 20px;
cursor: pointer;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 999;
transition: all 0.3s;
font-size: 18px;
}
.back-in-time-btn:hover {
background-color: var(--el-color-primary-light-3);
transform: translateY(-2px);
}
/* 响应式调整 - 移动端隐藏或调整位置 */
@media (max-width: 768px) {
.back-in-time-btn {
left: 10px;
bottom: 10px;
padding: 6px 12px;
font-size: 14px;
}
}
}
.icon-dislike-solid {
transform: scaleX(-1) rotate(180deg);
}
</style>