Files
ebiz-ai-knowledge-manage/src/views/knowledge/detail/components/HitTest/Index.vue
du.meimei df69effe38 feat:修改密码重置密码
- 使用 ESLint规则格式化了所有修改的文件
- 调整了缩进、空格和换行
- 修复了一些小的语法问题
2025-04-24 18:34:12 +08:00

553 lines
15 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.
<script>
export default {
name: 'hitTest',
data() {
return {
paramsConfig: {
visible: false,
searchMode: 'vector', // 默认选择向量检索
similarity: 0.6, // 默认相似度
topK: 5 // 默认引用分段数
},
inputMessage: '',
loading: false,
messages: [
{
id: 1,
type: 'question',
content: '黄金运用方式有哪些?',
time: new Date()
},
{
id: 2,
type: 'answer',
content: '根据您的问题,我找到了以下相关信息:',
references: [
{
id: 1,
title: '-',
content:
'黄金可以作为投资者的资产配置1把黄金作为储备资产的投资者2被保险人投资在黄金的抵押品或非标资产债券的担保3或者是投资者的避险资产。',
score: 0.709,
file: '北京人寿的准备金样表版_有关项.pdf'
},
{
id: 2,
title: '新增分说明一下',
content: '保险责任',
score: 0.705,
file: 'Sheet1'
},
{
id: 3,
title: '北京人寿承诺服务各项指标',
content:
'第二代保人寿保险营业执照人及行政管理人天津后勤保障处5.第三代保人寿保险公司承诺为公司客户方案服力及运营提供客户服务力6...',
score: 0.703,
file: '北京人寿承诺服务各项指标有效版102026.pdf'
},
{
id: 4,
title: '-',
content: '# 黄金的分:年金',
score: 0.7,
file: 'loma.md'
},
{
id: 5,
title: '第12讲年金 152',
content: '一、年金基础 153',
score: 0.698,
file: 'loma.md'
}
],
time: new Date()
}
]
}
},
methods: {
sendMessage() {
if (!this.inputMessage.trim()) return
// 添加用户问题到消息列表
this.messages.push({
id: this.messages.length + 1,
type: 'question',
content: this.inputMessage,
time: new Date()
})
// 清空输入框
this.inputMessage = ''
// 模拟加载状态
this.loading = true
// 模拟API请求延迟
setTimeout(() => {
this.loading = false
// 这里可以添加实际的API调用逻辑
}, 1000)
},
getTableData() {
// 根据之前的记忆修复getTableData函数确保正确返回Promise
return this.$api
.getDocByPage(this.queryParams)
.then(res => {
return res.data
})
.catch(err => {
console.error('获取数据失败:', err)
return []
})
},
formatScore(score) {
return score.toFixed(3)
}
}
}
</script>
<template>
<div class="hit-test-container">
<!-- 中间内容区域 -->
<div class="content-area">
<div class="empty-state" v-if="messages.length === 0">
<el-image alt="当前会话尚无记录"></el-image>
<p>这里会显示命中测试的记录</p>
</div>
<div class="messages" v-else>
<div
v-for="message in messages"
:key="message.id"
class="message-item"
:class="message.type"
>
<!-- 问题消息 -->
<div v-if="message.type === 'question'" class="question-message">
<div class="message-header">
<el-avatar size="small" icon="el-icon-user"></el-avatar>
<div class="message-info">
<div class="message-title">用户提问</div>
</div>
</div>
<div class="message-content">{{ message.content }}</div>
</div>
<!-- 回答消息 -->
<div v-else-if="message.type === 'answer'" class="answer-message">
<div class="message-header">
<el-avatar size="small" icon="el-icon-s-custom"></el-avatar>
<div class="message-info">
<div class="message-title">AI 回复</div>
</div>
</div>
<div class="message-content">{{ message.content }}</div>
<!-- 引用内容 -->
<div
class="references"
v-if="message.references && message.references.length"
>
<div
v-for="(ref, index) in message.references"
:key="index"
class="reference-item"
>
<div class="reference-header">
<div class="reference-index">{{ index + 1 }}</div>
<div class="reference-title">{{ ref.title }}</div>
<div class="reference-score">
{{ formatScore(ref.score) }}
</div>
</div>
<div class="reference-content">{{ ref.content }}</div>
<div class="reference-footer">
<el-tag size="mini" type="info">
<i class="el-icon-document"></i> {{ ref.file }}
</el-tag>
</div>
</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-message">
<el-skeleton style="width: 100%" animated>
<template slot="template">
<el-skeleton-item
variant="p"
style="width: 100%; height: 60px;"
/>
<el-skeleton-item variant="p" style="width: 90%; height: 20px;" />
<el-skeleton-item variant="p" style="width: 80%; height: 20px;" />
</template>
</el-skeleton>
</div>
</div>
</div>
<!-- 底部输入框 -->
<div class="footer">
<!-- 参数内容 -->
<el-popover placement="top" width="480" v-model="paramsConfig.visible">
<div class="search-params-container">
<h3>检索模式</h3>
<el-radio-group
v-model="paramsConfig.searchMode"
class="search-mode-options"
>
<div
class="search-mode-option"
:class="{ active: paramsConfig.searchMode === 'vector' }"
>
<el-radio v-model="paramsConfig.searchMode" label="vector"
>向量检索</el-radio
>
<p class="option-desc">
向量检索是一种基于向量相似度的检索方式适用于知识库中的大数据量场景
</p>
</div>
<div
class="search-mode-option"
:class="{ active: paramsConfig.searchMode === 'fulltext' }"
>
<el-radio v-model="paramsConfig.searchMode" label="fulltext"
>全文检索</el-radio
>
<p class="option-desc">
全文检索是一种基于文本相似度的检索方式适用于知识库中的小数据量场景
</p>
</div>
<div
class="search-mode-option"
:class="{ active: paramsConfig.searchMode === 'hybrid' }"
>
<el-radio v-model="paramsConfig.searchMode" label="hybrid"
>混合检索</el-radio
>
<p class="option-desc">
混合检索是一种基于向量和文本相似度的检索方式适用于知识库中的中等数据量场景
</p>
</div>
</el-radio-group>
<div class="params-settings">
<div class="param-item">
<span class="param-label">相似度高于</span>
<el-input-number
v-model="paramsConfig.similarity"
:precision="3"
:step="0.001"
:min="0"
:max="1"
size="small"
controls-position="right"
></el-input-number>
</div>
<div class="param-item">
<span class="param-label">引用分段数 TOP</span>
<el-input-number
v-model="paramsConfig.topK"
:min="1"
:max="20"
size="small"
controls-position="right"
></el-input-number>
</div>
</div>
<div class="action-buttons">
<el-button size="small" @click="paramsConfig.visible = false"
>取消</el-button
>
<el-button
type="primary"
size="small"
@click="paramsConfig.visible = false"
>确定</el-button
>
</div>
</div>
<el-button icon="el-icon-setting" size="small" slot="reference"
>参数设置</el-button
>
</el-popover>
<el-input
v-model="inputMessage"
placeholder="请输入"
class="message-input"
@keyup.enter.native="sendMessage"
>
<template slot="append">
<el-button
icon="el-icon-s-promotion"
@click="sendMessage"
size="small"
></el-button>
</template>
</el-input>
</div>
</div>
</template>
<style lang="scss" scoped>
.hit-test-container {
display: flex;
flex-direction: column;
height: 86vh;
background-color: #f5f7fa;
border: 1px solid #e4e7ed;
border-radius: 4px;
overflow: hidden;
.header {
padding: 10px 20px;
border-bottom: 1px solid #e4e7ed;
background-color: #fff;
}
.content-area {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: #909399;
.el-image {
width: 120px;
margin-bottom: 20px;
}
}
.messages {
display: flex;
flex-direction: column;
gap: 20px;
.message-item {
display: flex;
flex-direction: column;
&.question {
align-items: flex-end;
.question-message {
background-color: #ecf5ff;
border-radius: 8px 0 8px 8px;
padding: 12px;
max-width: 80%;
.message-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.message-info {
margin-left: 8px;
}
.message-title {
font-weight: 500;
font-size: 14px;
color: #409eff;
}
}
.message-content {
word-break: break-word;
}
}
}
&.answer {
align-items: flex-start;
.answer-message {
background-color: #fff;
border-radius: 0 8px 8px 8px;
padding: 12px;
max-width: 90%;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
.message-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.message-info {
margin-left: 8px;
}
.message-title {
font-weight: 500;
font-size: 14px;
color: #67c23a;
}
}
.message-content {
margin-bottom: 16px;
word-break: break-word;
}
.references {
display: flex;
flex-direction: column;
gap: 12px;
.reference-item {
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 12px;
background-color: #fafafa;
.reference-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.reference-index {
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #409eff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
margin-right: 8px;
}
.reference-title {
flex: 1;
font-weight: 500;
}
.reference-score {
color: #409eff;
font-weight: 500;
}
}
.reference-content {
margin-bottom: 8px;
font-size: 13px;
color: #606266;
line-height: 1.5;
}
.reference-footer {
display: flex;
justify-content: flex-start;
}
}
}
}
}
}
.loading-message {
padding: 12px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
margin-top: 10px;
}
}
}
.footer {
padding: 10px 20px;
background-color: #fff;
border-top: 1px solid #e4e7ed;
display: flex;
align-items: center;
.el-popover__reference {
margin-right: 10px;
}
.message-input {
flex: 1;
}
}
}
// 参数设置弹窗样式
.search-params-container {
padding: 10px;
h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
font-weight: 500;
}
.search-mode-options {
margin-bottom: 20px;
.search-mode-option {
padding: 10px;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin-bottom: 10px;
&.active {
border-color: #409eff;
background-color: #ecf5ff;
}
.option-desc {
margin: 5px 0 0 24px;
font-size: 12px;
color: #909399;
line-height: 1.4;
}
}
}
.params-settings {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
.param-item {
display: flex;
align-items: center;
.param-label {
margin-right: 10px;
white-space: nowrap;
}
}
}
.action-buttons {
display: flex;
justify-content: flex-end;
.el-button {
margin-left: 10px;
}
}
}
</style>