搜索优化

This commit is contained in:
JenniferW 2025-12-16 14:52:06 +08:00
parent 5da552d803
commit e990847c45
2 changed files with 151 additions and 20 deletions

View File

@ -1,11 +1,11 @@
import request from '@/utils/request'
// 智能搜索接口 - 根据输入内容获取对应的key和value
export function searchHint(query) {
export function searchHint(data) {
return request({
url: '/products/search/smart',
method: 'get',
params: query
method: 'post',
data: data
})
}

View File

@ -316,7 +316,7 @@
<el-table
:data="filteredCompareTableData"
border
height="60vh"
:height="isFullscreen ? '90vh' : '60vh'"
style="width: 100%; min-width: 600px"
>
<el-table-column
@ -482,6 +482,7 @@ const activePossibleFieldIndex = ref(-1);
const allFields = ref([]);
const fieldValueMap = ref({});
const allValues = ref([]);
const fieldsFromApi = ref(false); // 标记字段是否来自接口
const hintTimer = ref(null);
const pendingSearchTimer = ref(null);
const differenceTimer = ref(null);
@ -1044,6 +1045,8 @@ const triggerConditionSuggestions = (conditionText) => {
valueFieldCounts[val.fieldKey].count += val.weight;
});
// 只有在没有从接口获取字段时才设置
if (!fieldsFromApi.value) {
possibleFields.value = Object.values(valueFieldCounts)
.filter(
(item) =>
@ -1051,6 +1054,7 @@ const triggerConditionSuggestions = (conditionText) => {
)
.sort((a, b) => b.count - a.count)
.map((item) => item.field);
}
// 合并建议并按相关性排序
let allSuggestions = [...fieldSuggestions, ...valueSuggestions];
@ -1155,7 +1159,8 @@ const updateSuggestionsFromValue = (value) => {
valueFieldCounts[val.fieldKey].count += val.weight;
});
// 提取并排序可能的字段
// 提取并排序可能的字段(只有在没有从接口获取字段时才设置)
if (!fieldsFromApi.value) {
possibleFields.value = Object.values(valueFieldCounts)
.filter(
(item) =>
@ -1163,6 +1168,7 @@ const updateSuggestionsFromValue = (value) => {
) // 只显示有足够匹配度的字段且未使用
.sort((a, b) => b.count - a.count) // 按匹配度排序
.map((item) => item.field);
}
valueSuggestions = potentialValues;
} else {
@ -1250,6 +1256,11 @@ const updateSuggestionsFromValue = (value) => {
// 处理输入事件,实时生成建议并调度远程提示
const handleInput = (value, options = {}) => {
const normalizedValue = value ?? "";
// 如果输入为空,重置接口字段标记
if (!normalizedValue.trim()) {
fieldsFromApi.value = false;
possibleFields.value = [];
}
updateSuggestionsFromValue(normalizedValue);
if (!normalizedValue.trim()) {
keyDiffFields.value = [];
@ -1316,8 +1327,127 @@ const fetchSearchHints = async (keyword) => {
: Array.isArray(res)
? res
: [];
mergeFieldMetadata(hintList);
// 若接口无数据:若已有接口缓存,则保持缓存并继续用缓存给出建议;否则关闭下拉
if (!hintList.length) {
if (!allFields.value.length) {
suggestions.value = [];
possibleFields.value = [];
allFields.value = [];
allValues.value = [];
fieldValueMap.value = {};
fieldsFromApi.value = false;
showSuggestions.value = false;
return;
}
// 有缓存则继续用缓存生成建议
updateSuggestionsFromValue(currentInput.value);
showSuggestions.value = suggestions.value.length > 0;
return;
}
// 处理返回的数据:合并字段元数据(仅来自接口)
mergeFieldMetadata(hintList);
// 从返回的数据中提取字段信息用于"可能属于的字段"下拉框
// 只有在没有明确字段时(当前输入不包含冒号)才显示可能字段
const parts = currentInput.value.split(/[;;]/);
const currentPart = parts[parts.length - 1]?.trim() || "";
const cursorIndex = findCursorConditionIndex();
const isEditingExistingField =
cursorIndex >= 0 && cursorIndex < parts.length - 1;
if (
!isEditingExistingField &&
!currentPart.includes(":") &&
hintList.length > 0
) {
// 提取所有字段作为可能的字段选项(所有 fieldName)
const fieldsFromHint = hintList
.filter((item) => item.fieldName && item.fieldKey)
.map((item) => ({
key: item.fieldKey,
label: item.fieldName,
}));
// 去重,只保留未使用的字段
const usedLabels = getUsedFieldLabels(cursorIndex);
const uniqueFields = fieldsFromHint.filter(
(field) => !usedLabels.has(field.label)
);
// 如果当前输入有值,过滤出包含该值的字段
if (currentPart) {
const valueQuery = currentPart.includes(":")
? currentPart.split(":").slice(1).join(":").trim()
: currentPart;
const valueQueryLower = valueQuery.toLowerCase();
const matchingFields = uniqueFields.filter((field) => {
const fieldItem = hintList.find(
(item) => item.fieldKey === field.key
);
if (!fieldItem || !Array.isArray(fieldItem.fieldValues)) return false;
return fieldItem.fieldValues.some((value) =>
String(value).toLowerCase().includes(valueQueryLower)
);
});
// 如果有关联的字段,优先显示这些字段
possibleFields.value =
matchingFields.length > 0 ? matchingFields : uniqueFields;
} else {
possibleFields.value = uniqueFields;
}
// 标记字段来自接口
fieldsFromApi.value = true;
} else {
possibleFields.value = [];
fieldsFromApi.value = false;
}
// 基于接口返回的 fieldValues 直接构建值建议,仅展示接口值、不做数量限制
const valueQuery = currentPart.includes(":")
? currentPart.split(":").slice(1).join(":").trim()
: currentPart;
const valueQueryLower = (valueQuery || "").toLowerCase();
const apiValueSuggestions = [];
hintList.forEach((item) => {
if (!Array.isArray(item.fieldValues)) return;
const fieldKey = item.fieldKey || item.key || "";
const fieldLabel = item.fieldName || item.label || item.fieldLabel || "";
item.fieldValues.forEach((val) => {
const strVal =
val === null || val === undefined ? "" : String(val).trim();
if (!strVal) return;
if (
valueQueryLower &&
!strVal.toLowerCase().includes(valueQueryLower)
) {
return;
}
apiValueSuggestions.push({
type: "value",
label: strVal,
value: strVal,
fieldKey,
fieldLabel,
weight: 5, // 接口值优先展示
});
});
});
// 只用接口返回的值作为下拉建议,并去重
const seen = new Set();
const uniqueSuggestions = [];
apiValueSuggestions.forEach((s) => {
const key = s.label; // 按展示文本去重
if (seen.has(key)) return;
seen.add(key);
uniqueSuggestions.push(s);
});
suggestions.value = uniqueSuggestions;
showSuggestions.value = uniqueSuggestions.length > 0;
} catch (error) {
console.error("searchHint error:", error);
}
@ -1912,6 +2042,7 @@ const clearAll = () => {
showResults.value = false;
showSuggestions.value = false;
possibleFields.value = [];
fieldsFromApi.value = false; // 重置接口字段标记
selectedConditionIndex.value = -1;
// 重置关键差异提示
keyDiffFields.value = [];