zhiqi/src/views/order/intention/search.vue

2284 lines
58 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.

<template>
<div class="step2-container">
<div class="main-row">
<div class="right-content">
<div>
<div class="search-box-container">
<div class="page-header">
<h1>品号查询</h1>
</div>
<!-- 主查询区域 -->
<div class="search-box">
<div
class="custom-input-wrapper"
:style="{ width: inputWidth + 'px' }"
@click="handleInputContainerClick"
>
<el-input
v-model="currentInput"
ref="searchInput"
placeholder="输入查询条件,例如: 品号:D311299000045-00005;..."
class="main-input"
@input="handleInput"
@keydown.enter="handleSearch"
@keydown.arrow-down="focusFirstSuggestion"
@keydown.escape="hideSuggestions"
@keydown="handleKeydown"
@click="handleInputClick"
@mouseup="handleInputMouseUp"
>
<!-- 输入框内添加话筒图标 -->
<template #append>
<el-icon
class="voice-icon"
@click.stop="showVoicePopup = true"
>
<Microphone />
</el-icon>
</template>
</el-input>
</div>
<!-- 下拉建议框 -->
<div
v-if="
showSuggestions &&
(suggestions.length > 0 || possibleFields.length > 0)
"
class="suggestions-dropdown"
:style="{ width: inputWidth + 'px' }"
>
<!-- 可能的字段提示项 -->
<div
v-if="possibleFields.length > 0"
class="possible-fields-section"
>
<div class="possible-fields-header">
您输入的条件可能属于以下字段:
</div>
<div
v-for="(field, index) in possibleFields"
:key="'field-' + index"
class="possible-field-item"
:class="{ active: activePossibleFieldIndex === index }"
@click="selectPossibleField(field)"
@mouseenter="activePossibleFieldIndex = index"
@keydown.enter.stop="selectPossibleField(field)"
@keydown.arrow-down.stop="navigatePossibleFields('down')"
@keydown.arrow-up.stop="navigatePossibleFields('up')"
tabindex="0"
>
<span class="field-type">[字段]</span>
<span>{{ field.label }}</span>
<span class="field-hint">: </span>
</div>
</div>
<div
v-for="(item, index) in suggestions"
:key="index"
class="suggestion-item"
:class="{
active:
activeSuggestionIndex ===
index +
(possibleFields.length > 0
? 1 + possibleFields.length
: 0),
}"
@click="selectSuggestion(item)"
@mouseenter="
activeSuggestionIndex =
index +
(possibleFields.length > 0
? 1 + possibleFields.length
: 0)
"
@keydown.enter.stop="selectSuggestion(item)"
@keydown.arrow-down.stop="navigateSuggestions('down')"
@keydown.arrow-up.stop="navigateSuggestions('up')"
tabindex="0"
>
<span v-if="item.type === 'field'" class="field-type"
>[字段]</span
>
<span v-if="item.type === 'value'" class="value-type"
>[值]</span
>
<span>{{ item.label }}</span>
<span v-if="item.type === 'field'" class="field-hint"
>:
</span>
</div>
</div>
<!-- 查询按钮 -->
<el-button
type="primary"
class="search-button"
@click="handleSearch"
>
<el-icon><Search /></el-icon>
查询
</el-button>
</div>
<!-- 条件标签展示 -->
<div v-if="parsedConditions.length > 0" class="conditions-tags">
<el-tag
v-for="(cond, index) in parsedConditions"
:key="index"
closable
:type="cond.valid ? 'success' : 'danger'"
@close="removeCondition(index)"
@click="focusConditionTag(index)"
class="condition-tag"
>
<span v-if="cond.field" class="tag-field"
>{{ cond.fieldLabel }}:</span
>
<span
class="tag-value"
@click.stop="focusConditionTag(index)"
>{{ cond.value }}</span
>
<!-- 将编辑图标替换为"精准"文字,并添加点击事件 -->
<span
class="tag-precise"
@click.stop="addPreciseCondition(index)"
>精准</span
>
</el-tag>
<el-button link @click="clearAll" class="clear-button">
<el-icon><RefreshLeft /></el-icon>
清除所有
</el-button>
</div>
<!-- 精准查询参数展示区域 -->
<div v-if="preciseConditions.length > 0" class="precise-conditions">
<div class="precise-conditions-label">选择精准查询参数:</div>
<div class="precise-tags">
<el-tag
v-for="(cond, index) in preciseConditions"
:key="'precise-' + index"
closable
type="primary"
@close="removePreciseCondition(index)"
class="precise-tag"
>
<span v-if="cond.field" class="tag-field"
>{{ cond.fieldLabel }}:</span
>
<span class="tag-value">{{ cond.value }}</span>
</el-tag>
</div>
</div>
</div>
<!-- 查询结果区域 -->
<div class="results-section" v-if="showResults">
<div
v-if="currentPageData.length"
class="result-card-list-wrap"
style="position: relative"
>
<!-- 参数对比按钮 - 随滚动条移动 -->
<el-button
v-if="selectedCompareList.length > 0"
type="warning"
class="compare-btn-float"
@click="onCompare"
circle
>
参数<br />对比
</el-button>
<div class="result-card-list">
<el-card
v-for="item in currentPageData"
:key="item.productNumber"
class="result-card"
:class="{ active: item.selected }"
@click="toggleSelect(item)"
>
<div class="result-card-info">
<div class="result-card-name" :title="item.name">
{{ item.name }}
</div>
<div
class="result-card-parameter"
:title="`品号:${item.productNumber}`"
>
<span>品号:</span>{{ item.productNumber }}
</div>
<div
class="result-card-parameter"
:title="`品号-规格型号:${item.specificationModel}`"
>
<span>品号-规格型号: </span>{{ item.specificationModel }}
</div>
<div
class="result-card-parameter text-truncate"
:title="`车型:${item.carModel}`"
>
<span>车型:</span> {{ item.carModel }}
</div>
</div>
</el-card>
</div>
</div>
<div v-if="currentPageData.length === 0" class="no-results">
<el-empty description="没有匹配的结果"></el-empty>
</div>
<!-- 分页控件 -->
<div class="pagination-container" v-if="showResults">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[8, 16, 32]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalItems"
></el-pagination>
<!-- 确定按钮 -->
<el-button
type="primary"
class="confirm-button"
@click="handleConfirm"
:disabled="selectedCount !== 1"
>
确定
</el-button>
</div>
</div>
<!-- 参数对比弹窗 -->
<el-dialog
v-model="compareDialogVisible"
title="参数对比"
width="1200px"
top="40px"
:close-on-click-modal="false"
>
<!-- 仅看不同项按钮 -->
<div
class="compare-filter-options"
style="margin: -45px 80px 16px; text-align: left"
>
<el-button
type="primary"
@click="showOnlyDifferences = !showOnlyDifferences"
>
{{ showOnlyDifferences ? "显示全部" : "仅看不同项" }}
</el-button>
</div>
<!-- 表格对比区,添加横向滚动 -->
<div class="compare-table-wrap">
<el-table
:data="filteredCompareTableData"
border
style="width: 100%; min-width: 600px"
>
<el-table-column
prop="param"
label="参数"
width="180"
class-name="param-col"
align="center"
header-align="center"
fixed="left"
/>
<el-table-column
v-for="item in sortedCompareList"
:key="item.productNumber"
:label="item.productNumber"
:prop="item.productNumber"
align="center"
header-align="center"
min-width="300"
>
<template #header="{ column }">
<div
style="
display: flex;
align-items: center;
justify-content: center;
"
>
<span v-if="item.isLatest" class="new-tag">
<img
src="/images/cars/new.png"
alt="最新品号"
class="new-tag-img"
/>
</span>
<div
style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
"
>
<span>
{{ column.label }}
</span>
<span style="font-size: 12px; color: #666">{{
sortedCompareList.find(
(i) => i.productNumber === column.label
).name
}}</span>
</div>
</div>
</template>
<!-- 单元格内容,不同的值标红 -->
<template #default="scope">
<span
:class="{
'different-value': isValueDifferent(scope.row.param),
}"
>
{{ scope.row[scope.column.property] || "-" }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
<!-- 选择确认提示弹窗 -->
<el-dialog
v-model="confirmDialogVisible"
title="选择确认"
width="400px"
:show-close="false"
>
<div class="confirm-message">
您选择的品号为:{{ selectedProductNumber }}
</div>
<template #footer>
<el-button type="primary" @click="confirmDialogVisible = false">
确定
</el-button>
</template>
</el-dialog>
<!-- 语音输入弹窗 -->
<el-dialog
v-model="showVoicePopup"
title="语音输入"
width="400px"
:show-close="true"
>
<div class="voice-popup-content">
正式环境中提供语音输入检索功能demo中仅做示意。
</div>
<template #footer>
<el-button type="primary" @click="showVoicePopup = false">
我知道了
</el-button>
</template>
</el-dialog>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, nextTick } from "vue";
import {
paramsToCompare,
fieldMap,
allFields,
mockData,
fieldValueMap,
} from "@/data/step2MockData";
import { ElMessage, ElEmpty, ElDialog } from "element-plus";
import { Search, Edit, RefreshLeft, Microphone } from "@element-plus/icons-vue";
// 接收props和定义emit
const props = defineProps({
form: Object,
selectedCarType: String,
});
const emit = defineEmits(["update:form"]);
// 双向绑定
const selectedCarTypeProxy = computed({
get() {
return props.form?.selectedCarType ?? "";
},
set(val) {
if (props.form) {
props.form.selectedCarType = val;
emit("update:form", props.form);
}
},
});
// 查询相关变量
const currentInput = ref("");
const searchInput = ref(null);
const inputWidth = ref(600);
const selectedConditionIndex = ref(-1);
// 建议相关
const suggestions = ref([]);
const showSuggestions = ref(false);
const activeSuggestionIndex = ref(-1);
const possibleFields = ref([]);
const activePossibleFieldIndex = ref(-1);
// 语音输入弹窗控制
const showVoicePopup = ref(false);
// 解析后的条件和查询结果
const parsedConditions = ref([]);
// 精准查询条件
const preciseConditions = ref([]);
const showResults = ref(false);
const filteredData = ref([]);
// 分页相关变量
const currentPage = ref(1);
const pageSize = ref(8);
const totalItems = ref(0);
// 计算当前页数据
const currentPageData = computed(() => {
const startIndex = (currentPage.value - 1) * pageSize.value;
return filteredData.value.slice(startIndex, startIndex + pageSize.value);
});
// 计算选中的商品数量
const selectedCount = computed(() => {
return filteredData.value.filter((item) => item.selected).length;
});
// 选中的商品品号
const selectedProductNumber = computed(() => {
const selected = filteredData.value.find((item) => item.selected);
return selected ? selected.productNumber : "";
});
// 对比相关
const compareDialogVisible = ref(false);
const selectedCompareList = computed(() =>
filteredData.value.filter((i) => i.selected)
);
// 仅显示不同项的开关
const showOnlyDifferences = ref(false);
// 存储参数差异信息的映射
const parameterDifferences = ref({});
// 确认弹窗
const confirmDialogVisible = ref(false);
// 计算排序后的对比列表(最新品号在前)
const sortedCompareList = computed(() => {
// 复制数组以避免修改原始数据
const list = [...selectedCompareList.value];
// 排序:根据品号结尾的五位数从大到小排序
list.sort((a, b) => {
// 提取品号结尾的五位数
const aMatch = a.productNumber.match(/-(\d{5})$/);
const bMatch = b.productNumber.match(/-(\d{5})$/);
const aNum = aMatch ? parseInt(aMatch[1], 10) : 0;
const bNum = bMatch ? parseInt(bMatch[1], 10) : 0;
return bNum - aNum;
});
// 标记最新的品号(排序后的第一个)
if (list.length > 0) {
list.forEach((item, index) => {
item.isLatest = index === 0;
});
}
return list;
});
// 生成对比表格数据并分析差异
const compareTableData = computed(() => {
if (sortedCompareList.value.length === 0) {
parameterDifferences.value = {};
return [];
}
// 重置差异映射
const differences = {};
// 获取当前时间和昨天的日期
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
// 格式化日期为 yyyy-MM-dd
const formatDate = (date) => {
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;
return result;
});
// 过滤后的对比表格数据(根据"仅看不同项"选项)
const filteredCompareTableData = computed(() => {
if (!showOnlyDifferences.value) {
return compareTableData.value;
}
// 只返回有差异的参数项
return compareTableData.value.filter(
(row) => parameterDifferences.value[row.param]
);
});
// 判断某个参数是否有差异(只要有差异就标红)
const isValueDifferent = (param) => {
// 如果参数有任何差异,就标红
return parameterDifferences.value[param];
};
// 收集所有可能的值用于匹配
const allValues = ref([]);
// 初始化所有可能的值
const initializeValues = () => {
const values = [];
allFields.forEach((field) => {
const uniqueValues = [
...new Set(
mockData.map((item) => item[field.key] ?? "") // 将null转换为空字符串
),
];
uniqueValues.forEach((value) => {
// 过滤空值,避免空提示
if (value !== "") {
values.push({
value: String(value),
fieldKey: field.key,
fieldLabel: field.label,
weight: fieldValueMap[field.key]?.includes(String(value)) ? 2 : 1,
});
}
});
});
allValues.value = values;
};
const getCurrentInputPart = () => {
const parts = currentInput.value.split(/[;]/);
return parts[parts.length - 1].trim();
};
const handleKeydown = (e) => {
// 检查是否按下了分号键
if (e.key === ";" || e.key === "" || e.keyCode === 186) {
handleSemicolon(e);
}
};
// 处理分号输入
const handleSemicolon = (e) => {
// 如果是空值或最后一个字符已经是分号,则不处理
if (!currentInput.value.trim() || currentInput.value.endsWith(";")) {
e.preventDefault();
return;
}
// 延迟处理,确保分号已添加到输入框
setTimeout(() => {
// 触发输入事件以更新建议
handleInput(currentInput.value);
// 分号输入后触发实时查询
triggerRealTimeSearch();
}, 0);
};
// 查找光标所在的条件位置
const findCursorConditionIndex = () => {
if (!searchInput.value) return -1;
const inputEl = searchInput.value.$el.querySelector("input");
if (!inputEl) return -1;
const cursorPos = inputEl.selectionStart;
const inputValue = currentInput.value;
// 修复:处理中英文分号的正则表达式
const semicolons = [];
const semicolonRegex = /[;]/g;
let match;
// 收集所有分号的位置
while ((match = semicolonRegex.exec(inputValue)) !== null) {
semicolons.push(match.index);
}
// 确定光标所在的条件索引
for (let i = 0; i < semicolons.length; i++) {
if (cursorPos <= semicolons[i]) {
return i;
}
}
// 如果光标在最后一个分号之后,返回最后一个条件索引
return semicolons.length;
};
// 处理输入框容器点击
const handleInputContainerClick = (e) => {
// 不需要处理箭头相关逻辑了
};
// 处理输入框点击事件 - 支持点击已输入值显示下拉
const handleInputClick = () => {
const index = findCursorConditionIndex();
const parts = currentInput.value.split(";");
if (index >= 0 && index < parts.length) {
const part = parts[index].trim();
if (part) {
selectedConditionIndex.value = index;
// 触发该条件的建议显示
triggerConditionSuggestions(part);
}
}
};
// 处理输入框鼠标抬起事件 - 支持选择文本后显示下拉
const handleInputMouseUp = () => {
handleInputClick();
};
// 聚焦到指定的条件标签 - 修复问题2
const focusConditionTag = (index) => {
const parts = currentInput.value.split(/[;]/);
if (index < 0 || index >= parts.length) return;
// 将光标定位到该条件
selectedConditionIndex.value = index;
const inputEl = searchInput.value?.$el.querySelector("input");
if (!inputEl) return;
// 计算该条件在输入框中的位置
let cursorPos = 0;
for (let i = 0; i < index; i++) {
cursorPos += parts[i].length + 1; // +1 是分号的长度
}
// 设置光标位置到该条件的末尾
setTimeout(() => {
inputEl.focus();
inputEl.setSelectionRange(
cursorPos + parts[index].length,
cursorPos + parts[index].length
);
// 触发该条件的建议显示
triggerConditionSuggestions(parts[index].trim());
}, 0);
};
// 触发特定条件的建议显示
const triggerConditionSuggestions = (conditionText) => {
if (!conditionText) {
suggestions.value = [];
possibleFields.value = [];
return;
}
const colonIndex = conditionText.indexOf(":");
showSuggestions.value = true;
if (colonIndex > -1) {
// 已有字段和值,显示该字段的可能值
const fieldLabel = conditionText.substring(0, colonIndex).trim();
const valuePart = conditionText.substring(colonIndex + 1).trim();
const field = allFields.find((f) => f.label === fieldLabel);
if (field) {
// 显示该字段的所有可能值
const fieldValues = allValues.value
.filter((item) => item.fieldKey === field.key)
.map((item) => ({
type: "value",
label: item.value,
value: item.value,
fieldKey: item.fieldKey,
fieldLabel: item.fieldLabel,
weight: item.weight,
}));
// 按匹配度排序
fieldValues.sort((a, b) => {
const aMatch = a.label.toLowerCase().includes(valuePart.toLowerCase())
? 0
: 1;
const bMatch = b.label.toLowerCase().includes(valuePart.toLowerCase())
? 0
: 1;
return aMatch - bMatch || a.label.localeCompare(b.label);
});
suggestions.value = fieldValues.slice(0, 8);
possibleFields.value = []; // 已有字段时不显示可能的字段
}
} else {
// 只有值,没有字段,使用原有逻辑
handleInput(currentInput.value);
}
};
// 处理输入事件,实时生成建议
const handleInput = (value) => {
// 获取当前正在输入的条件部分(分号后面的部分)
const parts = value.split(/[;]/);
const currentPart = parts[parts.length - 1].trim();
// 检查是否在编辑已有条件
const cursorIndex = findCursorConditionIndex();
if (cursorIndex >= 0 && cursorIndex < parts.length - 1) {
// 正在编辑中间的条件,使用特殊处理
triggerConditionSuggestions(parts[cursorIndex].trim());
return;
}
// 始终显示建议框,除非输入为空
showSuggestions.value = value.trim() !== "";
// 如果当前部分为空,不显示具体建议
if (!currentPart) {
suggestions.value = [];
possibleFields.value = [];
return;
}
// 检查当前是否在输入字段还是值
const isEditingField = !currentPart.includes(":");
// 查找匹配的字段和值
let fieldSuggestions = [];
let valueSuggestions = [];
if (isEditingField) {
// 正在输入字段或值的开始部分,同时查找字段和值
fieldSuggestions = allFields
.filter((field) =>
field.label.toLowerCase().includes(currentPart.toLowerCase())
)
.map((field) => ({
type: "field",
label: field.label,
value: field.key,
weight: 3, // 字段建议权重更高
}));
// 查找可能匹配的值,并检查是否有强关联的字段
const potentialValues = allValues.value
.filter((item) =>
String(item.value).toLowerCase().includes(currentPart.toLowerCase())
)
.map((item) => ({
type: "value",
label: item.value,
value: item.value,
fieldKey: item.fieldKey,
fieldLabel: item.fieldLabel,
weight: item.weight,
}));
// 检查是否有值强烈匹配某个字段
const valueFieldCounts = {};
potentialValues.forEach((val) => {
if (!valueFieldCounts[val.fieldKey]) {
valueFieldCounts[val.fieldKey] = {
count: 0,
label: val.fieldLabel,
field: allFields.find((f) => f.key === val.fieldKey),
};
}
valueFieldCounts[val.fieldKey].count += val.weight;
});
// 提取并排序可能的字段
possibleFields.value = Object.values(valueFieldCounts)
.filter((item) => item.count > 1) // 只显示有足够匹配度的字段
.sort((a, b) => b.count - a.count) // 按匹配度排序
.map((item) => item.field);
valueSuggestions = potentialValues;
} else {
// 已经有字段,只查找值
const colonIndex = currentPart.indexOf(":");
const fieldLabel = currentPart.substring(0, colonIndex).trim();
const valuePart = currentPart.substring(colonIndex + 1).trim();
// 查找对应的字段
const field = allFields.find((f) => f.label === fieldLabel);
if (field) {
// 只查找该字段的值
valueSuggestions = allValues.value
.filter(
(item) =>
item.fieldKey === field.key &&
String(item.value).toLowerCase().includes(valuePart.toLowerCase())
)
.map((item) => ({
type: "value",
label: item.value,
value: item.value,
fieldKey: item.fieldKey,
fieldLabel: item.fieldLabel,
weight: item.weight,
}));
} else {
// 字段无效,查找所有值
valueSuggestions = allValues.value
.filter((item) =>
String(item.value).toLowerCase().includes(currentPart.toLowerCase())
)
.map((item) => ({
type: "value",
label: item.value,
value: item.value,
fieldKey: item.fieldKey,
fieldLabel: item.fieldLabel,
weight: item.weight,
}));
}
possibleFields.value = []; // 已有字段时不显示可能的字段
}
// 合并建议并按相关性排序
let allSuggestions = [...fieldSuggestions, ...valueSuggestions];
// 根据权重和匹配程度排序
allSuggestions.sort((a, b) => {
// 权重高的排在前面
if (b.weight !== a.weight) {
return b.weight - a.weight;
}
// 更精确的匹配排在前面
const aMatch = a.label.toLowerCase().indexOf(currentPart.toLowerCase());
const bMatch = b.label.toLowerCase().indexOf(currentPart.toLowerCase());
if (aMatch !== bMatch) {
return aMatch - bMatch;
}
// 短的排在前面
return a.label.length - b.label.length;
});
// 去重并限制数量
const uniqueSuggestions = [];
const seenLabels = new Set();
allSuggestions.forEach((suggestion) => {
if (!seenLabels.has(suggestion.label)) {
seenLabels.add(suggestion.label);
uniqueSuggestions.push(suggestion);
}
});
suggestions.value = uniqueSuggestions.slice(0, 8);
// 重置激活的建议索引
activeSuggestionIndex.value = -1;
activePossibleFieldIndex.value = -1;
};
// 选择可能的字段 - 修复问题4
const selectPossibleField = (field) => {
// 始终在当前条件上操作,不修改其他条件
const parts = currentInput.value.split(/[;]/);
let targetIndex = parts.length - 1;
// 如果最后一个条件为空,使用前一个
if (parts.length > 0 && parts[targetIndex].trim() === "") {
targetIndex = Math.max(0, parts.length - 2);
}
// 获取当前部分的值
let targetPart = parts[targetIndex] || "";
// 检查是否已有字段
if (targetPart.includes(":")) {
// 已有字段,添加新条件
const newPart = `${field.label}:`;
parts.push(newPart);
targetIndex = parts.length - 1;
} else {
// 没有字段,在当前值前添加字段名和冒号
parts[targetIndex] = `${field.label}:${targetPart}`;
}
// 重新拼接条件,清理多余分号
currentInput.value = parts.join(";").replace(/;;+/g, ";").trim();
// 保持建议框显示,允许继续编辑
setTimeout(() => {
handleInput(currentInput.value);
// 选择字段后触发实时查询
triggerRealTimeSearch();
}, 0);
// 聚焦输入框并将光标定位到字段后的适当位置
searchInput.value.focus();
const inputEl = searchInput.value.$el.querySelector("input");
if (inputEl) {
// 计算光标应该在的位置(字段名+冒号之后)
let cursorPos = 0;
for (let i = 0; i < targetIndex; i++) {
cursorPos += parts[i].length + 1; // +1 是分号的长度
}
cursorPos += field.label.length + 1; // 字段名长度 + 冒号
inputEl.setSelectionRange(cursorPos, cursorPos);
}
};
// 导航可能的字段
const navigatePossibleFields = (direction) => {
if (!showSuggestions.value || possibleFields.value.length === 0) return;
if (direction === "down") {
activePossibleFieldIndex.value =
(activePossibleFieldIndex.value + 1) % possibleFields.value.length;
} else {
activePossibleFieldIndex.value =
(activePossibleFieldIndex.value - 1 + possibleFields.value.length) %
possibleFields.value.length;
}
// 滚动到激活的字段
const fieldElements = document.querySelectorAll(".possible-field-item");
if (fieldElements[activePossibleFieldIndex.value]) {
fieldElements[activePossibleFieldIndex.value].scrollIntoView({
block: "nearest",
});
}
};
// 提取运算符
const getOperator = (conditionValue) => {
const operators = [">=", "<=", "!=", ">", "<", "="];
return operators.find((op) => conditionValue.startsWith(op));
};
// 选择建议项
// 修改selectSuggestion函数在选择值后自动添加分号
const selectSuggestion = (item) => {
// 使用光标位置确定当前正在编辑的条件索引
const targetIndex = findCursorConditionIndex();
if (targetIndex === -1) return;
const parts = currentInput.value.split(/[;]/);
let targetPart = parts[targetIndex] || "";
if (item.type === "field") {
// 字段建议:直接替换为「字段:」格式(不自动加分号,等待输入值)
parts[targetIndex] = `${item.label}:`;
} else {
// 值建议处理:选择值后自动添加分号
const colonIndex = targetPart.indexOf(":");
const operator = getOperator(targetPart.split(":").pop() || "");
if (colonIndex > -1) {
// 已有字段部分
const fieldPart = targetPart.substring(0, colonIndex + 1);
if (operator) {
// 带有运算符的情况,保留运算符结构
const operatorIndex = targetPart.indexOf(operator, colonIndex);
const prefix = targetPart.substring(0, operatorIndex + operator.length);
parts[targetIndex] = `${prefix}${item.label}`;
} else {
// 普通值替换
parts[targetIndex] = `${fieldPart}${item.label}`;
}
} else {
// 无字段时直接处理值
parts[targetIndex] = item.label;
}
// 核心:值选择后自动添加分号(避免重复添加)
const lastChar = parts[targetIndex].slice(-1);
if (![",", ";", ""].includes(lastChar)) {
parts[targetIndex] += ";"; // 使用英文分号统一分隔
}
}
// 重新拼接条件,清理多余分号
currentInput.value = parts.join(";").replace(/;;+/g, ";").trim();
// 保持建议框显示,允许继续编辑
setTimeout(() => {
handleInput(currentInput.value);
// 选择建议项后触发实时查询
triggerRealTimeSearch();
}, 0);
// 聚焦输入框并将光标定位到当前条件末尾(分号后)
searchInput.value.focus();
const inputEl = searchInput.value.$el.querySelector("input");
if (inputEl) {
// 计算光标应该在的位置(分号后面)
let cursorPos = 0;
for (let i = 0; i < targetIndex; i++) {
cursorPos += parts[i].length + 1; // +1 是分号的长度
}
cursorPos += parts[targetIndex].length; // 光标定位到分号后
inputEl.setSelectionRange(cursorPos, cursorPos);
}
};
// 导航建议项(上下箭头)
const navigateSuggestions = (direction) => {
if (!showSuggestions.value || suggestions.value.length === 0) return;
const totalPossibleFields = possibleFields.value.length;
const totalItems =
suggestions.value.length +
(totalPossibleFields > 0 ? 1 + totalPossibleFields : 0);
if (direction === "down") {
activeSuggestionIndex.value =
(activeSuggestionIndex.value + 1) % totalItems;
} else {
activeSuggestionIndex.value =
(activeSuggestionIndex.value - 1 + totalItems) % totalItems;
}
// 如果有可选字段,处理导航逻辑
if (totalPossibleFields > 0) {
// 导航进入字段区域
if (activeSuggestionIndex.value <= totalPossibleFields) {
activePossibleFieldIndex.value = activeSuggestionIndex.value - 1;
activeSuggestionIndex.value = -1;
return;
} else {
activePossibleFieldIndex.value = -1;
activeSuggestionIndex.value -= 1 + totalPossibleFields;
}
}
// 滚动到激活的建议项
const suggestionElements = document.querySelectorAll(".suggestion-item");
if (suggestionElements[activeSuggestionIndex.value]) {
suggestionElements[activeSuggestionIndex.value].scrollIntoView({
block: "nearest",
});
}
};
// 聚焦第一个建议项
const focusFirstSuggestion = () => {
if (showSuggestions.value) {
if (possibleFields.value.length > 0) {
// 先聚焦到可能的字段
activePossibleFieldIndex.value = 0;
const firstField = document.querySelector(".possible-field-item");
if (firstField) firstField.focus();
} else if (suggestions.value.length > 0) {
// 再聚焦到建议项
activeSuggestionIndex.value = 0;
const firstElement = document.querySelector(".suggestion-item");
if (firstElement) firstElement.focus();
}
}
};
// 隐藏建议
const hideSuggestions = () => {
showSuggestions.value = false;
possibleFields.value = [];
};
// 解析查询条件
const parseConditions = (input) => {
const conditions = [];
const parts = input
.split(/[;]/) // 同时匹配中英文分号
.map((part) => part.trim())
.filter((part) => part);
parts.forEach((part) => {
// 检查是否包含字段名(冒号分隔)
const colonIndex = part.indexOf(":");
if (colonIndex > 0) {
const fieldLabel = part.substring(0, colonIndex).trim();
const valuePart = part.substring(colonIndex + 1).trim();
// 查找对应的字段key
const field = allFields.find((f) => f.label === fieldLabel);
conditions.push({
fieldLabel,
field: field ? field.key : null,
value: valuePart,
valid: !!field,
});
} else {
// 没有字段名,默认为值查询
conditions.push({
fieldLabel: null,
field: null,
value: part,
valid: true,
});
}
});
return conditions;
};
// 添加精准查询条件
const addPreciseCondition = (index) => {
const condition = parsedConditions.value[index];
// 检查是否已经添加到精准查询条件中
const exists = preciseConditions.value.some(
(cond) => cond.field === condition.field && cond.value === condition.value
);
if (!exists) {
preciseConditions.value.push({ ...condition, originalIndex: index });
// 重新过滤数据,应用精准查询
applyPreciseFilter();
}
};
// 移除精准查询条件
const removePreciseCondition = (index) => {
preciseConditions.value.splice(index, 1);
// 重新过滤数据
applyPreciseFilter();
};
// 处理查询
const handleSearch = () => {
// 解析条件
parsedConditions.value = parseConditions(currentInput.value);
// 清空精准查询条件
preciseConditions.value = [];
// 应用过滤
applyPreciseFilter();
// 显示结果
showResults.value = true;
// 隐藏建议
showSuggestions.value = false;
possibleFields.value = [];
};
// 触发实时查询
const triggerRealTimeSearch = () => {
// 只有当有有效条件时才触发实时查询
if (currentInput.value.trim()) {
parsedConditions.value = parseConditions(currentInput.value);
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 parts = currentInput.value.split(/[;]/);
parts.splice(index, 1);
currentInput.value = parts
.join(";")
.replace(/[;]+/g, ";")
.trim();
parsedConditions.value = parseConditions(currentInput.value);
// 检查是否有精准查询条件引用了这个索引,如果有则一并删除
const preciseIndex = preciseConditions.value.findIndex(
(cond) => cond.originalIndex === index
);
if (preciseIndex !== -1) {
preciseConditions.value.splice(preciseIndex, 1);
}
// 重新过滤数据
applyPreciseFilter();
// 重置选中的条件索引
if (selectedConditionIndex.value === index) {
selectedConditionIndex.value = -1;
}
};
// 清除所有查询条件和结果
const clearAll = () => {
currentInput.value = "";
parsedConditions.value = [];
preciseConditions.value = [];
filteredData.value = [];
totalItems.value = 0;
currentPage.value = 1;
showResults.value = false;
showSuggestions.value = false;
possibleFields.value = [];
selectedConditionIndex.value = -1;
if (searchInput.value) {
searchInput.value.focus();
}
handleSearch();
};
// 分页相关方法
const handleSizeChange = (val) => {
pageSize.value = val;
currentPage.value = 1; // 重置为第一页
};
const handleCurrentChange = (val) => {
currentPage.value = val;
// 滚动到顶部
window.scrollTo({ top: 0, behavior: "smooth" });
};
// 生命周期钩子
onMounted(() => {
initializeValues();
handleSearch();
updateInputWidth();
window.addEventListener("resize", updateInputWidth);
// 自动聚焦输入框
setTimeout(() => {
if (searchInput.value) {
searchInput.value.focus();
}
}, 100);
// 点击页面其他地方隐藏建议
document.addEventListener("click", (e) => {
if (
!e.target.closest(".search-box") &&
!e.target.closest(".suggestions-dropdown")
) {
showSuggestions.value = false;
}
});
});
// 监听输入框宽度变化
const updateInputWidth = () => {
if (searchInput.value) {
inputWidth.value = searchInput.value.$el.clientWidth;
}
};
// 监听输入框宽度变化
watch(
() => currentInput.value,
() => {
// 延迟更新确保DOM已更新
setTimeout(() => {
updateInputWidth();
}, 0);
}
);
// 切换选择状态
function toggleSelect(item) {
// 统计已选中的数量
const selectedCount = filteredData.value.filter((i) => i.selected).length;
item.selected = !item.selected;
}
// 打开对比弹窗
function onCompare() {
if (selectedCompareList.value.length === 0) {
ElMessage.warning("请先选择要对比的卡片");
return;
}
compareDialogVisible.value = true;
}
// 确定按钮点击事件
function handleConfirm() {
if (selectedCount.value === 1) {
confirmDialogVisible.value = true;
}
}
</script>
<style scoped lang="scss">
// 原有样式保持不变
.step2-container {
background: #fff;
padding: 24px;
border-radius: 8px;
}
.main-row {
display: flex;
align-items: flex-start;
}
.right-content {
flex: 1;
}
.step {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.step-left {
flex: 1;
margin-right: 10px;
}
.step-title {
font-size: 18px;
margin: 15px 0;
color: #333;
font-weight: 500;
&.active {
color: #2156f3;
}
.reference-model {
font-size: 14px;
color: #666;
margin-top: 8px;
padding-left: 20px;
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 12px;
height: 12px;
background: #2156f3;
border-radius: 50%;
}
}
}
.image-box.right-align {
display: flex;
justify-content: flex-end;
align-items: flex-start;
margin-bottom: 16px;
}
.table-title {
font-weight: bold;
margin: 16px 0 8px 0;
color: #333;
}
.tree-table-row {
display: flex;
flex-direction: column;
border: 1px solid #bbb;
border-radius: 4px;
padding: 12px;
background: #fff;
}
.header-title {
flex: 1;
padding: 10px;
border-radius: 4px;
}
.header-code,
.header-name {
font-size: 14px;
color: #333;
margin-bottom: 5px;
}
.tab-container {
width: 100%;
}
.tab-header {
background: #f5f7fa;
border-radius: 4px;
padding: 16px;
margin-bottom: 20px;
}
.tab-header-item {
display: flex;
gap: 20px;
align-items: center;
}
.tab-header-image {
width: 200px;
height: 150px;
flex-shrink: 0;
}
.tab-header-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
}
.tab-header-code,
.tab-header-name {
font-size: 14px;
color: #333;
line-height: 1.5;
}
.part-img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.model-form {
max-width: 900px;
margin-top: 16px;
}
.btn-row {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 20px;
}
.main-img {
width: 580px;
height: 260px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #eee;
}
.custom-tab-label {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 0;
}
.tab-label-image {
width: 80px;
height: 60px;
margin-bottom: 8px;
}
.tab-img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.tab-label-info {
text-align: center;
}
.tab-label-code {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.tab-label-name {
font-size: 14px;
color: #333;
font-weight: 500;
}
:deep(.el-tabs__item) {
height: auto !important;
padding: 0 20px !important;
}
:deep(.el-tabs__nav) {
display: flex;
gap: 20px;
}
:deep(.el-tabs__item.is-active) {
.tab-label-code,
.tab-label-name {
color: var(--el-color-primary);
}
}
.tree-node {
transition: background 0.2s;
padding: 8px 12px;
cursor: pointer;
border-radius: 4px;
margin-bottom: 4px;
display: flex;
align-items: center;
justify-content: space-between;
}
.tree-node.active {
color: #2156f3;
}
.tree-node:hover {
color: #2156f3;
}
.tab-content-flex {
display: flex;
min-height: 300px;
height: 400px;
max-height: 400px;
overflow: hidden;
}
.tab-left {
width: 260px;
border-right: 1px solid #eee;
padding-right: 16px;
overflow-y: auto;
height: 100%;
}
.tab-right {
flex: 1;
padding-left: 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-y: auto;
height: 100%;
}
.right-button {
margin: 15px 0 0 260px;
}
.detail-img {
width: 180px;
height: 120px;
object-fit: contain;
border: 1px solid #eee;
border-radius: 4px;
margin-bottom: 16px;
}
.replace-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.replace-item {
display: flex;
align-items: center;
gap: 16px;
padding: 8px;
border: 1px solid #eee;
border-radius: 4px;
cursor: pointer;
}
.replace-item.selected {
border-color: #2156f3;
}
.replace-img {
width: 80px;
height: 60px;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.no-data {
display: flex;
align-items: center;
justify-content: center;
min-height: 300px;
color: #999;
font-size: 18px;
}
:deep(.el-collapse-item.is-disabled .el-collapse-item__header) {
cursor: default;
color: #303133;
&:hover {
background-color: transparent;
}
}
:deep(.el-collapse-item.is-disabled .el-collapse-item__arrow) {
display: none;
}
:deep(.el-collapse-item__header) {
padding: 0;
height: auto;
}
:deep(.el-collapse-item__content) {
padding: 0;
padding-left: 16px;
}
:deep(.el-collapse-item__wrap) {
border-bottom: none;
}
.search-row {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.search-label {
font-size: 15px;
color: #666;
margin-right: 8px;
}
.car-type-text-list {
display: flex;
gap: 32px;
}
.car-type-text {
font-size: 16px;
color: #333;
cursor: pointer;
padding: 0 4px 4px 4px;
border-bottom: 2px solid transparent;
transition: color 0.2s, border 0.2s;
}
.car-type-text.active {
color: #2156f3;
border-bottom: 2px solid #2156f3;
font-weight: 500;
}
.search-divider {
border-bottom: 1px dashed #e5e5e5;
margin: 8px 0 16px 0;
}
.param-group-title {
font-size: 15px;
color: #333;
font-weight: 600;
margin: 12px 0 12px 0;
}
.param-form-row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 24px 32px;
margin-bottom: 0;
position: relative;
}
.param-form-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 0;
}
.param-label {
font-size: 14px;
color: #666;
min-width: 60px;
text-align: right;
}
.param-input {
width: 120px;
}
.param-form-btns {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
margin-left: auto;
min-width: 200px;
}
.param-btn {
min-width: 90px;
}
.result-card-list {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
.result-card {
width: 280px;
position: relative;
padding-top: 16px;
box-sizing: border-box;
transition: box-shadow 0.2s, transform 0.15s, border-color 0.2s;
cursor: pointer;
border: 1px solid #eee;
}
.result-card:hover {
box-shadow: 0 4px 16px rgba(33, 86, 243, 0.12);
transform: translateY(-4px) scale(1.03);
border-color: #2156f3;
}
.result-card.active {
box-shadow: 0 6px 20px rgba(33, 86, 243, 0.18);
border-color: #2156f3;
background: #f5f8ff;
transform: scale(1.04);
}
.result-card-info {
text-align: left;
}
.result-card-name {
font-size: 16px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.result-card-parameter {
font-size: 13px;
color: #888;
overflow: visible;
white-space: normal;
padding: 2px;
max-width: 300px;
word-break: break-word;
span {
color: #555;
}
}
.compare-dialog-cards {
display: flex;
gap: 32px;
margin-bottom: 24px;
}
.compare-card {
width: 220px;
text-align: center;
box-sizing: border-box;
}
.compare-card-img {
width: 100%;
height: 120px;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
margin-bottom: 12px;
}
.compare-card-info {
text-align: center;
}
.compare-card-name {
font-size: 15px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.compare-card-code {
font-size: 13px;
color: #888;
}
.compare-table-wrap {
margin-top: 16px;
overflow-x: auto; /* 允许横向滚动 */
max-width: 100%; /* 限制最大宽度 */
}
/* 参数对比按钮样式 - 随滚动条移动 */
.compare-btn-float {
position: fixed; /* 修改为fixed定位相对于视口固定 */
top: 260px;
right: 10px;
z-index: 100; /* 提高层级确保可见 */
width: 52px;
height: 52px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0;
border-radius: 8px;
box-shadow: 0 3px 15px rgba(33, 86, 243, 0.15);
transition: all 0.25s ease;
font-size: 13px;
line-height: 1.3;
}
.compare-btn-float:hover {
background: #fff3cd;
color: #d39e00;
box-shadow: 0 5px 20px rgba(255, 153, 0, 0.2);
transform: translateY(-2px);
}
.result-card-list-wrap {
position: relative;
padding-right: 20px;
}
.result-card-checkbox .el-checkbox__input.is-checked .el-checkbox__inner {
border-color: #2156f3 !important;
background-color: #2156f3 !important;
}
// 参数对比表格第一列背景色与表头一致
:deep(.el-table .param-col),
:deep(.el-table th.param-col) {
background: #f5f7fa !important;
}
// 固定列的样式优化
:deep(.el-table__fixed-left) {
box-shadow: 2px 0 6px rgba(0, 0, 0, 0.05);
z-index: 2;
}
.search-box-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.search-box {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
position: relative;
}
.custom-input-wrapper {
position: relative;
flex-grow: 1;
}
.main-input {
width: 100%;
min-width: 300px;
font-size: 16px;
}
// 语音图标样式
:deep(.voice-icon) {
cursor: pointer;
color: #666;
margin-right: 8px;
transition: color 0.2s;
&:hover {
color: #2156f3;
}
}
.search-button {
white-space: nowrap;
}
.suggestions-dropdown {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
background: white;
border: 1px solid #e4e7ed;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 5px;
max-height: 300px;
overflow-y: auto;
}
/* 可能的字段部分样式 */
.possible-fields-section {
border-bottom: 1px solid #e4e7ed;
padding-bottom: 5px;
margin-bottom: 5px;
}
.possible-fields-header {
padding: 8px 15px;
font-size: 14px;
color: #666;
background-color: #f5f7fa;
}
.possible-field-item {
padding: 10px 15px;
cursor: pointer;
transition: background-color 0.2s;
}
.possible-field-item:hover,
.possible-field-item.active {
background-color: #f0f7ff;
color: #1890ff;
}
.hint-item {
padding: 10px 15px;
background-color: #f0f7ff;
border-bottom: 1px solid #e4e7ed;
border-bottom: 1px solid #e4e7ed;
display: flex;
align-items: center;
gap: 8px;
color: #1890ff;
}
.hint-icon {
font-size: 14px;
}
.suggestion-item {
padding: 10px 15px;
cursor: pointer;
transition: background-color 0.2s;
}
.suggestion-item:hover,
.suggestion-item.active {
background-color: #f5f7fa;
}
.field-type,
.value-type {
display: inline-block;
width: 50px;
font-size: 12px;
color: #909399;
}
.field-hint {
color: #909399;
}
.conditions-tags {
margin: 15px 0;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.condition-tag {
cursor: pointer;
transition: all 0.2s;
padding-right: 60px !important; /* 增加右侧 padding 以容纳"精准"文字 */
position: relative;
}
.condition-tag:hover {
transform: translateY(-2px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tag-field {
font-weight: 500;
font-weight: 500;
}
.tag-value {
color: #409eff; /* 突出显示可更改的值 */
margin-right: 5px;
cursor: pointer;
text-decoration: underline;
text-underline-offset: 2px;
}
.tag-value:hover {
color: #1890ff;
}
/* 精准查询相关样式 */
.tag-precise {
position: absolute;
right: 20px; /* 为关闭按钮留出空间 */
top: 50%;
transform: translateY(-50%);
color: #409eff;
font-size: 12px;
cursor: pointer;
padding: 2px 4px;
border-radius: 2px;
background-color: rgba(64, 158, 255, 0.1);
transition: all 0.2s;
}
.tag-precise:hover {
background-color: rgba(64, 158, 255, 0.2);
color: #1890ff;
}
.precise-conditions {
margin: 15px 0;
}
.precise-conditions-label {
font-size: 14px;
color: #666;
margin-bottom: 8px;
font-weight: 500;
}
.precise-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.precise-tag {
cursor: default;
padding-right: 30px !important;
position: relative;
}
.results-section {
max-width: 1250px;
margin: 0 auto;
}
.no-results {
padding: 50px 0;
text-align: center;
}
.clear-button {
color: #666;
}
.compare-table-wrap {
margin-top: 16px;
max-height: 500px;
overflow-y: auto;
}
/* 分页和确定按钮容器 */
.pagination-container {
display: flex;
justify-content: end;
align-items: center;
margin-top: 20px;
padding: 10px 0;
border-top: 1px solid #eee;
}
/* 确定按钮样式 */
.confirm-button {
min-width: 100px;
margin-left: 15px;
}
/* 文字溢出处理样式 */
.text-truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
}
.text-truncate:hover {
/* 保持原样式不变但通过title属性显示完整内容 */
overflow: visible;
white-space: normal;
z-index: 10;
background-color: rgba(255, 255, 255, 0.95);
padding: 5px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
max-width: 300px;
word-break: break-word;
}
/* 确认弹窗样式 */
.confirm-message {
font-size: 16px;
text-align: center;
padding: 20px 0;
}
/* 不同参数值的样式 */
.different-value {
color: #ff4d4f; /* 红色 */
font-weight: 500;
}
/* 对比筛选选项样式 */
.compare-filter-options {
text-align: right;
padding-right: 10px;
}
/* 语音弹窗内容样式 */
.voice-popup-content {
font-size: 16px;
text-align: center;
padding: 30px 0;
line-height: 1.6;
}
.new-tag-img {
width: 35px;
height: 35px;
vertical-align: middle;
}
@media (max-width: 768px) {
.search-box {
flex-direction: column;
align-items: stretch;
}
.main-input {
min-width: auto;
}
.search-button {
width: 100%;
}
.result-card {
width: 100%;
}
.pagination-container {
flex-direction: column;
gap: 15px;
align-items: stretch;
}
.confirm-button {
width: 100%;
}
}
</style>