|
|
@@ -1,30 +1,57 @@
|
|
|
<template>
|
|
|
- <main class="ai-main">
|
|
|
+ <main class="ai-main ai-chat-shell">
|
|
|
+ <!-- 对话头:工作台内没有外层标题栏,这里补会话名与状态;传统分析页有顶部条,默认不显示 -->
|
|
|
+ <header v-if="showHeader" class="ai-chat__head">
|
|
|
+ <div class="ai-chat__head-inner">
|
|
|
+ <span class="ai-chat__head-icon">
|
|
|
+ <Icon icon="mdi:message-text-outline" :size="18" />
|
|
|
+ </span>
|
|
|
+ <div class="ai-chat__head-main">
|
|
|
+ <div class="ai-chat__head-title">{{ hasConversation ? title || '当前对话' : '新对话' }}</div>
|
|
|
+ <div class="ai-chat__head-sub">
|
|
|
+ {{ hasConversation ? `共 ${messages.length} 条消息` : '描述分析目标,AI 会检索案件数据并执行只读查询' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span v-if="streaming" class="ai-chat__head-status">
|
|
|
+ <i class="ai-chat__head-dot"></i>
|
|
|
+ 生成中
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+
|
|
|
<!-- 未打开案件 -->
|
|
|
<div v-if="disabled" class="ai-empty-tip ai-chat__center">
|
|
|
请先打开一个案件,AI 数据分析将基于当前案件的数据空间工作。
|
|
|
</div>
|
|
|
|
|
|
<!-- 空态:用法引导 -->
|
|
|
- <div v-else-if="!hasConversation" class="ai-scroll">
|
|
|
+ <div v-else-if="!hasConversation" class="ai-scroll ai-chat__welcome-scroll">
|
|
|
<div class="ai-column ai-chat__welcome">
|
|
|
+ <span class="ai-chat__welcome-icon">
|
|
|
+ <Icon icon="mdi:sparkles" :size="22" />
|
|
|
+ </span>
|
|
|
<h1 class="ai-chat__welcome-title">用自然语言完成一次数据研判</h1>
|
|
|
<p class="ai-chat__welcome-desc">
|
|
|
AI 会在当前案件的数据空间中检索表结构、编写并执行只读 SQL,把结果整理成可翻页的数据表、图表与关系图谱,
|
|
|
并给出分析结论。所有取数过程都可展开核对。
|
|
|
</p>
|
|
|
|
|
|
- <ol class="ai-chat__steps">
|
|
|
- <li v-for="(item, idx) in EXAMPLES" :key="item.text" class="ai-chat__step">
|
|
|
- <span class="ai-chat__step-index">{{ idx + 1 }}</span>
|
|
|
- <div class="ai-chat__step-body">
|
|
|
- <div class="ai-chat__step-title">{{ item.title }}</div>
|
|
|
- <button class="ai-chat__step-prompt" type="button" @click="fill(item.text)">
|
|
|
- {{ item.text }}
|
|
|
- </button>
|
|
|
+ <div class="ai-chat__examples">
|
|
|
+ <button
|
|
|
+ v-for="(item, idx) in EXAMPLES"
|
|
|
+ :key="item.text"
|
|
|
+ class="ai-chat__example"
|
|
|
+ type="button"
|
|
|
+ @click="fill(item.text)"
|
|
|
+ >
|
|
|
+ <div class="ai-chat__example-top">
|
|
|
+ <span class="ai-chat__example-index">{{ String(idx + 1).padStart(2, '0') }}</span>
|
|
|
+ <span class="ai-chat__example-title">{{ item.title }}</span>
|
|
|
+ <Icon class="ai-chat__example-arrow" icon="mdi:arrow-right" :size="15" />
|
|
|
</div>
|
|
|
- </li>
|
|
|
- </ol>
|
|
|
+ <p class="ai-chat__example-text">{{ item.text }}</p>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -103,6 +130,8 @@
|
|
|
hasConversation?: boolean;
|
|
|
/** 未打开案件时禁用输入与消息区 */
|
|
|
disabled?: boolean;
|
|
|
+ /** 是否显示对话头(工作台内使用;传统分析页已有顶部条,保持默认关闭) */
|
|
|
+ showHeader?: boolean;
|
|
|
streaming?: boolean;
|
|
|
models?: AiModel[];
|
|
|
/** 模型ID(雪花 ID 字符串,禁止转 number) */
|
|
|
@@ -114,6 +143,7 @@
|
|
|
title: '',
|
|
|
hasConversation: false,
|
|
|
disabled: false,
|
|
|
+ showHeader: false,
|
|
|
streaming: false,
|
|
|
models: () => [],
|
|
|
modelId: null,
|
|
|
@@ -254,6 +284,112 @@
|
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
+ /* 聊天外壳:与插件/模型页一致的白色卡片,靠 8/10px 外边距露出页面灰底 */
|
|
|
+ .ai-main.ai-chat-shell {
|
|
|
+ --ai-avatar: 30px;
|
|
|
+
|
|
|
+ margin: 8px 10px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 14px;
|
|
|
+ box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 对话头(工作台内):内容与消息列同宽居中,左右边缘严格对齐 */
|
|
|
+ .ai-chat__head {
|
|
|
+ flex: 0 0 auto;
|
|
|
+ min-height: 56px;
|
|
|
+ padding: 8px 0;
|
|
|
+ background: #fff;
|
|
|
+ border-bottom: 1px solid #eff2f7;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-inner {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ width: 100%;
|
|
|
+ max-width: var(--ai-content-max);
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 0 24px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-icon {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 34px;
|
|
|
+ height: 34px;
|
|
|
+ color: #2a50ec;
|
|
|
+ background: rgb(42 80 236 / 8%);
|
|
|
+ border-radius: 9px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-main {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 2px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-title {
|
|
|
+ overflow: hidden;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-sub {
|
|
|
+ overflow: hidden;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-status {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ height: 24px;
|
|
|
+ padding: 0 10px;
|
|
|
+ color: #2a50ec;
|
|
|
+ font-size: 11.5px;
|
|
|
+ font-weight: 500;
|
|
|
+ background: rgb(42 80 236 / 7%);
|
|
|
+ border: 1px solid rgb(42 80 236 / 16%);
|
|
|
+ border-radius: 999px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-chat__head-dot {
|
|
|
+ width: 6px;
|
|
|
+ height: 6px;
|
|
|
+ background: currentColor;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: ai-chat-pulse 1.2s ease-in-out infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes ai-chat-pulse {
|
|
|
+ 0%,
|
|
|
+ 100% {
|
|
|
+ opacity: 0.35;
|
|
|
+ transform: scale(0.85);
|
|
|
+ }
|
|
|
+
|
|
|
+ 50% {
|
|
|
+ opacity: 1;
|
|
|
+ transform: scale(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.ai-chat {
|
|
|
&__center {
|
|
|
display: flex;
|
|
|
@@ -262,88 +398,140 @@
|
|
|
flex: 1 1 auto;
|
|
|
}
|
|
|
|
|
|
+ /* 无会话时内容垂直居中;内容比视口高时自动退回顶部滚动 */
|
|
|
+ &__welcome-scroll {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ > .ai-chat__welcome {
|
|
|
+ margin-top: auto;
|
|
|
+ margin-bottom: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
&__welcome {
|
|
|
- padding-top: var(--ai-sp-6);
|
|
|
- padding-bottom: var(--ai-sp-6);
|
|
|
+ padding-top: 40px;
|
|
|
+ padding-bottom: 40px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__welcome-icon {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
+ margin-bottom: 18px;
|
|
|
+ color: #2a50ec;
|
|
|
+ background: linear-gradient(135deg, #eef2ff, #dfe8ff);
|
|
|
+ border: 1px solid #dbe4ff;
|
|
|
+ border-radius: 12px;
|
|
|
}
|
|
|
|
|
|
&__welcome-title {
|
|
|
- margin: 0 0 var(--ai-sp-2);
|
|
|
- font-size: 20px;
|
|
|
+ margin: 0 0 8px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 22px;
|
|
|
font-weight: 600;
|
|
|
line-height: 1.4;
|
|
|
- color: var(--ai-text);
|
|
|
}
|
|
|
|
|
|
&__welcome-desc {
|
|
|
- max-width: 620px;
|
|
|
- margin: 0 0 var(--ai-sp-5);
|
|
|
+ max-width: 680px;
|
|
|
+ margin: 0 0 24px;
|
|
|
+ color: #5b6577;
|
|
|
font-size: 13.5px;
|
|
|
line-height: 1.8;
|
|
|
- color: var(--ai-text-2);
|
|
|
}
|
|
|
|
|
|
- /* 用法引导:编号列表 + 可点击示例,比等宽卡片网格更省空间也更像"操作指引" */
|
|
|
- &__steps {
|
|
|
+ /* 示例问题:2×2 卡片,点击填入输入框(只填不发) */
|
|
|
+ &__examples {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__example {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- gap: 0;
|
|
|
- margin: 0;
|
|
|
- padding: 0;
|
|
|
- list-style: none;
|
|
|
- border-top: 1px solid var(--ai-border);
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
+ padding: 14px;
|
|
|
+ font-family: inherit;
|
|
|
+ text-align: left;
|
|
|
+ background: #fafbfe;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ background-color 0.15s ease,
|
|
|
+ border-color 0.15s ease,
|
|
|
+ box-shadow 0.15s ease,
|
|
|
+ transform 0.15s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #fff;
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ box-shadow: 0 4px 14px rgb(16 24 40 / 8%);
|
|
|
+ transform: translateY(-1px);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- &__step {
|
|
|
+ &__example-top {
|
|
|
display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- gap: var(--ai-sp-3);
|
|
|
- padding: var(--ai-sp-3) 0;
|
|
|
- border-bottom: 1px solid var(--ai-border);
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- &__step-index {
|
|
|
+ &__example-index {
|
|
|
flex: 0 0 auto;
|
|
|
display: inline-flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- width: 22px;
|
|
|
- height: 22px;
|
|
|
- margin-top: 1px;
|
|
|
- font-size: 11px;
|
|
|
- font-weight: 600;
|
|
|
- color: var(--ai-primary);
|
|
|
- background: var(--ai-primary-soft);
|
|
|
- border-radius: 50%;
|
|
|
+ height: 20px;
|
|
|
+ padding: 0 6px;
|
|
|
+ color: #2a50ec;
|
|
|
+ font-size: 10.5px;
|
|
|
+ font-weight: 700;
|
|
|
+ letter-spacing: 0.04em;
|
|
|
+ font-variant-numeric: tabular-nums;
|
|
|
+ background: rgb(42 80 236 / 8%);
|
|
|
+ border-radius: 5px;
|
|
|
}
|
|
|
|
|
|
- &__step-body {
|
|
|
+ &__example-title {
|
|
|
flex: 1 1 auto;
|
|
|
min-width: 0;
|
|
|
- }
|
|
|
-
|
|
|
- &__step-title {
|
|
|
+ overflow: hidden;
|
|
|
+ color: #111827;
|
|
|
font-size: 13px;
|
|
|
font-weight: 600;
|
|
|
- color: var(--ai-text);
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
- &__step-prompt {
|
|
|
- display: block;
|
|
|
- margin-top: var(--ai-sp-1);
|
|
|
- padding: 0;
|
|
|
- font-size: 13px;
|
|
|
- line-height: 1.7;
|
|
|
- text-align: left;
|
|
|
- color: var(--ai-text-2);
|
|
|
- background: none;
|
|
|
- border: 0;
|
|
|
- cursor: pointer;
|
|
|
- transition: color 0.15s ease-out;
|
|
|
+ &__example-arrow {
|
|
|
+ flex: 0 0 auto;
|
|
|
+ color: #98a2b3;
|
|
|
+ transition:
|
|
|
+ color 0.15s ease,
|
|
|
+ transform 0.15s ease;
|
|
|
+ }
|
|
|
|
|
|
- &:hover {
|
|
|
- color: var(--ai-primary);
|
|
|
- }
|
|
|
+ &__example:hover &__example-arrow {
|
|
|
+ color: #2a50ec;
|
|
|
+ transform: translateX(2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ &__example-text {
|
|
|
+ display: -webkit-box;
|
|
|
+ margin: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12.5px;
|
|
|
+ line-height: 1.7;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
}
|
|
|
|
|
|
/* 对话区:滚动区 + 悬浮按钮的定位父级 */
|
|
|
@@ -393,4 +581,11 @@
|
|
|
padding-bottom: var(--ai-sp-6);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /* 右侧产物栏展开时聊天列会明显变窄,示例卡提前退成单列 */
|
|
|
+ @media (max-width: 1180px) {
|
|
|
+ .ai-chat__examples {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+ }
|
|
|
</style>
|