2025-05-06 18:07:15 +08:00
|
|
|
|
<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">
|
2025-05-15 15:29:01 +08:00
|
|
|
|
<McBubble v-if="msg.role === 'user'" :content="msg.content" :align="'right'" ></McBubble>
|
2025-05-08 11:25:57 +08:00
|
|
|
|
<McBubble v-else :loading="msg.loading ?? false" >
|
|
|
|
|
|
<div class="think-toggle-btn" @click="toggleThink(idx)">
|
2025-06-18 15:37:36 +08:00
|
|
|
|
<div class="flex align-center">
|
2025-05-08 11:25:57 +08:00
|
|
|
|
<img style="height: 15px;margin-right: 8px" width="16" src="@/assets/imgs/jyh/ai/icon_skgc.png" />
|
|
|
|
|
|
<span class="span1">思考过程:</span>
|
2025-06-18 15:37:36 +08:00
|
|
|
|
<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>
|
2025-06-25 14:59:13 +08:00
|
|
|
|
<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>
|
2025-06-18 15:37:36 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</div>
|
2025-05-08 11:25:57 +08:00
|
|
|
|
</div>
|
2025-06-18 15:37:36 +08:00
|
|
|
|
<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>
|
2025-05-08 11:25:57 +08:00
|
|
|
|
<span class="span1">完成回答:</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!--开源软件列表-->
|
2025-05-08 15:27:41 +08:00
|
|
|
|
<OssList v-if="msg.type===1"/>
|
2025-05-09 14:13:08 +08:00
|
|
|
|
<!--预警列表-->
|
2025-05-08 15:27:41 +08:00
|
|
|
|
<WarningList v-else-if="msg.type===2"/>
|
2025-05-09 14:13:08 +08:00
|
|
|
|
<!--软件详情-->
|
2025-05-26 17:04:58 +08:00
|
|
|
|
<OssDetail :id="msg.id" :software_name="msg.software_name||''" v-else-if="msg.type==='critical_software_info'"/>
|
|
|
|
|
|
<!--列表展示-->
|
2025-05-13 17:59:24 +08:00
|
|
|
|
<AIList v-else-if="msg.type==='df'" :content="msg.content"/>
|
|
|
|
|
|
<!--text或者富文本展示-->
|
|
|
|
|
|
<Other v-else :content="msg.content"></Other>
|
2025-06-06 15:48:09 +08:00
|
|
|
|
|
|
|
|
|
|
<div class="bubble-bottom-operations">
|
2025-07-01 17:03:24 +08:00
|
|
|
|
<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>
|
2025-06-06 15:48:09 +08:00
|
|
|
|
<i :class="msg.islike==='like'?'icon-like-solid':'icon-like'" title="点赞" @click="() => handleLike(idx, 'like')" ></i>
|
2025-07-01 17:03:24 +08:00
|
|
|
|
<i :class="msg.islike==='dislike'?'icon-like-solid icon-dislike-solid':'icon-dislike'" title="踩" @click="() => handleLike(idx, 'dislike')" ></i>
|
2025-06-06 15:48:09 +08:00
|
|
|
|
<!-- <i class="icon-clever-customer" title="智能客服" onclick="handleCustomerService()"></i>-->
|
|
|
|
|
|
<!-- <i class="icon-more-operate" title="更多操作" onclick="handleMore()"></i>-->
|
|
|
|
|
|
</div>
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</McBubble>
|
2025-05-08 11:25:57 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- <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>-->
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</template>
|
2025-05-21 11:33:13 +08:00
|
|
|
|
<div v-if="followupQuestions.length>0" class="conversation-cnxw">
|
2025-05-23 10:32:39 +08:00
|
|
|
|
<a @click="handleResetQuestions" class="flex conversation-cnxw-item">
|
2025-05-13 17:59:24 +08:00
|
|
|
|
<span class="span2">猜你想问</span>
|
2025-05-21 11:33:13 +08:00
|
|
|
|
<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" />
|
2025-07-03 16:49:40 +08:00
|
|
|
|
<span class="ml-xs">{{ q.question }}</span>
|
2025-05-13 17:59:24 +08:00
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else class="welcome-page">
|
|
|
|
|
|
<div class="home-title">
|
|
|
|
|
|
<img src="@/assets/imgs/jyh/ai/aiLogo2.png" alt="logo" />
|
|
|
|
|
|
<h1>可信开源代码库AI助手</h1>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="home-detail">
|
|
|
|
|
|
<div class="home-detail-1">Hi,欢迎使用可信开源代码库AI助手</div>
|
|
|
|
|
|
<div class="home-detail-2">可信开源代码库AI助手 可以辅助研发人员进行软件查询、批量校验、查看软件详情等。</div>
|
|
|
|
|
|
<div class="home-detail-2">作为AI模型,可信开源代码库AI助手 提供的答案可能不总是确定或准确的,但您的反馈可以帮助 可信开源代码库AI助手 做得更好。</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="width: 100%;margin-top: -20px">
|
|
|
|
|
|
<SwiperComponent />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!--猜你想问-->
|
|
|
|
|
|
<div style="width: 100%;margin-top: -20px">
|
2025-05-08 11:25:57 +08:00
|
|
|
|
<GuessYouWantToAsk @submit="onSubmit"/>
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!--操作按钮-->
|
|
|
|
|
|
<div class="new-convo-button">
|
2025-05-08 11:25:57 +08:00
|
|
|
|
<!-- <McPrompt-->
|
|
|
|
|
|
<!-- v-if="startChat"-->
|
|
|
|
|
|
<!-- class="simple-prompt"-->
|
|
|
|
|
|
<!-- style="flex: 1"-->
|
|
|
|
|
|
<!-- :list="simplePrompt"-->
|
|
|
|
|
|
<!-- :direction="'horizontal'"-->
|
|
|
|
|
|
<!-- @itemClick="onItemClick($event)"-->
|
|
|
|
|
|
<!-- ></McPrompt>-->
|
2025-05-23 10:40:01 +08:00
|
|
|
|
<!-- <div class="agent-knowledge">-->
|
|
|
|
|
|
<!-- <d-dropdown :position="['top']" @toggle="(val) => (isAgentOpen = val)">-->
|
|
|
|
|
|
<!-- <div class="agent-wrapper">-->
|
|
|
|
|
|
<!-- <img src="@/assets/imgs/jyh/ai/aiLogo2.png" />-->
|
|
|
|
|
|
<!-- <span>{{ selectedAgent.label }}</span>-->
|
|
|
|
|
|
<!-- <i class="icon-infrastructure"></i>-->
|
|
|
|
|
|
<!-- <i :class="['icon-chevron-down-2', { 'is-open': isAgentOpen }]"></i>-->
|
|
|
|
|
|
<!-- </div>-->
|
|
|
|
|
|
<!-- <template #menu>-->
|
|
|
|
|
|
<!-- <McList :data="agentList" @select="(val) => (selectedAgent = val)"></McList>-->
|
|
|
|
|
|
<!-- </template>-->
|
|
|
|
|
|
<!-- </d-dropdown>-->
|
|
|
|
|
|
<!-- <span class="agent-knowledge-dividing-line"></span>-->
|
|
|
|
|
|
<!-- <div class="knowledge-wrapper">-->
|
|
|
|
|
|
<!-- <i class="icon-operation-log"></i>-->
|
|
|
|
|
|
<!-- <span>添加知识</span>-->
|
|
|
|
|
|
<!-- </div>-->
|
|
|
|
|
|
<!-- </div>-->
|
2025-05-06 18:07:15 +08:00
|
|
|
|
<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">
|
2025-05-23 10:40:01 +08:00
|
|
|
|
<!-- <span v-for="(item, index) in inputFootIcons" :key="index" @click="() => onInputIconClick(item)">-->
|
|
|
|
|
|
<!-- <i :class="item.icon"></i>-->
|
|
|
|
|
|
<!-- {{ item.text }}-->
|
|
|
|
|
|
<!-- </span>-->
|
|
|
|
|
|
<!-- <span class="input-foot-dividing-line"></span>-->
|
2025-05-06 18:07:15 +08:00
|
|
|
|
<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>
|
2025-05-15 15:29:01 +08:00
|
|
|
|
<div class="back-in-btn">
|
|
|
|
|
|
<div class="back-in-time-btn" @click="toggleHistory" :class="{ 'active': isHistoryVisible }">
|
|
|
|
|
|
<i class="icon icon-history" title="查看历史"></i>
|
|
|
|
|
|
</div>
|
2025-05-23 10:40:01 +08:00
|
|
|
|
<!-- <div class="back-in-time-btn" @click="handleBackInTime">-->
|
|
|
|
|
|
<!-- <i class="icon icon-operation-log" title="查看文档"></i>-->
|
|
|
|
|
|
<!-- </div>-->
|
2025-05-15 15:29:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<ViewHistoryAside ref="viewHistoryRef" :CONV_ID="CONV_ID" :username="username" v-show="isHistoryVisible" @select-conv="handleSelectConv"/>
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</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';
|
2025-05-08 11:25:57 +08:00
|
|
|
|
import OssList from './components/OssList.vue';
|
2025-05-08 15:27:41 +08:00
|
|
|
|
import WarningList from './components/WarningList.vue';
|
2025-05-09 14:13:08 +08:00
|
|
|
|
import OssDetail from './components/OssDetail.vue';
|
2025-05-13 17:59:24 +08:00
|
|
|
|
import Other from './components/Other.vue';
|
|
|
|
|
|
import AIList from './components/AIList.vue';
|
2025-05-15 15:29:01 +08:00
|
|
|
|
import ViewHistoryAside from './components/ViewHistoryAside/ViewHistoryAside.vue';
|
2025-05-13 17:59:24 +08:00
|
|
|
|
import axios from 'axios';
|
|
|
|
|
|
import { useDiscussGetUserInfo } from '@/api/discussion/hook';
|
2025-05-15 15:29:01 +08:00
|
|
|
|
import { Message } from 'vue-devui/message';
|
2025-06-24 17:35:03 +08:00
|
|
|
|
import { fetchChatUseSql, fetchConversationDetail, fetchCreateConversation, FetchFollowupQuestions } from '@/api/jyh';
|
2025-05-13 17:59:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取当前用户信息 & 是否登录
|
|
|
|
|
|
const { isLogin = false, userInfo = {}} = useDiscussGetUserInfo();
|
2025-05-15 15:29:01 +08:00
|
|
|
|
const {username=''} = userInfo
|
2025-05-06 18:07:15 +08:00
|
|
|
|
|
|
|
|
|
|
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]);
|
2025-05-15 15:29:01 +08:00
|
|
|
|
|
2025-05-06 18:07:15 +08:00
|
|
|
|
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([]);
|
2025-05-21 11:33:13 +08:00
|
|
|
|
const followupQuestions = ref([]);
|
2025-05-06 18:07:15 +08:00
|
|
|
|
|
2025-05-15 15:29:01 +08:00
|
|
|
|
// 控制 ViewHistoryAside 组件的显示状态
|
|
|
|
|
|
const isHistoryVisible = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 切换 ViewHistoryAside 组件的显示与隐藏
|
|
|
|
|
|
const toggleHistory = () => {
|
|
|
|
|
|
isHistoryVisible.value =!isHistoryVisible.value;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-05-06 18:07:15 +08:00
|
|
|
|
const onInputIconClick = (e) => {
|
|
|
|
|
|
if (e.icon === 'icon-at') {
|
|
|
|
|
|
inputValue.value += `@${selectedAgent.value.label}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-05-15 15:29:01 +08:00
|
|
|
|
const onSubmit = async (e, answer = undefined) => {
|
2025-07-03 16:49:40 +08:00
|
|
|
|
let obj = {
|
|
|
|
|
|
role: 'user',
|
|
|
|
|
|
avatarPosition: 'side-right',
|
|
|
|
|
|
}
|
2025-05-06 18:07:15 +08:00
|
|
|
|
if(e === '') {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-03 16:49:40 +08:00
|
|
|
|
if (e?.sql) {
|
|
|
|
|
|
obj.content = e.question
|
|
|
|
|
|
obj.sql = e.sql
|
|
|
|
|
|
} else {
|
|
|
|
|
|
obj.content = e
|
|
|
|
|
|
}
|
2025-05-06 18:07:15 +08:00
|
|
|
|
inputValue.value = '';
|
|
|
|
|
|
if (!messages.value.length) {
|
|
|
|
|
|
startChat.value = true;
|
|
|
|
|
|
}
|
2025-07-03 16:49:40 +08:00
|
|
|
|
messages.value.push(obj);
|
2025-05-21 11:33:13 +08:00
|
|
|
|
followupQuestions.value = [];
|
2025-05-15 15:29:01 +08:00
|
|
|
|
// getAIAnswer(answer ?? e);
|
|
|
|
|
|
// 判断是否有会话id
|
|
|
|
|
|
if(!CONV_ID.value) {
|
|
|
|
|
|
// 调用创建会话接口
|
|
|
|
|
|
await createConversation()
|
|
|
|
|
|
}
|
|
|
|
|
|
await getAIAnswer(e,1);
|
2025-05-06 18:07:15 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
conversationRef.value?.scrollTo({
|
|
|
|
|
|
top: conversationRef.value.scrollHeight,
|
|
|
|
|
|
behavior: 'smooth',
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-06-06 15:48:09 +08:00
|
|
|
|
// 刷新回答(重新发送上一个用户问题)
|
|
|
|
|
|
const handleRefresh = (index: number) => {
|
2025-07-01 17:03:24 +08:00
|
|
|
|
const currentMsg = messages.value[index-1];
|
|
|
|
|
|
if (currentMsg.role === 'user' && currentMsg.content) { // 假设AI回答的question字段存储了用户原始问题
|
2025-07-03 16:49:40 +08:00
|
|
|
|
if(currentMsg.sql) {
|
|
|
|
|
|
onSubmit({question:currentMsg.content,sql:currentMsg.sql}); // 调用原有发送方法
|
|
|
|
|
|
} else {
|
|
|
|
|
|
onSubmit(currentMsg.content); // 调用原有发送方法
|
|
|
|
|
|
}
|
2025-06-06 15:48:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 复制回答内容
|
|
|
|
|
|
const handleFile = (index: number) => {
|
|
|
|
|
|
const currentMsg = messages.value[index];
|
2025-07-01 17:03:24 +08:00
|
|
|
|
if (currentMsg.role === 'ai-model' || currentMsg.role === 'system') {
|
2025-06-06 15:48:09 +08:00
|
|
|
|
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];
|
2025-07-01 17:03:24 +08:00
|
|
|
|
if (currentMsg.role === 'ai-model' || currentMsg.role === 'system') {
|
|
|
|
|
|
messages.value[index].islike = type;
|
2025-06-06 15:48:09 +08:00
|
|
|
|
|
|
|
|
|
|
// // 调用接口(示例中需替换为实际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('更多操作功能待开发');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-05-13 18:06:07 +08:00
|
|
|
|
const VITE_AI_API_HOST = (import.meta as any).env.VITE_AI_API_HOST;
|
2025-05-15 15:29:01 +08:00
|
|
|
|
|
|
|
|
|
|
// AI对话接口对接
|
2025-05-13 17:59:24 +08:00
|
|
|
|
const getAIAnswer = async (content) => {
|
2025-05-15 15:29:01 +08:00
|
|
|
|
if(CONV_ID.value) {
|
2025-07-03 16:49:40 +08:00
|
|
|
|
let obj = {
|
2025-05-15 15:29:01 +08:00
|
|
|
|
role: 'ai-model',
|
|
|
|
|
|
type: '',
|
|
|
|
|
|
content: '',
|
|
|
|
|
|
avatarPosition: 'side-left',
|
|
|
|
|
|
avatarConfig: { ...aiModelAvatar },
|
|
|
|
|
|
loading: true,
|
2025-05-23 10:32:39 +08:00
|
|
|
|
question: content
|
2025-07-03 16:49:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (content.sql) {
|
|
|
|
|
|
obj.question = content.question
|
|
|
|
|
|
obj.sql = content.sql
|
|
|
|
|
|
} else {
|
|
|
|
|
|
obj.question = content
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
messages.value.push(obj);
|
2025-05-06 18:07:15 +08:00
|
|
|
|
|
2025-06-24 17:35:03 +08:00
|
|
|
|
// const response = await axios.post(`${VITE_AI_API_HOST}/api/v0/chat_use_sql`, {
|
|
|
|
|
|
// userId: username, // 使用 username 作为 userId
|
|
|
|
|
|
// conversationId: CONV_ID.value,
|
|
|
|
|
|
// question: content,
|
|
|
|
|
|
// });
|
2025-06-27 11:22:37 +08:00
|
|
|
|
const {data} = await fetchChatUseSql({
|
2025-05-15 15:29:01 +08:00
|
|
|
|
userId: username, // 使用 username 作为 userId
|
|
|
|
|
|
conversationId: CONV_ID.value,
|
2025-07-03 16:49:40 +08:00
|
|
|
|
question: obj.question,
|
|
|
|
|
|
sql: obj?.sql || '',
|
2025-05-15 15:29:01 +08:00
|
|
|
|
});
|
2025-06-27 11:22:37 +08:00
|
|
|
|
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]['软件名称'];
|
|
|
|
|
|
}
|
2025-05-26 17:04:58 +08:00
|
|
|
|
}
|
2025-06-27 11:22:37 +08:00
|
|
|
|
} 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';
|
2025-05-26 17:04:58 +08:00
|
|
|
|
}
|
2025-06-27 11:22:37 +08:00
|
|
|
|
fetchFollowupQuestions(content,id)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Message.warning('接口异常!')
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
2025-06-27 11:22:37 +08:00
|
|
|
|
// 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);
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-05-15 17:02:39 +08:00
|
|
|
|
// Message.error('请先创建会话!');
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-06-18 15:37:36 +08:00
|
|
|
|
// 思考步骤定义
|
|
|
|
|
|
const thinkStepsOrder = ['意图分析', 'sql有效性判断', 'sql执行'];
|
|
|
|
|
|
// 处理思考状态
|
|
|
|
|
|
const processThinkStates = (states) => {
|
2025-06-25 14:59:13 +08:00
|
|
|
|
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}
|
2025-06-18 15:37:36 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-05-15 15:29:01 +08:00
|
|
|
|
const viewHistoryRef = ref(null);
|
|
|
|
|
|
// 当前会话id
|
|
|
|
|
|
const CONV_ID = ref('');
|
|
|
|
|
|
// 创建对话接口
|
|
|
|
|
|
const createConversation = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!username) {
|
|
|
|
|
|
Message.error('请先登录!');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-24 17:35:03 +08:00
|
|
|
|
// const response = await axios.post(`${VITE_AI_API_HOST}/api/createConversation`, {
|
|
|
|
|
|
// userId: username,
|
|
|
|
|
|
// });
|
2025-06-27 11:22:37 +08:00
|
|
|
|
const {data} = await fetchCreateConversation({
|
2025-05-15 15:29:01 +08:00
|
|
|
|
userId: username,
|
|
|
|
|
|
});
|
2025-06-27 11:22:37 +08:00
|
|
|
|
if(data.code===200) {
|
|
|
|
|
|
CONV_ID.value = data.data.conversationId;
|
2025-05-15 15:29:01 +08:00
|
|
|
|
console.log('创建会话成功,会话ID:', CONV_ID.value);
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
// 刷新会话列表
|
|
|
|
|
|
if (viewHistoryRef.value) {
|
2025-06-27 11:22:37 +08:00
|
|
|
|
viewHistoryRef.value.getQuestionHistory(); // 调用子组件方法
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
2025-06-26 18:19:54 +08:00
|
|
|
|
// Message.error('创建会话失败,未获取到会话ID');
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('创建会话失败:', error);
|
2025-06-26 18:19:54 +08:00
|
|
|
|
// Message.error('创建会话失败,请重试');
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-05-21 11:33:13 +08:00
|
|
|
|
// 猜你想问接口
|
|
|
|
|
|
const fetchFollowupQuestions = async (question,df_id,sql_id='') => {
|
2025-07-03 16:49:40 +08:00
|
|
|
|
debugger
|
2025-05-21 11:33:13 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// 构造请求参数(需根据实际文档补充 df_id 和 sql_id,示例中使用空字符串占位)
|
2025-07-03 16:49:40 +08:00
|
|
|
|
let params = {
|
2025-05-21 11:33:13 +08:00
|
|
|
|
question,
|
|
|
|
|
|
df_id: df_id || '', // 使用当前会话ID,若无则为空
|
|
|
|
|
|
sql_id: sql_id || '' // 同上
|
|
|
|
|
|
};
|
2025-07-03 16:49:40 +08:00
|
|
|
|
if (question?.sql) {
|
|
|
|
|
|
params.question = question.question
|
|
|
|
|
|
}
|
2025-06-24 17:35:03 +08:00
|
|
|
|
// const response = await axios.post(
|
|
|
|
|
|
// `${VITE_AI_API_HOST}/api/v0/provide_followup_questions`,
|
|
|
|
|
|
// params
|
|
|
|
|
|
// );
|
2025-06-27 11:22:37 +08:00
|
|
|
|
const {data} = await FetchFollowupQuestions(params);
|
2025-07-03 16:49:40 +08:00
|
|
|
|
debugger
|
2025-06-27 11:22:37 +08:00
|
|
|
|
if(data.code===200) {
|
|
|
|
|
|
const createRes = data.data;
|
2025-07-03 16:49:40 +08:00
|
|
|
|
if (createRes) {
|
|
|
|
|
|
followupQuestions.value = createRes
|
2025-06-27 11:22:37 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// Message.error('创建会话失败,未获取到会话ID');
|
|
|
|
|
|
}
|
2025-05-21 11:33:13 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// Message.error('创建会话失败,未获取到会话ID');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('查询相关问题失败:', error);
|
|
|
|
|
|
// Message.error('创建会话失败,请重试');
|
|
|
|
|
|
}
|
2025-05-21 15:11:27 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
conversationRef.value?.scrollTo({
|
|
|
|
|
|
top: conversationRef.value.scrollHeight,
|
|
|
|
|
|
behavior: 'smooth',
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-06-13 10:59:56 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
// 刷新会话列表
|
|
|
|
|
|
if (viewHistoryRef.value) {
|
|
|
|
|
|
viewHistoryRef.value.getQuestionHistory(); // 调用子组件方法
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-05-21 11:33:13 +08:00
|
|
|
|
};
|
2025-05-15 15:29:01 +08:00
|
|
|
|
|
2025-05-23 10:32:39 +08:00
|
|
|
|
// 刷新猜你想问
|
|
|
|
|
|
const handleResetQuestions = () => {
|
|
|
|
|
|
const lastItem = messages.value[messages.value.length - 1]
|
|
|
|
|
|
if(lastItem.role = 'system') {
|
|
|
|
|
|
fetchFollowupQuestions(lastItem.question,lastItem.id); // 调用详情接口
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-05-15 15:29:01 +08:00
|
|
|
|
const handleSelectConv = (convId: string) => {
|
|
|
|
|
|
CONV_ID.value = convId; // 更新为选中的会话 ID
|
|
|
|
|
|
getConversationDetail(convId); // 调用详情接口
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 定义获取会话详情的接口(根据实际接口调整)
|
|
|
|
|
|
const getConversationDetail = async (convId: string) => {
|
|
|
|
|
|
try {
|
2025-06-24 17:35:03 +08:00
|
|
|
|
// const response = await axios.get(`${VITE_AI_API_HOST}/api/conversationDetail`, {
|
|
|
|
|
|
// params: {
|
|
|
|
|
|
// userId: username,
|
|
|
|
|
|
// conversationId: convId,
|
|
|
|
|
|
// },
|
|
|
|
|
|
// });
|
|
|
|
|
|
const response = await fetchConversationDetail({
|
|
|
|
|
|
userId: username,
|
|
|
|
|
|
conversationId: convId,
|
2025-05-15 15:29:01 +08:00
|
|
|
|
});
|
2025-06-27 11:22:37 +08:00
|
|
|
|
if(response.data.code!==200) {
|
2025-06-26 18:19:54 +08:00
|
|
|
|
// Message.error(response.data?.error);
|
2025-06-24 17:35:03 +08:00
|
|
|
|
} else {
|
2025-06-27 11:22:37 +08:00
|
|
|
|
const detail = response.data.data;
|
2025-06-24 17:35:03 +08:00
|
|
|
|
// 处理详情数据(如回显消息列表)
|
|
|
|
|
|
handleDetailResponse(detail);
|
|
|
|
|
|
}
|
2025-05-15 15:29:01 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取会话详情失败:', error);
|
2025-06-26 18:19:54 +08:00
|
|
|
|
// Message.error('获取会话详情失败,请重试');
|
2025-05-15 15:29:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理详情数据(示例:更新消息列表)
|
|
|
|
|
|
const handleDetailResponse = (detail: any) => {
|
|
|
|
|
|
// 假设接口返回消息列表为 messagesList
|
2025-06-06 14:26:28 +08:00
|
|
|
|
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') {
|
2025-06-27 11:22:37 +08:00
|
|
|
|
messages.content = i.text || i.content;
|
2025-06-06 14:26:28 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-05-15 15:29:01 +08:00
|
|
|
|
startChat.value = true; // 显示聊天界面
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
conversationRef.value?.scrollTo({
|
|
|
|
|
|
top: conversationRef.value.scrollHeight,
|
|
|
|
|
|
behavior: 'smooth',
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-05-06 18:07:15 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const onNewConvo = () => {
|
|
|
|
|
|
startChat.value = false;
|
|
|
|
|
|
messages.value = [];
|
2025-05-15 15:29:01 +08:00
|
|
|
|
createConversation()
|
2025-05-06 18:07:15 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-08 11:25:57 +08:00
|
|
|
|
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'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 18:07:15 +08:00
|
|
|
|
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%;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
height: calc(100vh - 56px);
|
2025-05-06 18:07:15 +08:00
|
|
|
|
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%;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
width: 60%;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
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;
|
2025-05-12 10:23:52 +08:00
|
|
|
|
font-size: 32px;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
line-height: 48px;
|
|
|
|
|
|
letter-spacing: 0px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
img {
|
2025-05-12 10:23:52 +08:00
|
|
|
|
width: 64px;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
//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 {
|
2025-05-08 11:25:57 +08:00
|
|
|
|
//height: 90%;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
overflow: auto;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
padding: 0 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.conversation-area {
|
|
|
|
|
|
gap: 8px;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
:deep(.mc-bubble-right) {
|
|
|
|
|
|
.mc-bubble-content {
|
|
|
|
|
|
background: #DFDFDF;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.mc-bubble-left) {
|
|
|
|
|
|
.mc-bubble-content-container {
|
2025-06-06 15:48:09 +08:00
|
|
|
|
position: relative;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-06-06 15:48:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.bubble-bottom-operations {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 20px;
|
|
|
|
|
|
bottom: 20px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 8px; /* 图标之间的间隔 */
|
|
|
|
|
|
align-items: center; /* 垂直居中图标 */
|
|
|
|
|
|
justify-content: end;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
2025-05-08 11:25:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.conversation-cnxw {
|
|
|
|
|
|
margin-top: 8px;
|
2025-05-21 11:33:13 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column; /* 改为垂直排列 */
|
|
|
|
|
|
align-items: flex-end; /* 内容右对齐 */
|
2025-05-08 11:25:57 +08:00
|
|
|
|
.conversation-cnxw-item {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
align-items: center;
|
2025-05-21 11:33:13 +08:00
|
|
|
|
//justify-content: end;
|
|
|
|
|
|
//margin-right: 12px;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
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 {
|
2025-05-21 11:33:13 +08:00
|
|
|
|
margin-right: 16px;
|
|
|
|
|
|
margin-top: 4px;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
color: black;
|
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
|
border-radius: 48px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
cursor: pointer;
|
2025-05-21 11:33:13 +08:00
|
|
|
|
//justify-content: center;
|
2025-05-08 11:25:57 +08:00
|
|
|
|
&:hover {
|
|
|
|
|
|
background: rgba(41, 81, 224, 0.8);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.g-header-copilot:hover {
|
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-06 18:07:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.welcome-page {
|
2025-05-12 10:23:52 +08:00
|
|
|
|
gap: 16px;
|
2025-05-06 18:07:15 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-05-15 15:29:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-01 17:03:24 +08:00
|
|
|
|
|
|
|
|
|
|
.icon-dislike-solid {
|
|
|
|
|
|
transform: scaleX(-1) rotate(180deg);
|
|
|
|
|
|
}
|
2025-05-06 18:07:15 +08:00
|
|
|
|
</style>
|