ForcePilot/web/src/components/SearchConfigTab.vue

302 lines
7.8 KiB
Vue
Raw Normal View History

<template>
<div class="search-config-tab">
<div class="config-header">
<p class="config-title">检索配置</p>
<div class="config-actions">
<a-button size="small" @click="resetToDefaults">重置默认</a-button>
<a-button size="small" type="primary" @click="saveConfig">保存配置</a-button>
</div>
</div>
<div class="config-content">
<div v-if="loading" class="config-loading">
<a-spin size="large" />
<p>加载配置参数中...</p>
</div>
<div v-else-if="error" class="config-error">
<a-result
status="error"
title="配置加载失败"
:sub-title="error"
>
<template #extra>
<a-button type="primary" @click="loadQueryParams">重新加载</a-button>
</template>
</a-result>
</div>
<div v-else class="config-forms">
<a-form layout="vertical">
<a-row :gutter="16">
<a-col :span="12" v-for="param in queryParams" :key="param.key">
<a-form-item :label="param.label">
<a-select
v-if="param.type === 'select'"
v-model:value="meta[param.key]"
style="width: 100%;"
>
<a-select-option
v-for="option in param.options"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</a-select-option>
</a-select>
<a-select
v-else-if="param.type === 'boolean'"
:value="computedMeta[param.key]"
@update:value="(value) => updateMeta(param.key, value)"
style="width: 100%;"
>
<a-select-option value="true">启用</a-select-option>
<a-select-option value="false">关闭</a-select-option>
</a-select>
<a-input-number
v-else-if="param.type === 'number'"
v-model:value="meta[param.key]"
style="width: 100%;"
:min="param.min || 0"
:max="param.max || 100"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue';
import { useDatabaseStore } from '@/stores/database';
import { message } from 'ant-design-vue';
import { queryApi } from '@/apis/knowledge_api';
const props = defineProps({
databaseId: {
type: String,
required: true
}
});
const store = useDatabaseStore();
// 响应式数据
const loading = ref(false);
const error = ref('');
const queryParams = ref([]);
const meta = reactive({});
// 计算属性:处理布尔类型的双向绑定
const computedMeta = computed(() => {
const result = {};
for (const key in meta) {
const param = queryParams.value.find(p => p.key === key);
if (param?.type === 'boolean') {
// 对于布尔类型,返回字符串给 select但保持内部为布尔值
result[key] = meta[key].toString();
} else {
result[key] = meta[key];
}
}
return result;
});
// 处理值更新
const updateMeta = (key, value) => {
const param = queryParams.value.find(p => p.key === key);
if (param?.type === 'boolean') {
// 将字符串转换回布尔值
meta[key] = value === 'true';
} else {
meta[key] = value;
}
};
// 加载查询参数
const loadQueryParams = async () => {
try {
loading.value = true;
error.value = '';
const response = await queryApi.getKnowledgeBaseQueryParams(props.databaseId);
queryParams.value = response.params?.options || [];
// 过滤掉 include_distances 配置项,默认为 True 且不可修改
queryParams.value = queryParams.value.filter(param => param.key !== 'include_distances');
// 初始化 meta 对象
queryParams.value.forEach(param => {
if (param.default !== undefined) {
// 对于布尔类型,确保使用布尔值而不是字符串
if (param.type === 'boolean') {
meta[param.key] = Boolean(param.default);
} else {
meta[param.key] = param.default;
}
}
});
// 确保设置 include_distances 为 true即使不显示也要设置
meta['include_distances'] = true;
// 加载保存的配置
loadSavedConfig();
} catch (err) {
console.error('Failed to load query params:', err);
error.value = err.message || '加载查询参数失败';
} finally {
loading.value = false;
}
};
// 加载保存的配置
const loadSavedConfig = () => {
const saved = localStorage.getItem(`search-config-${props.databaseId}`);
if (saved) {
try {
const savedConfig = JSON.parse(saved);
// 处理布尔类型转换
queryParams.value.forEach(param => {
if (param.type === 'boolean' && savedConfig[param.key] !== undefined) {
// 将字符串值转换为布尔值
if (typeof savedConfig[param.key] === 'string') {
savedConfig[param.key] = savedConfig[param.key] === 'true';
}
}
});
Object.assign(meta, savedConfig);
} catch (e) {
console.warn('Failed to parse saved config:', e);
}
}
// 确保 include_distances 始终为 true覆盖任何保存的值
meta['include_distances'] = true;
};
// 重置为默认值
const resetToDefaults = () => {
queryParams.value.forEach(param => {
if (param.default !== undefined) {
meta[param.key] = param.default;
}
});
// 确保 include_distances 始终为 true
meta['include_distances'] = true;
message.success('已重置为默认配置');
};
// 保存配置
const saveConfig = async () => {
// 确保 include_distances 始终为 true
meta['include_distances'] = true;
// 保存到 localStorage兼容性
localStorage.setItem(`search-config-${props.databaseId}`, JSON.stringify(meta));
// 保存到知识库元数据
try {
const { knowledgeApi } = await import('@/apis/knowledge_api');
const response = await knowledgeApi.updateKnowledgeBaseQueryParams(props.databaseId, meta);
if (response.message === 'success') {
message.success('配置已保存到知识库');
} else {
message.warning('配置已保存到本地,但同步到知识库失败');
}
} catch (error) {
console.error('保存配置到知识库失败:', error);
message.warning('配置已保存到本地,但同步到知识库失败');
}
// 更新 store 中的配置
Object.assign(store.meta, meta);
};
// 组件挂载时加载数据
onMounted(() => {
loadQueryParams();
});
</script>
<style lang="less" scoped>
.search-config-tab {
height: 100%;
display: flex;
flex-direction: column;
Add dark/light theme toggle feature (#343) * Add dark/light theme toggle feature * update: refine dark/light theme and related components * 调整语义化主题变量,优化暗/亮模式切换效果 * Revert "调整语义化主题变量,优化暗/亮模式切换效果" This reverts commit 85d9373297686fdbacb252a880b2847d20a39641. * 深色模式适配:减少 !important,迁移 CSS 变量,优化布局 * style: 替换硬编码颜色值为CSS变量以支持主题切换 将多处硬编码的颜色值(如#fff、#f0f0f0等)替换为CSS变量(如--gray-0、--gray-150等),统一管理颜色样式,便于主题切换和样式维护。主要修改包括背景色、边框色、文字色等视觉元素,同时移除不再需要的深色模式适配代码。 * style: 使用CSS变量替换硬编码颜色值 * refactor(theme): 重构主题系统,统一使用CSS变量并优化暗色模式实现 - 移除冗余的theme.js配置文件,将主题配置集中到theme store - 使用ant-design-vue的darkAlgorithm实现暗色模式 - 在多个组件中替换硬编码颜色值为CSS变量 - 优化用户信息组件,整合文档中心和主题切换功能 - 更新基础CSS样式,完善颜色系统和滚动条样式 * reset: 恢复被覆盖的修改(96d257a7ace38c94e2b113d56472d211d1141087) * feat(theme): 实现深色主题支持并重构样式系统 重构颜色变量系统,添加深色主题支持 移除硬编码颜色值,统一使用CSS变量 优化图表组件以响应主题切换 清理无用样式代码,提升可维护性 * docs: 更新开发规范文档 * style: 调整边框和背景颜色样式 --------- Co-authored-by: Wenjie Zhang <xerrors@163.com>
2025-11-23 01:39:44 +08:00
background: var(--gray-0);
}
.config-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
// border-bottom: 1px solid var(--gray-200);
flex-shrink: 0;
.config-title {
margin: 0;
color: var(--gray-800);
}
.config-actions {
display: flex;
gap: 8px;
}
}
.config-content {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.config-loading,
.config-error {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
color: var(--gray-500);
p {
margin-top: 16px;
font-size: 14px;
}
}
.config-forms {
max-width: 800px;
}
// 表单样式优化
:deep(.ant-form-item) {
margin-bottom: 16px;
}
:deep(.ant-form-item-label > label) {
font-weight: 500;
color: var(--gray-700);
}
:deep(.ant-input),
:deep(.ant-select-selector) {
border-radius: 6px;
}
:deep(.ant-switch) {
background-color: var(--gray-300);
&.ant-switch-checked {
background-color: var(--main-color);
}
}
</style>