2025-10-27 17:01:35 +08:00

711 lines
20 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
style="display: flex;align-items: center;justify-content: center;width: 100%;max-width: 1400px;">
<!-- <img @click="goHome" class="logo-clickable" style="width: 200px;height: 60px;padding-right: 20px;"
src="https://www.opencomputing.cn/idfile?path=logo_ncmatch.png" alt=""> -->
<div
style="min-width:800px;display: flex;align-items: center;justify-content: space-between;">
<!-- 中间搜索区域 -->
<div class="search-section" style="position: relative;margin: 0 20px;">
<div class="search-bar" :class="{ 'has-results': showSearchResults && searchResults.length > 0 }">
<el-select class="mySelect" v-model="publish_type" placeholder="请选择" @change="handleTypeChange"
style="width: 75px;">
<el-option label="商品" value="1">
</el-option>
<el-option label="需求" value="2"></el-option>
</el-select>
<input v-model="keyword" type="text" class="search-input" placeholder="搜你想搜..." @input="handleInputChange"
@focus="handleInputFocus" @blur="handleInputBlur">
<button class="search-btn" @click="handleSearch">搜索</button>
</div>
<!-- 实时搜索结果 -->
<div v-if="showSearchResults && searchResults.length > 0" class="search-results" @click.stop>
<div v-for="result in searchResults" :key="result.id" class="search-result-item"
@click.stop.prevent="handleSearch(result)"
@mousedown.stop.prevent
@mouseup.stop.prevent
style="cursor: pointer; padding: 10px">
<span class="result-title" v-html="highlightKeyword(result)"></span>
</div>
</div>
<!-- 热搜关键词 -->
<div v-if="false" class="hot-search">
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword"
@click="handleHotKeywordClick(keyword)">
{{ keyword }}
</a>
</div>
</div>
<!-- position: absolute;right: -150px;top: 0px; -->
<span
v-if="false"
style="height: 44px;line-height: 44px;border-radius: 8px;border:1px solid #275AFF; color: #275AFF;font-size: 18px;font-weight: 500;padding: 0 20px;z-index: 10;display: flex;align-items: center;gap: 10px;">
<img style="width: 24px;height: 24px;" src="../mainPage/img/robot.svg" alt="">
NC AI</span>
</div>
</div>
</template>
<script>
import eventBus from '@/utils/eventBus'
import { reqSearch } from '@/api/ncmatch'
export default {
name: 'search',
data() {
return {
keyword: '',
publish_type: '1',
showSearchResults: false,
searchResults: [],
searchTimeout: null,
// 移除搜索历史记录
// 移除当前搜索状态
hotSearchKeywords: [
'昆仑芯-P800',
'天垓-150',
'4090',
'云服务器-GPU',
'人脸识别',
'SDWAN',
'互联网专线',
'DCI',
'AI专线',
'对象存储',
'智慧医疗',
'智慧客服',
'A800',
'A100',
'910B'
],
hotMenuList: [
{
id: "hot",
name: "热门推荐",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png")
},
{
id: "hlzx",
name: "互联网专线",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "SDWAN",
name: "SDWAN",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "DCI",
name: "DCI",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "AI",
name: "AI专线",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
}
],
}
},
// 移除计算属性
// 移除watch监听器
methods: {
goHome(event) {
if (this.$route.path != '/ncmatchHome/index') {
console.log('准备跳转到:', '/ncmatchHome/index');
this.$router.push('/ncmatchHome/index').then(() => {
console.log('跳转成功');
}).catch(err => {
console.error('跳转失败:', err);
});
} else {
console.log('已经在首页,无需跳转');
}
},
goSearch() {
// 通过路由传参控制搜索行为
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
const targetQuery = {
keyword: this.keyword,
publish_type: this.publish_type,
display_page: 'list', // 正式搜索使用 list
current_page: 1,
page_size: 8
};
console.log('准备跳转到搜索结果页面:', { currentRoute, targetQuery });
// 通过 display_page 参数判断是否在搜索结果页面
if (currentDisplayPage !== 'list') {
// 不在搜索结果页面,需要跳转
console.log('执行路由跳转');
this.$router.push({
path: '/ncmatchHome/search',
query: targetQuery
}).catch(err => {
// 忽略重复导航错误
if (err.name !== 'NavigationDuplicated') {
console.error('路由导航错误:', err);
} else {
console.log('路由重复导航,忽略错误');
}
});
} else {
// 如果已经在搜索结果页面,重新调用接口获取最新数据
console.log('已在搜索结果页面,重新调用接口获取最新数据');
this.performFormalSearch(this.keyword, this.publish_type);
}
},
// 处理输入变化
handleInputChange() {
// 清除之前的定时器
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
// 如果输入框为空,隐藏搜索结果
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
// 设置防抖300ms后执行搜索
this.searchTimeout = setTimeout(() => {
this.performRandomSearch()
}, 300)
},
// 执行搜索
performRandomSearch() {
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
console.log('执行联想搜索:', { keyword: this.keyword, type: this.publish_type });
// 调用统一的搜索方法
this.performSearch(this.keyword, this.publish_type);
},
// 统一的搜索方法
performSearch(keyword, type) {
console.log('执行联想搜索:', { keyword, type });
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'overview', // 联想搜索使用 overview
current_page: 1,
page_size: 15
}).then(res => {
this.searchResults = res.data.result
console.log('联想搜索结果:', res)
this.showSearchResults = true
// 联想搜索不需要更新URL参数只显示结果
console.log('联想搜索完成,显示结果');
}).catch(error => {
console.error('联想搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
// 正式搜索方法(用于在搜索路由下重新调用接口)
performFormalSearch(keyword, type) {
console.log('=== 执行正式搜索开始 ===');
console.log('搜索参数:', { keyword, type });
console.log('当前路由:', this.$route.path);
console.log('事件总线实例:', eventBus);
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'list', // 正式搜索使用 list
current_page: 1,
page_size: 8
}).then(res => {
console.log('正式搜索结果:', res);
// 准备发送事件数据
const eventData = {
type: type,
keyword: keyword,
data: res.data
};
console.log('准备发送事件数据:', eventData);
// 通过事件总线触发搜索事件,让兄弟组件处理结果
try {
eventBus.$emit('search', eventData);
console.log('事件总线事件发送成功');
} catch (error) {
console.error('事件总线事件发送失败:', error);
}
console.log('正式搜索完成,已触发搜索事件');
}).catch(error => {
console.error('正式搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
// 更新URL参数
updateURLParams(keyword, type) {
const query = { ...this.$route.query };
if (keyword) {
query.keyword = keyword;
} else {
delete query.keyword;
}
if (type) {
query.publish_type = type;
} else {
delete query.publish_type;
}
console.log('更新URL参数:', {
oldQuery: this.$route.query,
newQuery: query
});
// 直接更新URL参数不检查是否变化
this.$router.replace({
path: this.$route.path,
query: query
}).catch(err => {
// 忽略重复导航错误
if (err.name !== 'NavigationDuplicated') {
console.error('更新URL参数失败:', err);
} else {
console.log('URL参数更新重复忽略错误');
}
});
},
// 提取纯文本内容
extractPlainText(htmlText) {
// 创建临时DOM元素来提取纯文本
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlText;
return tempDiv.textContent || tempDiv.innerText || '';
},
// 处理类型选择变化
handleTypeChange(value) {
console.log('选择的类型:', value === '1' ? '商品' : '需求', value)
this.publish_type = value
// 如果当前有搜索结果,重新搜索
if (this.keyword.trim() && this.showSearchResults) {
this.performSearch(this.keyword, value);
}
},
// 处理搜索
handleSearch(result) {
// 立即阻止事件传播,确保方法能被正确执行
if (event) {
event.stopPropagation();
event.preventDefault();
}
console.log('=== handleSearch 开始执行 ===');
console.log('接收到的参数:', result);
console.log('当前路由路径:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
// 如果传入了result参数来自联想结果使用其product_name作为keyword
if (result && result.product_name) {
console.log('使用联想结果作为搜索关键词');
const plainText = this.extractPlainText(result.product_name);
this.keyword = plainText;
console.log('提取的纯文本:', plainText);
// 立即隐藏搜索结果,避免被全局点击事件干扰
this.showSearchResults = false;
this.searchResults = [];
} else {
console.log('使用输入框的关键词进行搜索');
// 如果没有传入result检查输入框是否有内容
if (!this.keyword.trim()) {
this.$message.warning('请输入搜索关键词');
return;
}
}
// 执行搜索逻辑
console.log('准备执行搜索,关键词:', this.keyword, '类型:', this.publish_type);
// 通过路由传参控制搜索行为
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
if (currentDisplayPage !== 'list') {
// 不在搜索结果页面,需要跳转
console.log('跳转到搜索结果页面');
this.goSearch()
} else {
// 已经在搜索结果页面,重新调用接口获取最新数据
console.log('已在搜索结果页面,重新调用接口获取最新数据');
// 更新URL参数
this.updateURLParams(this.keyword, this.publish_type);
// 重新调用搜索接口,获取最新数据
this.performFormalSearch(this.keyword, this.publish_type);
}
console.log('=== handleSearch 执行完成 ===');
},
// 处理热门关键词点击
handleHotKeywordClick(keyword) {
this.keyword = keyword
this.handleSearch()
},
// 处理输入框聚焦
handleInputFocus() {
// 聚焦时不立即显示联想框,只有当有搜索结果时才显示
if (this.keyword.trim() && this.searchResults.length > 0) {
this.showSearchResults = true;
}
},
// 处理输入框失焦
handleInputBlur() {
// 延迟隐藏搜索结果,给用户点击联想结果的机会
setTimeout(() => {
if (!this.$el.contains(document.activeElement)) {
this.hideSearchResults()
}
}, 100); // 延迟100ms
},
// 手动隐藏搜索结果
hideSearchResults() {
this.showSearchResults = false
this.searchResults = []
},
// 处理全局点击事件
handleGlobalClick(event) {
// 延迟处理,避免干扰其他点击事件
setTimeout(() => {
// 检查点击是否在搜索框内部或联想结果内部
if (this.$el && this.$el.contains(event.target)) {
console.log('点击在搜索组件内部,不隐藏联想结果');
return;
}
// 如果联想结果正在显示,则隐藏
if (this.showSearchResults) {
console.log('点击在搜索组件外部,隐藏联想结果');
this.hideSearchResults();
}
}, 100); // 增加延迟到100ms给其他事件处理器留出更多时间
},
// 高亮显示搜索关键词
highlightKeyword(item) {
console.log(item)
if (!this.keyword || !this.keyword.trim()) {
return item.product_name || item;
}
const keyword = this.keyword.trim();
// 使用 product_name 字段作为显示文本
const text = item.product_name || item;
console.log('高亮处理:', { keyword, text, item, textType: typeof text });
// 如果关键词为空,直接返回原文本
if (!keyword) {
return text;
}
// 如果文本为空或不是字符串,返回原文本
if (!text || typeof text !== 'string') {
console.warn('文本不是字符串类型:', text);
return text;
}
// 创建正则表达式,支持多个关键词(用空格分隔)
const keywords = keyword.split(/\s+/).filter(k => k.length > 0);
let highlightedText = text;
// 对每个关键词进行高亮处理
keywords.forEach(key => {
if (key.length > 0) {
const regex = new RegExp(`(${this.escapeRegExp(key)})`, 'gi');
highlightedText = highlightedText.replace(regex, '<span class="highlighted-keyword">$1</span>');
}
});
console.log('高亮结果:', highlightedText);
return highlightedText;
},
// 转义正则表达式特殊字符
escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
},
// 处理路由变化
handleRouteChange(to, from) {
console.log('处理路由变化:', { to, from, currentKeyword: this.keyword });
const keyword = to.query.keyword;
const publishType = to.query.publish_type;
if (keyword) {
// 直接更新搜索状态,不检查是否相同
console.log('更新搜索状态:', { keyword, publishType });
this.keyword = keyword;
this.publish_type = publishType || '1'; // 如果没有指定类型,默认为商品
// 确保组件状态正确初始化
this.showSearchResults = false;
this.searchResults = [];
// 重新绑定事件监听器,确保点击事件正常工作
this.$nextTick(() => {
this.reinitializeEventListeners();
});
// 不在这里执行搜索,只更新状态
// 搜索由用户点击搜索按钮或选择联想结果时触发
console.log('搜索状态已更新,等待用户操作');
} else {
// 如果没有搜索参数,清空状态
if (this.keyword || this.publish_type !== '1') {
console.log('清空搜索状态');
this.keyword = '';
this.publish_type = '1';
this.hideSearchResults();
}
}
},
// 重新初始化事件监听器
reinitializeEventListeners() {
console.log('重新初始化事件监听器');
// 移除旧的监听器
document.removeEventListener('click', this.handleGlobalClick);
// 重新添加新的监听器
document.addEventListener('click', this.handleGlobalClick);
// 确保组件状态正确
this.showSearchResults = false;
this.searchResults = [];
console.log('事件监听器重新初始化完成');
}
},
// 组件挂载后添加全局点击事件监听
mounted() {
console.log('=== 组件挂载开始 ===');
console.log('当前路由:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
document.addEventListener('click', this.handleGlobalClick)
console.log('全局点击事件监听器已添加');
// 检查URL参数如果有搜索参数则自动执行搜索
this.handleRouteChange(this.$route, null);
// 添加路由监听,确保子路由变化时能正确处理
this.$watch('$route', (to, from) => {
console.log('路由变化监听:', { to, from });
this.handleRouteChange(to, from);
}, { immediate: true, deep: true });
console.log('=== 组件挂载完成 ===');
},
// 组件销毁时清理定时器和事件监听器
beforeDestroy() {
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
document.removeEventListener('click', this.handleGlobalClick)
}
}
</script>
<style scoped lang="scss">
.search-section {
flex: 1;
min-width: 850px;
// padding-bottom: 10px;
.search-bar {
display: flex;
align-items: center;
background: white;
border: 2px solid #3f68d8;
border-radius: 4px;
overflow: hidden;
position: relative;
transition: border-radius 0.2s ease;
// 当显示搜索结果时,隐藏下边框并调整圆角
&.has-results {
border-bottom-color: transparent;
border-radius: 4px 4px 0 0;
}
.search-input {
flex: 1;
padding: 12px 15px;
border: none;
outline: none;
font-size: 14px;
padding-left: 0;
}
.search-btn {
background: #0864dd;
color: white;
border: none;
padding: 12px 20px;
cursor: pointer;
font-size: 14px;
&:hover {
background: #173ad4;
}
}
}
// 搜索结果样式
.search-results {
position: absolute;
top: 42px;
left: 0;
right: 0;
background: white;
border: 2px solid #3f68d8;
border-top: none;
border-radius: 0 0 4px 4px;
z-index: 99999;
max-height: 300px;
overflow-y: auto;
.search-result-item {
padding: 12px 15px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background-color: #f5f7fa;
* {
color: #0864dd!important;
}
}
&:last-child {
border-bottom: none;
}
.result-title {
font-size: 14px;
color: #333;
}
.result-type {
font-size: 12px;
color: #909399;
padding: 2px 8px;
background: #f0f2f5;
border-radius: 10px;
}
}
}
.hot-search {
margin-top: 8px;
display: flex;
gap: 15px;
flex-wrap: wrap;
.search-label {
font-size: 14px;
color: #666;
margin-right: 10px;
}
.hot-keyword {
&:nth-child(-n+3) {
color: #e1251b;
}
color: #666;
text-decoration: none;
font-size: 12px;
&:hover {
color: #e1251b;
}
}
}
}
::v-deep .mySelect {
input {
border: none;
}
}
::v-deep .highlighted-keyword {
font-weight: bold;
color: #0864dd;
}
.logo-clickable {
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.8;
}
}
@media (max-width: 768px) {
.search-section {
width: 100%;
}
}
</style>