接口化
This commit is contained in:
parent
ab07644f22
commit
fc7fd4f2cf
|
|
@ -210,32 +210,32 @@
|
||||||
<div class="result-card-list">
|
<div class="result-card-list">
|
||||||
<el-card
|
<el-card
|
||||||
v-for="item in currentPageData"
|
v-for="item in currentPageData"
|
||||||
:key="item.productNumber"
|
:key="item.partNumber"
|
||||||
class="result-card"
|
class="result-card"
|
||||||
:class="{ active: item.selected }"
|
:class="{ active: item.selected }"
|
||||||
@click="toggleSelect(item)"
|
@click="toggleSelect(item)"
|
||||||
>
|
>
|
||||||
<div class="result-card-info">
|
<div class="result-card-info">
|
||||||
<div class="result-card-name" :title="item.name">
|
<div class="result-card-name" :title="item.partName">
|
||||||
{{ item.name }}
|
{{ item.partName }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="result-card-parameter"
|
class="result-card-parameter"
|
||||||
:title="`品号:${item.productNumber}`"
|
:title="`品号:${item.partNumber}`"
|
||||||
>
|
>
|
||||||
<span>品号:</span>{{ item.productNumber }}
|
<span>品号:</span>{{ item.partNumber }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="result-card-parameter"
|
class="result-card-parameter"
|
||||||
:title="`品号-规格型号:${item.specificationModel}`"
|
:title="`品号-规格型号:${item.partNumberSpec}`"
|
||||||
>
|
>
|
||||||
<span>品号-规格型号: </span>{{ item.specificationModel }}
|
<span>品号-规格型号: </span>{{ item.partNumberSpec }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="result-card-parameter text-truncate"
|
class="result-card-parameter text-truncate"
|
||||||
:title="`车型:${item.carModel}`"
|
:title="`车型:${item.trainModel}`"
|
||||||
>
|
>
|
||||||
<span>车型:</span> {{ item.carModel }}
|
<span>车型:</span> {{ item.trainModel }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
@ -309,9 +309,9 @@
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="item in sortedCompareList"
|
v-for="item in sortedCompareList"
|
||||||
:key="item.productNumber"
|
:key="item.partNumber"
|
||||||
:label="item.productNumber"
|
:label="item.partNumber"
|
||||||
:prop="item.productNumber"
|
:prop="item.partNumber"
|
||||||
align="center"
|
align="center"
|
||||||
header-align="center"
|
header-align="center"
|
||||||
min-width="300"
|
min-width="300"
|
||||||
|
|
@ -344,8 +344,8 @@
|
||||||
</span>
|
</span>
|
||||||
<span style="font-size: 12px; color: #666">{{
|
<span style="font-size: 12px; color: #666">{{
|
||||||
sortedCompareList.find(
|
sortedCompareList.find(
|
||||||
(i) => i.productNumber === column.label
|
(i) => i.partNumber === column.label
|
||||||
).name
|
)?.partName || "-"
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -407,14 +407,13 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted, nextTick } from "vue";
|
import { ref, computed, watch, onMounted, nextTick } from "vue";
|
||||||
import {
|
import {
|
||||||
paramsToCompare,
|
searchHint,
|
||||||
fieldMap,
|
search as searchProducts,
|
||||||
allFields,
|
differenceWords,
|
||||||
mockData,
|
compare,
|
||||||
fieldValueMap,
|
} from "@/api/order";
|
||||||
} from "@/data/step2MockData";
|
|
||||||
import { ElMessage, ElEmpty, ElDialog } from "element-plus";
|
import { ElMessage, ElEmpty, ElDialog } from "element-plus";
|
||||||
import { Search, Edit, RefreshLeft, Microphone } from "@element-plus/icons-vue";
|
import { Search, RefreshLeft, Microphone } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
// 接收props和定义emit
|
// 接收props和定义emit
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -448,6 +447,11 @@ const showSuggestions = ref(false);
|
||||||
const activeSuggestionIndex = ref(-1);
|
const activeSuggestionIndex = ref(-1);
|
||||||
const possibleFields = ref([]);
|
const possibleFields = ref([]);
|
||||||
const activePossibleFieldIndex = ref(-1);
|
const activePossibleFieldIndex = ref(-1);
|
||||||
|
const allFields = ref([]);
|
||||||
|
const fieldValueMap = ref({});
|
||||||
|
const allValues = ref([]);
|
||||||
|
const hintTimer = ref(null);
|
||||||
|
const pendingSearchTimer = ref(null);
|
||||||
|
|
||||||
// 语音输入弹窗控制
|
// 语音输入弹窗控制
|
||||||
const showVoicePopup = ref(false);
|
const showVoicePopup = ref(false);
|
||||||
|
|
@ -466,8 +470,7 @@ const totalItems = ref(0);
|
||||||
|
|
||||||
// 计算当前页数据
|
// 计算当前页数据
|
||||||
const currentPageData = computed(() => {
|
const currentPageData = computed(() => {
|
||||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
return filteredData.value;
|
||||||
return filteredData.value.slice(startIndex, startIndex + pageSize.value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算选中的商品数量
|
// 计算选中的商品数量
|
||||||
|
|
@ -478,7 +481,7 @@ const selectedCount = computed(() => {
|
||||||
// 选中的商品品号
|
// 选中的商品品号
|
||||||
const selectedProductNumber = computed(() => {
|
const selectedProductNumber = computed(() => {
|
||||||
const selected = filteredData.value.find((item) => item.selected);
|
const selected = filteredData.value.find((item) => item.selected);
|
||||||
return selected ? selected.productNumber : "";
|
return selected ? selected.partNumber : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
// 对比相关
|
// 对比相关
|
||||||
|
|
@ -506,8 +509,8 @@ const sortedCompareList = computed(() => {
|
||||||
// 排序:根据品号结尾的五位数从大到小排序
|
// 排序:根据品号结尾的五位数从大到小排序
|
||||||
list.sort((a, b) => {
|
list.sort((a, b) => {
|
||||||
// 提取品号结尾的五位数
|
// 提取品号结尾的五位数
|
||||||
const aMatch = a.productNumber.match(/-(\d{5})$/);
|
const aMatch = a.partNumber?.match(/-(\d{5})$/);
|
||||||
const bMatch = b.productNumber.match(/-(\d{5})$/);
|
const bMatch = b.partNumber?.match(/-(\d{5})$/);
|
||||||
|
|
||||||
const aNum = aMatch ? parseInt(aMatch[1], 10) : 0;
|
const aNum = aMatch ? parseInt(aMatch[1], 10) : 0;
|
||||||
const bNum = bMatch ? parseInt(bMatch[1], 10) : 0;
|
const bNum = bMatch ? parseInt(bMatch[1], 10) : 0;
|
||||||
|
|
@ -525,78 +528,21 @@ const sortedCompareList = computed(() => {
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 生成对比表格数据并分析差异
|
const compareTableData = ref([]);
|
||||||
const compareTableData = computed(() => {
|
|
||||||
if (sortedCompareList.value.length === 0) {
|
|
||||||
parameterDifferences.value = {};
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置差异映射
|
const updateParameterDifferenceFlags = (rows) => {
|
||||||
const differences = {};
|
const differences = {};
|
||||||
|
rows.forEach((row) => {
|
||||||
// 获取当前时间和昨天的日期
|
if (!row || !row.param) return;
|
||||||
const today = new Date();
|
const values = sortedCompareList.value.map((item) => row[item.partNumber]);
|
||||||
const yesterday = new Date(today);
|
const baseline = values.find(
|
||||||
yesterday.setDate(yesterday.getDate() - 1);
|
(val) => val !== undefined && val !== null && val !== "-"
|
||||||
|
);
|
||||||
// 格式化日期为 yyyy-MM-dd
|
differences[row.param] =
|
||||||
const formatDate = (date) => {
|
baseline !== undefined && values.some((val) => val !== baseline);
|
||||||
return date.toISOString().split("T")[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
const formattedToday = formatDate(today);
|
|
||||||
const formattedYesterday = formatDate(yesterday);
|
|
||||||
|
|
||||||
// 构建对比数据,添加新参数
|
|
||||||
const result = [
|
|
||||||
// 原有参数
|
|
||||||
{ param: "品号-规格型号" },
|
|
||||||
// 新增参数:品号申请时间
|
|
||||||
{ param: "品号申请时间" },
|
|
||||||
{ param: "图号" },
|
|
||||||
{ param: "技术规范编号" },
|
|
||||||
{ param: "技术规范版本" },
|
|
||||||
{ param: "技术规范名称" },
|
|
||||||
// 新增参数:技术要求签订时间
|
|
||||||
{ param: "技术要求签订时间" },
|
|
||||||
{ param: "CBC编号" },
|
|
||||||
{ param: "CBC版本" },
|
|
||||||
{ param: "车型" },
|
|
||||||
{ param: "型号" },
|
|
||||||
{ param: "材质" },
|
|
||||||
{ param: "采购属性" },
|
|
||||||
{ param: "车轮踏面形式" },
|
|
||||||
{ param: "油漆制造商" },
|
|
||||||
].map((item) => {
|
|
||||||
const row = { param: item.param };
|
|
||||||
|
|
||||||
// 为每个产品填充参数值
|
|
||||||
sortedCompareList.value.forEach((product) => {
|
|
||||||
// 处理新增的两个参数
|
|
||||||
if (item.param === "品号申请时间") {
|
|
||||||
row[product.productNumber] = formattedToday;
|
|
||||||
} else if (item.param === "技术要求签订时间") {
|
|
||||||
row[product.productNumber] = formattedYesterday;
|
|
||||||
} else {
|
|
||||||
// 处理原有参数
|
|
||||||
const fieldKey = fieldMap[item.param];
|
|
||||||
row[product.productNumber] = fieldKey ? product[fieldKey] ?? "-" : "-";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 收集所有产品的参数值用于判断差异
|
|
||||||
const values = sortedCompareList.value.map((p) => row[p.productNumber]);
|
|
||||||
const allSame = values.every((v) => v === values[0]);
|
|
||||||
differences[item.param] = !allSame;
|
|
||||||
|
|
||||||
return row;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 保存差异信息
|
|
||||||
parameterDifferences.value = differences;
|
parameterDifferences.value = differences;
|
||||||
return result;
|
};
|
||||||
});
|
|
||||||
|
|
||||||
// 过滤后的对比表格数据(根据"仅看不同项"选项)
|
// 过滤后的对比表格数据(根据"仅看不同项"选项)
|
||||||
const filteredCompareTableData = computed(() => {
|
const filteredCompareTableData = computed(() => {
|
||||||
|
|
@ -616,26 +562,24 @@ const isValueDifferent = (param) => {
|
||||||
return parameterDifferences.value[param];
|
return parameterDifferences.value[param];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 收集所有可能的值用于匹配
|
|
||||||
const allValues = ref([]);
|
|
||||||
|
|
||||||
// 初始化所有可能的值
|
// 初始化所有可能的值
|
||||||
const initializeValues = () => {
|
const initializeValues = () => {
|
||||||
const values = [];
|
const values = [];
|
||||||
allFields.forEach((field) => {
|
allFields.value.forEach((field) => {
|
||||||
const uniqueValues = [
|
const uniqueValues = [
|
||||||
...new Set(
|
...new Set(
|
||||||
mockData.map((item) => item[field.key] ?? "") // 将null转换为空字符串
|
(fieldValueMap.value[field.key] || []).map((item) =>
|
||||||
|
item === null || item === undefined ? "" : String(item)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
uniqueValues.forEach((value) => {
|
uniqueValues.forEach((value) => {
|
||||||
// 过滤空值,避免空提示
|
|
||||||
if (value !== "") {
|
if (value !== "") {
|
||||||
values.push({
|
values.push({
|
||||||
value: String(value),
|
value,
|
||||||
fieldKey: field.key,
|
fieldKey: field.key,
|
||||||
fieldLabel: field.label,
|
fieldLabel: field.label,
|
||||||
weight: fieldValueMap[field.key]?.includes(String(value)) ? 2 : 1,
|
weight: 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -643,32 +587,30 @@ const initializeValues = () => {
|
||||||
allValues.value = values;
|
allValues.value = values;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检查并更新关键差异字段提示
|
const mergeFieldMetadata = (fields = []) => {
|
||||||
const updateKeyDiffHint = () => {
|
if (!Array.isArray(fields) || fields.length === 0) return;
|
||||||
// 获取技术规范编号和CBC编号的字段信息
|
const fieldMapCache = new Map(
|
||||||
const techSpecField = allFields.find(
|
allFields.value.map((item) => [item.key, item])
|
||||||
(field) => field.label === "技术规范编号"
|
|
||||||
);
|
|
||||||
const cbcField = allFields.find((field) => field.label === "CBC编号");
|
|
||||||
|
|
||||||
// 检查当前搜索条件中是否包含这两个字段
|
|
||||||
const hasTechSpec = parsedConditions.value.some(
|
|
||||||
(cond) => cond.field === techSpecField?.key
|
|
||||||
);
|
|
||||||
const hasCbc = parsedConditions.value.some(
|
|
||||||
(cond) => cond.field === cbcField?.key
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// 确定需要显示的关键差异字段
|
fields.forEach((item) => {
|
||||||
const diffFields = [];
|
const label = item.fieldName || item.fieldLabel || item.label;
|
||||||
if (!hasTechSpec && techSpecField) diffFields.push(techSpecField);
|
const key = item.fieldKey || item.key;
|
||||||
if (!hasCbc && cbcField) diffFields.push(cbcField);
|
if (!label || !key) return;
|
||||||
|
if (!fieldMapCache.has(key)) {
|
||||||
|
fieldMapCache.set(key, { key, label });
|
||||||
|
}
|
||||||
|
|
||||||
// 更新关键差异字段和显示状态
|
if (Array.isArray(item.fieldValues) && item.fieldValues.length > 0) {
|
||||||
keyDiffFields.value = diffFields;
|
const existingValues = fieldValueMap.value[key] || [];
|
||||||
// 只有当有搜索条件且有关键差异字段时才显示提示
|
fieldValueMap.value[key] = Array.from(
|
||||||
showKeyDiffHint.value =
|
new Set([...existingValues, ...item.fieldValues])
|
||||||
currentInput.value.trim() !== "" && diffFields.length > 0;
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
allFields.value = Array.from(fieldMapCache.values());
|
||||||
|
initializeValues();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 点击差异字段时添加到输入框
|
// 点击差异字段时添加到输入框
|
||||||
|
|
@ -834,7 +776,7 @@ const triggerConditionSuggestions = (conditionText) => {
|
||||||
const fieldLabel = conditionText.substring(0, colonIndex).trim();
|
const fieldLabel = conditionText.substring(0, colonIndex).trim();
|
||||||
const valuePart = conditionText.substring(colonIndex + 1).trim();
|
const valuePart = conditionText.substring(colonIndex + 1).trim();
|
||||||
|
|
||||||
const field = allFields.find((f) => f.label === fieldLabel);
|
const field = allFields.value.find((f) => f.label === fieldLabel);
|
||||||
|
|
||||||
if (field) {
|
if (field) {
|
||||||
// 显示该字段的所有可能值
|
// 显示该字段的所有可能值
|
||||||
|
|
@ -869,10 +811,11 @@ const triggerConditionSuggestions = (conditionText) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理输入事件,实时生成建议
|
// 根据当前输入更新建议列表(不触发远程请求)
|
||||||
const handleInput = (value) => {
|
const updateSuggestionsFromValue = (value) => {
|
||||||
|
const normalizedValue = value ?? "";
|
||||||
// 获取当前正在输入的条件部分(分号后面的部分)
|
// 获取当前正在输入的条件部分(分号后面的部分)
|
||||||
const parts = value.split(/[;;]/);
|
const parts = normalizedValue.split(/[;;]/);
|
||||||
const currentPart = parts[parts.length - 1].trim();
|
const currentPart = parts[parts.length - 1].trim();
|
||||||
|
|
||||||
// 检查是否在编辑已有条件
|
// 检查是否在编辑已有条件
|
||||||
|
|
@ -884,7 +827,7 @@ const handleInput = (value) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 始终显示建议框,除非输入为空
|
// 始终显示建议框,除非输入为空
|
||||||
showSuggestions.value = value.trim() !== "";
|
showSuggestions.value = normalizedValue.trim() !== "";
|
||||||
|
|
||||||
// 如果当前部分为空,不显示具体建议
|
// 如果当前部分为空,不显示具体建议
|
||||||
if (!currentPart) {
|
if (!currentPart) {
|
||||||
|
|
@ -902,7 +845,7 @@ const handleInput = (value) => {
|
||||||
|
|
||||||
if (isEditingField) {
|
if (isEditingField) {
|
||||||
// 正在输入字段或值的开始部分,同时查找字段和值
|
// 正在输入字段或值的开始部分,同时查找字段和值
|
||||||
fieldSuggestions = allFields
|
fieldSuggestions = allFields.value
|
||||||
.filter((field) =>
|
.filter((field) =>
|
||||||
field.label.toLowerCase().includes(currentPart.toLowerCase())
|
field.label.toLowerCase().includes(currentPart.toLowerCase())
|
||||||
)
|
)
|
||||||
|
|
@ -934,7 +877,7 @@ const handleInput = (value) => {
|
||||||
valueFieldCounts[val.fieldKey] = {
|
valueFieldCounts[val.fieldKey] = {
|
||||||
count: 0,
|
count: 0,
|
||||||
label: val.fieldLabel,
|
label: val.fieldLabel,
|
||||||
field: allFields.find((f) => f.key === val.fieldKey),
|
field: allFields.value.find((f) => f.key === val.fieldKey),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
valueFieldCounts[val.fieldKey].count += val.weight;
|
valueFieldCounts[val.fieldKey].count += val.weight;
|
||||||
|
|
@ -954,7 +897,7 @@ const handleInput = (value) => {
|
||||||
const valuePart = currentPart.substring(colonIndex + 1).trim();
|
const valuePart = currentPart.substring(colonIndex + 1).trim();
|
||||||
|
|
||||||
// 查找对应的字段
|
// 查找对应的字段
|
||||||
const field = allFields.find((f) => f.label === fieldLabel);
|
const field = allFields.value.find((f) => f.label === fieldLabel);
|
||||||
|
|
||||||
if (field) {
|
if (field) {
|
||||||
// 只查找该字段的值
|
// 只查找该字段的值
|
||||||
|
|
@ -1029,6 +972,46 @@ const handleInput = (value) => {
|
||||||
activePossibleFieldIndex.value = -1;
|
activePossibleFieldIndex.value = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理输入事件,实时生成建议并调度远程提示
|
||||||
|
const handleInput = (value, options = {}) => {
|
||||||
|
const normalizedValue = value ?? "";
|
||||||
|
updateSuggestionsFromValue(normalizedValue);
|
||||||
|
if (options.skipFetch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parts = normalizedValue.split(/[;;]/);
|
||||||
|
const currentPart = parts[parts.length - 1]?.trim();
|
||||||
|
if (currentPart) {
|
||||||
|
scheduleSearchHint(currentPart);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchSearchHints = async (keyword) => {
|
||||||
|
if (!keyword) return;
|
||||||
|
try {
|
||||||
|
const res = await searchHint({ keyword });
|
||||||
|
const hintList = Array.isArray(res?.data)
|
||||||
|
? res.data
|
||||||
|
: Array.isArray(res)
|
||||||
|
? res
|
||||||
|
: [];
|
||||||
|
mergeFieldMetadata(hintList);
|
||||||
|
updateSuggestionsFromValue(currentInput.value, { skipFetch: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("searchHint error:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleSearchHint = (keyword) => {
|
||||||
|
if (hintTimer.value) {
|
||||||
|
clearTimeout(hintTimer.value);
|
||||||
|
}
|
||||||
|
if (!keyword) return;
|
||||||
|
hintTimer.value = setTimeout(() => {
|
||||||
|
fetchSearchHints(keyword);
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
// 选择可能的字段 - 修复问题4
|
// 选择可能的字段 - 修复问题4
|
||||||
const selectPossibleField = (field) => {
|
const selectPossibleField = (field) => {
|
||||||
// 始终在当前条件上操作,不修改其他条件
|
// 始终在当前条件上操作,不修改其他条件
|
||||||
|
|
@ -1252,7 +1235,7 @@ const parseConditions = (input) => {
|
||||||
const valuePart = part.substring(colonIndex + 1).trim();
|
const valuePart = part.substring(colonIndex + 1).trim();
|
||||||
|
|
||||||
// 查找对应的字段key
|
// 查找对应的字段key
|
||||||
const field = allFields.find((f) => f.label === fieldLabel);
|
const field = allFields.value.find((f) => f.label === fieldLabel);
|
||||||
|
|
||||||
conditions.push({
|
conditions.push({
|
||||||
fieldLabel,
|
fieldLabel,
|
||||||
|
|
@ -1274,6 +1257,148 @@ const parseConditions = (input) => {
|
||||||
return conditions;
|
return conditions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPreciseIndexSet = () => {
|
||||||
|
const preciseIndexSet = new Set();
|
||||||
|
preciseConditions.value.forEach((condition) => {
|
||||||
|
if (typeof condition.originalIndex === "number") {
|
||||||
|
preciseIndexSet.add(condition.originalIndex);
|
||||||
|
} else {
|
||||||
|
const idx = parsedConditions.value.findIndex(
|
||||||
|
(item) =>
|
||||||
|
item.field === condition.field && item.value === condition.value
|
||||||
|
);
|
||||||
|
if (idx !== -1) {
|
||||||
|
preciseIndexSet.add(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return preciseIndexSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildFieldConditionsPayload = () => {
|
||||||
|
const preciseIndexSet = getPreciseIndexSet();
|
||||||
|
|
||||||
|
return parsedConditions.value
|
||||||
|
.filter((condition) => condition.value)
|
||||||
|
.map((condition, index) => {
|
||||||
|
const queryType = preciseIndexSet.has(index) ? "EXACT" : "FUZZY";
|
||||||
|
|
||||||
|
if (condition.valid && condition.field) {
|
||||||
|
return {
|
||||||
|
fieldName: condition.fieldLabel,
|
||||||
|
fieldValue: condition.value,
|
||||||
|
keyword: "",
|
||||||
|
queryType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyword = condition.fieldLabel
|
||||||
|
? `${condition.fieldLabel}:${condition.value}`.replace(/:$/, "")
|
||||||
|
: condition.value;
|
||||||
|
|
||||||
|
return {
|
||||||
|
fieldName: "",
|
||||||
|
fieldValue: "",
|
||||||
|
keyword,
|
||||||
|
queryType,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const executeSearch = async ({ page = currentPage.value } = {}) => {
|
||||||
|
if (pendingSearchTimer.value) {
|
||||||
|
clearTimeout(pendingSearchTimer.value);
|
||||||
|
pendingSearchTimer.value = null;
|
||||||
|
}
|
||||||
|
const payload = {
|
||||||
|
fieldConditions: buildFieldConditionsPayload(),
|
||||||
|
inputWord: currentInput.value.trim(),
|
||||||
|
page: Math.max(page - 1, 0),
|
||||||
|
size: pageSize.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await searchProducts(payload);
|
||||||
|
const pageData = res?.data ?? res ?? {};
|
||||||
|
const list = pageData?.content ?? [];
|
||||||
|
|
||||||
|
filteredData.value = list.map((item) => ({
|
||||||
|
...item,
|
||||||
|
selected: false,
|
||||||
|
}));
|
||||||
|
totalItems.value = pageData?.totalElements ?? list.length;
|
||||||
|
const serverPage = pageData?.number ?? Math.max(page - 1, 0);
|
||||||
|
currentPage.value = serverPage + 1;
|
||||||
|
pageSize.value = pageData?.size ?? pageSize.value;
|
||||||
|
showResults.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("search error:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleSearchExecution = (page = 1) => {
|
||||||
|
if (pendingSearchTimer.value) {
|
||||||
|
clearTimeout(pendingSearchTimer.value);
|
||||||
|
}
|
||||||
|
pendingSearchTimer.value = setTimeout(() => {
|
||||||
|
executeSearch({ page });
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDifferenceRecommendations = async () => {
|
||||||
|
if (!currentInput.value.trim()) {
|
||||||
|
keyDiffFields.value = [];
|
||||||
|
showKeyDiffHint.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await differenceWords({
|
||||||
|
fieldConditions: buildFieldConditionsPayload(),
|
||||||
|
inputWord: currentInput.value.trim(),
|
||||||
|
});
|
||||||
|
const diffList = Array.isArray(res?.data)
|
||||||
|
? res.data
|
||||||
|
: Array.isArray(res)
|
||||||
|
? res
|
||||||
|
: [];
|
||||||
|
mergeFieldMetadata(diffList);
|
||||||
|
keyDiffFields.value = diffList.slice(0, 5).map((item) => ({
|
||||||
|
label: item.fieldName || "",
|
||||||
|
key: item.fieldKey,
|
||||||
|
}));
|
||||||
|
showKeyDiffHint.value =
|
||||||
|
keyDiffFields.value.length > 0 && currentInput.value.trim() !== "";
|
||||||
|
} catch (error) {
|
||||||
|
console.error("differenceWords error:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchCompareTable = async () => {
|
||||||
|
const idList = selectedCompareList.value
|
||||||
|
.map((item) => item.id || item.partNumber)
|
||||||
|
.filter(Boolean);
|
||||||
|
const ids = idList.join(",");
|
||||||
|
if (!ids) {
|
||||||
|
compareTableData.value = [];
|
||||||
|
parameterDifferences.value = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await compare({ ids });
|
||||||
|
const rows = Array.isArray(res?.data)
|
||||||
|
? res.data
|
||||||
|
: Array.isArray(res)
|
||||||
|
? res
|
||||||
|
: [];
|
||||||
|
compareTableData.value = rows;
|
||||||
|
updateParameterDifferenceFlags(rows);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("compare error:", error);
|
||||||
|
compareTableData.value = [];
|
||||||
|
parameterDifferences.value = {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 添加精准查询条件
|
// 添加精准查询条件
|
||||||
const addPreciseCondition = (index) => {
|
const addPreciseCondition = (index) => {
|
||||||
const condition = parsedConditions.value[index];
|
const condition = parsedConditions.value[index];
|
||||||
|
|
@ -1284,34 +1409,27 @@ const addPreciseCondition = (index) => {
|
||||||
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
preciseConditions.value.push({ ...condition, originalIndex: index });
|
preciseConditions.value.push({ ...condition, originalIndex: index });
|
||||||
// 重新过滤数据,应用精准查询
|
executeSearch({ page: 1 });
|
||||||
applyPreciseFilter();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 移除精准查询条件
|
// 移除精准查询条件
|
||||||
const removePreciseCondition = (index) => {
|
const removePreciseCondition = (index) => {
|
||||||
preciseConditions.value.splice(index, 1);
|
preciseConditions.value.splice(index, 1);
|
||||||
// 重新过滤数据
|
executeSearch({ page: 1 });
|
||||||
applyPreciseFilter();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理查询
|
// 处理查询
|
||||||
const handleSearch = () => {
|
const handleSearch = async () => {
|
||||||
// 解析条件
|
// 解析条件
|
||||||
parsedConditions.value = parseConditions(currentInput.value);
|
parsedConditions.value = parseConditions(currentInput.value);
|
||||||
|
|
||||||
// 更新关键差异提示
|
|
||||||
updateKeyDiffHint();
|
|
||||||
|
|
||||||
// 清空精准查询条件
|
// 清空精准查询条件
|
||||||
preciseConditions.value = [];
|
preciseConditions.value = [];
|
||||||
|
|
||||||
// 应用过滤
|
// 执行查询与关键差异词推荐
|
||||||
applyPreciseFilter();
|
await executeSearch({ page: 1 });
|
||||||
|
await fetchDifferenceRecommendations();
|
||||||
// 显示结果
|
|
||||||
showResults.value = true;
|
|
||||||
|
|
||||||
// 隐藏建议
|
// 隐藏建议
|
||||||
showSuggestions.value = false;
|
showSuggestions.value = false;
|
||||||
|
|
@ -1323,106 +1441,10 @@ const triggerRealTimeSearch = () => {
|
||||||
// 只有当有有效条件时才触发实时查询
|
// 只有当有有效条件时才触发实时查询
|
||||||
if (currentInput.value.trim()) {
|
if (currentInput.value.trim()) {
|
||||||
parsedConditions.value = parseConditions(currentInput.value);
|
parsedConditions.value = parseConditions(currentInput.value);
|
||||||
// 更新关键差异提示
|
scheduleSearchExecution(1);
|
||||||
updateKeyDiffHint();
|
|
||||||
applyPreciseFilter();
|
|
||||||
showResults.value = true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 应用精准过滤
|
|
||||||
const applyPreciseFilter = () => {
|
|
||||||
// 先应用原始过滤
|
|
||||||
let filtered = filterData(parsedConditions.value);
|
|
||||||
|
|
||||||
// 再应用精准过滤
|
|
||||||
if (preciseConditions.value.length > 0) {
|
|
||||||
filtered = filtered.filter((item) => {
|
|
||||||
return preciseConditions.value.every((condition) => {
|
|
||||||
if (!condition.valid) return false;
|
|
||||||
|
|
||||||
if (condition.field) {
|
|
||||||
const fieldValue = item[condition.field];
|
|
||||||
return checkPreciseCondition(fieldValue, condition.value);
|
|
||||||
} else {
|
|
||||||
return allFields.some((field) => {
|
|
||||||
const fieldValue = item[field.key];
|
|
||||||
return checkPreciseCondition(fieldValue, condition.value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新过滤后的数据
|
|
||||||
filteredData.value = filtered.map((item) => ({ ...item, selected: false }));
|
|
||||||
totalItems.value = filteredData.value.length;
|
|
||||||
currentPage.value = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 根据条件过滤数据
|
|
||||||
const filterData = (conditions) => {
|
|
||||||
return mockData.filter((item) => {
|
|
||||||
return conditions.every((condition) => {
|
|
||||||
if (!condition.valid) return false;
|
|
||||||
|
|
||||||
// 如果指定了字段
|
|
||||||
if (condition.field) {
|
|
||||||
const fieldValue = item[condition.field];
|
|
||||||
return checkCondition(fieldValue, condition.value);
|
|
||||||
}
|
|
||||||
// 没有指定字段,检查所有字段
|
|
||||||
else {
|
|
||||||
return allFields.some((field) => {
|
|
||||||
const fieldValue = item[field.key];
|
|
||||||
return checkCondition(fieldValue, condition.value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查单个条件(模糊匹配)
|
|
||||||
const checkCondition = (fieldValue, conditionValue) => {
|
|
||||||
// 将null转换为空字符串处理
|
|
||||||
const processedValue = fieldValue ?? "";
|
|
||||||
const processedCondition = conditionValue ?? "";
|
|
||||||
|
|
||||||
// 后续逻辑使用processedValue和processedCondition
|
|
||||||
const operators = [
|
|
||||||
{ symbol: ">=", func: (a, b) => a >= b },
|
|
||||||
{ symbol: "<=", func: (a, b) => a <= b },
|
|
||||||
{ symbol: "!=", func: (a, b) => a != b },
|
|
||||||
{ symbol: ">", func: (a, b) => a > b },
|
|
||||||
{ symbol: "<", func: (a, b) => a < b },
|
|
||||||
{ symbol: "=", func: (a, b) => a == b },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const op of operators) {
|
|
||||||
if (processedCondition.startsWith(op.symbol)) {
|
|
||||||
const value = processedCondition.substring(op.symbol.length).trim();
|
|
||||||
if (!isNaN(Number(processedValue)) && !isNaN(Number(value))) {
|
|
||||||
return op.func(Number(processedValue), Number(value));
|
|
||||||
}
|
|
||||||
return op.func(String(processedValue), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return String(processedValue)
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(processedCondition.toLowerCase());
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查精准条件(精确匹配)
|
|
||||||
const checkPreciseCondition = (fieldValue, conditionValue) => {
|
|
||||||
// 将null转换为空字符串处理
|
|
||||||
const processedValue = fieldValue ?? "";
|
|
||||||
const processedCondition = conditionValue ?? "";
|
|
||||||
|
|
||||||
// 精准匹配,不使用包含关系
|
|
||||||
return String(processedValue) === processedCondition;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 移除单个条件
|
// 移除单个条件
|
||||||
const removeCondition = (index) => {
|
const removeCondition = (index) => {
|
||||||
const parts = currentInput.value.split(/[;;]/);
|
const parts = currentInput.value.split(/[;;]/);
|
||||||
|
|
@ -1444,8 +1466,8 @@ const removeCondition = (index) => {
|
||||||
preciseConditions.value.splice(preciseIndex, 1);
|
preciseConditions.value.splice(preciseIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新过滤数据
|
// 重新查询
|
||||||
applyPreciseFilter();
|
executeSearch({ page: 1 });
|
||||||
|
|
||||||
// 重置选中的条件索引
|
// 重置选中的条件索引
|
||||||
if (selectedConditionIndex.value === index) {
|
if (selectedConditionIndex.value === index) {
|
||||||
|
|
@ -1479,10 +1501,12 @@ const clearAll = () => {
|
||||||
const handleSizeChange = (val) => {
|
const handleSizeChange = (val) => {
|
||||||
pageSize.value = val;
|
pageSize.value = val;
|
||||||
currentPage.value = 1; // 重置为第一页
|
currentPage.value = 1; // 重置为第一页
|
||||||
|
executeSearch({ page: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCurrentChange = (val) => {
|
const handleCurrentChange = (val) => {
|
||||||
currentPage.value = val;
|
currentPage.value = val;
|
||||||
|
executeSearch({ page: val });
|
||||||
// 滚动到顶部
|
// 滚动到顶部
|
||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
};
|
};
|
||||||
|
|
@ -1532,19 +1556,18 @@ watch(
|
||||||
|
|
||||||
// 切换选择状态
|
// 切换选择状态
|
||||||
function toggleSelect(item) {
|
function toggleSelect(item) {
|
||||||
// 统计已选中的数量
|
|
||||||
const selectedCount = filteredData.value.filter((i) => i.selected).length;
|
|
||||||
item.selected = !item.selected;
|
item.selected = !item.selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开对比弹窗
|
// 打开对比弹窗
|
||||||
function onCompare() {
|
const onCompare = async () => {
|
||||||
if (selectedCompareList.value.length === 0) {
|
if (selectedCompareList.value.length === 0) {
|
||||||
ElMessage.warning("请先选择要对比的卡片");
|
ElMessage.warning("请先选择要对比的卡片");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await fetchCompareTable();
|
||||||
compareDialogVisible.value = true;
|
compareDialogVisible.value = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
// 确定按钮点击事件
|
// 确定按钮点击事件
|
||||||
function handleConfirm() {
|
function handleConfirm() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue