2026-04-27 10:27:48 +08:00

1575 lines
42 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="top-nav">
<!-- 顶部容器 -->
<div id="topContainer" class="container">
<!-- 统一显示logo和导航 -->
<div class="logo">
<!-- Logo图片点击跳转首页 - 使用与goHome相同的逻辑 -->
<img v-if="JSON.stringify(logoInfoNew) !== '{}'" @click="goHome" style="cursor:pointer;"
class="logoImg" :src="(logoInfoNew.home && logoInfoNew.home.logoImg) || ''" alt="">
<!-- 主导航菜单 -->
<nav class="main-nav">
<div class="nav-list">
<!-- 首页 -->
<p :class="{ active: isActiveHome }">
<!-- 动态判断域名跳转首页 -->
<a @click="goHome">首页</a>
</p>
<!-- 产品与服务:鼠标移入显示子菜单 -->
<p @mouseleave="sildeOut" @mouseenter="sildeIn(product_service)">
<a>产品与服务</a>
</p>
<!-- 供需广场 -->
<p :class="{ active: $route.path.includes('/supply') }">
<a @click="$router.push('/ncmatchHome/supplyAndDemandSquare')">供需广场</a>
</p>
<!-- 关于我们 -->
<p :class="{ active: $route.path.includes('/about') }" @mouseleave="sildeOut"
@click="$router.push('/homePage/about')">
<a>关于我们</a>
</p>
</div>
</nav>
</div>
<!-- 用户区域:登录/注册按钮和用户下拉菜单 -->
<div class="user-area">
<!-- AI -->
<i class="iconfont icon-AIzhushou functions" @click="handleAIClick"></i>
<!-- 客服 -->
<i class="iconfont icon-kefu functions" @click="handleServiceClick"></i>
<!-- 控制台按钮(已登录) -->
<a @click="goB" v-if="loginState" class="login-btn">控制台</a>
<!-- 消息 -->
<i class="iconfont icon-xiaoxi functions" @click="handleMessageClick"></i>
<!-- 登录按钮(未登录) -->
<a @click="$router.push({
path: '/login',
query: {
fromPath: 'homePage'
}
})" v-if="!loginState" class="login-btn">登录</a>
<!-- 注册按钮(未登录) -->
<a @click="$router.push('/registrationPage')" v-if="!loginState" class="register-btn">立即注册</a>
<!-- 用户下拉菜单(已登录) -->
<el-dropdown v-if="loginState" @visible-change="handleShow"
class="avatar-container right-menu-item hover-effect nick-name-style" style="margin-right: 0" trigger="click">
<!-- 头像区域:添加点击事件 -->
<div class="avatar-wrapper" @click="goHome">
<i
style="font-size: 18px; color: #262626;font-weight: 400;cursor: pointer;display: flex;justify-content: flex-end;align-items: center">
<div class="imgUser"><img src="../../../../assets/image/user.png" alt=""></div>
{{ nick_name }}
<i id="resverIcon" class="el-icon-arrow-up el-icon--right resverIcon"></i>
</i>
</div>
<!-- 用户信息下拉菜单内容 -->
<el-dropdown-menu slot="dropdown" style="width: 230px;font-size: 16px;" divided>
<div style="display: flex;flex-direction: column;padding:15px 15px;background-color: #f0f2f5">
<span style="font-weight: bold;font-size: 20px;margin-bottom: 5px;color: #333333">{{ nick_name }}</span>
<span style="font-size: 12px;color:#666;display: flex;justify-content:flex-start;align-items: center"> ID:
<span ref="contentToCopy">
{{
userId
}}
</span>
<svg @click="copyBtn" class="copy-btn" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"
width="12" height="12" style="fill: #1019ab;">
<path
d="M394.666667 106.666667h448a74.666667 74.666667 0 0 1 74.666666 74.666666v448a74.666667 74.666667 0 0 1-74.666666 74.666667H394.666667a74.666667 74.666667 0 0 1-74.666667-74.666667V181.333333a74.666667 74.666667 0 0 1 74.666667-74.666666z m0 64a10.666667 10.666667 0 0 0-10.666667 10.666666v448a10.666667 10.666667 0 0 0 10.666667 10.666667h448a10.666667 10.666667 0 0 0 10.666666-10.666667V181.333333a10.666667 10.666667 0 0 0-10.666666-10.666666H394.666667z m245.333333 597.333333a32 32 0 0 1 64 0v74.666667a74.666667 74.666667 0 0 1-74.666667 74.666666H181.333333a74.666667 74.666667 0 0 1-74.666666-74.666666V394.666667a74.666667 74.666667 0 0 1 74.666666-74.666667h74.666667a32 32 0 0 1 0 64h-74.666667a10.666667 10.666667 0 0 0-10.666666 10.666667v448a10.666667 10.666667 0 0 0 10.666666 10.666666h448a10.666667 10.666667 0 0 0 10.666667-10.666666v-74.666667z"
p-id="1521"></path>
</svg>
</span>
</div>
<el-divider style="margin: 0!important;"></el-divider>
<div v-if="role.includes('客户')"
style="padding: 0 18px; width: 100%;height: 60px;display: flex;justify-content: space-between;align-items: center">
<span style="color:#666;display: block;font-size: 14px;" class="moneyNow"><span
style="display: block;margin-bottom: 5px">余额</span>{{
mybalance ? mybalance : initMybalance()
}} ¥</span>
<el-button v-if="!isShowKbossCharge" type="primary" @click="$router.push('/kbossCharge')" plain
style="padding: 8px; ">
充值
</el-button>
</div>
<el-dropdown-item v-if="role.includes('客户')" @click.native="$router.push('/customer/customerInformation')">
<span style="display: block">个人中心</span>
</el-dropdown-item>
<el-dropdown-item @click.native="logout">
<span style="display: block">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
<!-- 悬浮面板 -->
<div @mouseleave="sildeOut" @mouseenter="keepPanel" v-show="isShowPanel" class="panel">
<div class="inPanel">
<div class="panelLeft">
<ul class="outUl">
<li style="cursor: default" class="outLi" v-for="item in showPanelData" :key="item.firTitle">
<span style="cursor: default!important;" :class="['tilte', 'activeFir']">
{{
item.firTitle
}}
</span>
<ul class="inUl">
<li class="inLi" v-for="i in item.secMenu" :key="i.id">
<span style="position: relative" @mouseenter="sildeSecMenu(i)"
:class="['twoMenuStyle', secId === i.id ? 'activeSec' : '']">{{ i.secTitle }}</span>
</li>
</ul>
</li>
</ul>
</div>
<span class="ge"></span>
<div
v-if="secMenuData.length > 0 && secMenuData[0].thrMenu && secMenuData[0].thrMenu.length > 0 && secMenuData[0].thrMenu[0].thrTitle != null && secMenuData[0].secTitle !== secMenuData[0].thrMenu[0].thrTitle"
class="panelLeft">
<ul>
<li style="font-size: 14px!important;" @mouseenter="sildeThrMenu(thr)" v-for="(thr, index) in threeData"
:key="index">
{{ thr.thrTitle }}
</li>
</ul>
</div>
<span
v-if="secMenuData.length > 0 && secMenuData[0].thrMenu && secMenuData[0].thrMenu.length > 0 && secMenuData[0].thrMenu[0].thrTitle != null && secMenuData[0].secTitle !== secMenuData[0].thrMenu[0].thrTitle"
class="ge"></span>
<div class="panelRight"
v-if="showPanelRightData.length > 0 && showPanelRightData[0] && showPanelRightData[0].value">
<ul class="leastMenu inBox">
<li class="clickItem" style="font-size: 14px!important;margin: 8px 0;cursor: pointer"
v-for="itemR in showPanelRightData[0].value" :key="itemR.id" @click="clickGo(itemR)">
{{ itemR.name }} <span v-if="itemR.discount" class="zhe">{{
itemR.discount }}</span>
</li>
</ul>
</div>
<div class="panelRight" v-else>
<div style="text-align: center; color: #999; padding: 20px;">
暂无数据
</div>
</div>
</div>
</div>
<div
v-show="aiDialogVisible"
ref="aiChatPanel"
class="ai-chat-panel"
:style="aiPanelStyle"
>
<div class="ai-chat-panel__header" @mousedown="startAIPanelDrag">
<div class="ai-chat-panel__title">
<span class="ai-chat-panel__logo">AI</span>
<span>有问题找开元</span>
</div>
<i class="el-icon-close ai-chat-panel__close" @click="closeAIPanel"></i>
</div>
<div ref="aiChatMessages" class="ai-chat-panel__body">
<div v-if="!aiMessages.length" class="ai-chat-panel__welcome">
<h3>Hi!</h3>
<p>我是开元智能助手可以为您解答算力服务器选型采购部署和资源配置等问题</p>
<div class="ai-chat-panel__suggestions">
<span
v-for="item in aiQuickQuestions"
:key="item"
class="ai-chat-panel__tag"
@click="useAISuggestion(item)"
>
{{ item }}
</span>
</div>
</div>
<div
v-for="(item, index) in aiMessages"
:key="index"
:class="['ai-chat__message', `is-${item.role}`]"
>
<div class="ai-chat__avatar">
{{ item.role === 'assistant' ? 'AI' : '我' }}
</div>
<div class="ai-chat__bubble">
<div
v-if="item.role === 'assistant'"
class="ai-chat__rich-text"
v-html="formatAIContent(item.content)"
></div>
<span v-else>{{ item.content }}</span>
</div>
</div>
</div>
<div class="ai-chat-panel__input-wrap">
<div class="ai-chat-panel__actions">
<span class="ai-chat-panel__new" @click="resetAIChat">新对话</span>
<span class="ai-chat__tip">Enter 发送</span>
</div>
<div class="ai-chat-panel__input">
<el-input
v-model="aiInput"
type="textarea"
:rows="2"
resize="none"
maxlength="500"
placeholder="请输入你的问题"
@keydown.native="handleAIKeydown"
/>
<el-button
class="ai-chat-panel__send"
type="text"
:loading="aiLoading"
@click="sendAIMessage"
>
<i class="iconfont icon-fasong"></i>
</el-button>
</div>
</div>
</div>
<message-center
ref="messageCenter"
:visible.sync="messageCenterVisible"
:userId="userId"
@unread-count-update="handleUnreadCountUpdate"
/>
</div>
</template>
<script>
import Vue from 'vue'
import { computingData, networkData, storageData, testData } from "@/views/homePage/components/topBox/testData";
import { reqNavList, reqNewHomeFestival, reqNewHomeSync } from "@/api/newHome";
import { mapGetters, mapState } from "vuex";
import { getLogoAPI, getUserInfoAPI } from "@/api/login";
import { reqApplyChannel } from "@/api/customer/channel";
import store from "@/store";
import { getHomePath } from '@/views/setting/tools'
import MessageCenter from '@/components/MessageCenter/MessageCenter.vue'
import { reqAIChat } from '@/api/AI/ai'
export default Vue.extend({
name: "TopBox",
components: {
MessageCenter
},
data() {
return {
aiDialogVisible: false,
aiInput: '',
aiLoading: false,
aiMessages: [],
aiPanelPosition: {
left: null,
top: null
},
aiPanelDragging: false,
aiPanelDragOffset: {
x: 0,
y: 0
},
aiQuickQuestions: [
'推荐适合训练大模型的 GPU 服务器',
'4090 和 A100 怎么选',
'有没有高性价比 8 卡方案',
'国产化算力服务器有哪些'
],
messageCenterVisible: false,
homePath: getHomePath(),
isShowKbossCharge: false,
role: sessionStorage.getItem("jueseNew") == "admin" ? [] : sessionStorage.getItem("jueseNew"),
userId: sessionStorage.getItem("userId"),
firId: "",
secId: "",
nick_name: "",
showTimer: null,
hideTimer: null,
showPanelData: [],
secMenuData: [],
showPanelRightData: [],
threeData: [],
fourData: [],
product_service: [],
messageCount: 0
}
},
created() {
this.homePath = getHomePath()
if (sessionStorage.getItem('userId')) {
this.$store.commit('setLoginState', true);
}
!this.showRegisterButton && this.getUserInfo();
this.init()
this.initData()
},
mounted() {
if (sessionStorage.getItem("username")) {
this.nick_name = sessionStorage.getItem("username");
}
this.$nextTick(() => {
const topContainer = document.getElementById('topContainer');
if (topContainer) {
topContainer.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
},
beforeDestroy() {
this.stopAIPanelDrag()
},
computed: {
...mapGetters(["sidebar", "avatar", "device"]),
...mapState({
isShowPanel: (state) => state.product.showHomeNav,
navIndex: (state) => state.product.navIndex,
gridObj: state => state.operationAnalysis.gridObj,
mybalance: state => state.user.mybalance,
logoutUrl: state => state.login.logoutUrl,
loginStateVuex: state => state.login.loginState,
logoInfoNew: state => state.product.logoInfoNew,
}),
loginState() {
const userId = sessionStorage.getItem('userId');
return this.loginStateVuex || (userId !== null && userId !== 'null' && userId !== '');
},
showRegisterButton() {
const orgType = window.sessionStorage.getItem('org_type');
const userId = window.sessionStorage.getItem('userId');
return orgType !== '2' && orgType !== '3' && userId === null;
},
username() {
return sessionStorage.getItem('username') || '';
},
// 判断当前是否为ncmatch.cn域名
isNcmatchDomain() {
return window.location.hostname.includes('ncmatch.cn');
},
// 首页激活状态计算
isActiveHome() {
if (this.isNcmatchDomain) {
return this.$route.path.includes('/ncmatchHome/index');
} else {
return this.$route.path.includes('/homePage/index');
}
},
aiPanelStyle() {
if (this.aiPanelPosition.left === null || this.aiPanelPosition.top === null) {
return {}
}
return {
left: `${this.aiPanelPosition.left}px`,
top: `${this.aiPanelPosition.top}px`,
right: 'auto',
bottom: 'auto'
}
}
},
methods: {
// 处理AI助手点击
handleAIClick() {
this.aiDialogVisible = true
this.$nextTick(() => {
this.initAIPanelPosition()
this.scrollAIChatToBottom()
})
},
closeAIPanel() {
this.aiDialogVisible = false
this.stopAIPanelDrag()
},
resetAIChat() {
this.aiMessages = []
this.aiInput = ''
},
useAISuggestion(question) {
this.sendAIMessage(question)
},
initAIPanelPosition() {
if (this.aiPanelPosition.left !== null && this.aiPanelPosition.top !== null) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
this.aiPanelPosition = {
left: Math.max(window.innerWidth - panelWidth - 24, 16),
top: Math.max(window.innerHeight - panelHeight - 24, 16)
}
},
startAIPanelDrag(event) {
if (event.button !== 0) return
if (event.target && event.target.closest('.ai-chat-panel__close')) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const rect = panel.getBoundingClientRect()
this.aiPanelDragging = true
this.aiPanelDragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
window.addEventListener('mousemove', this.handleAIPanelDrag)
window.addEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = 'none'
},
handleAIPanelDrag(event) {
if (!this.aiPanelDragging) return
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
const minLeft = 0
const minTop = 0
const maxLeft = Math.max(window.innerWidth - panelWidth, 0)
const maxTop = Math.max(window.innerHeight - panelHeight, 0)
const nextLeft = event.clientX - this.aiPanelDragOffset.x
const nextTop = event.clientY - this.aiPanelDragOffset.y
this.aiPanelPosition = {
left: Math.min(Math.max(nextLeft, minLeft), maxLeft),
top: Math.min(Math.max(nextTop, minTop), maxTop)
}
},
stopAIPanelDrag() {
this.aiPanelDragging = false
window.removeEventListener('mousemove', this.handleAIPanelDrag)
window.removeEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = ''
},
handleAIKeydown(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
this.sendAIMessage()
}
},
async sendAIMessage(quickQuestion) {
const message = (quickQuestion || this.aiInput).trim()
if (!message || this.aiLoading) return
this.aiMessages.push({
role: 'user',
content: message
})
this.aiInput = ''
const loadingMessageIndex = this.aiMessages.push({
role: 'assistant',
content: '[正在检索并生成回答...]'
}) - 1
this.aiLoading = true
this.$nextTick(() => {
this.scrollAIChatToBottom()
})
try {
const response = await reqAIChat({ message }, (content) => {
if (!content) return
this.$set(this.aiMessages, loadingMessageIndex, {
role: 'assistant',
content
})
this.$nextTick(() => {
this.scrollAIChatToBottom()
})
})
if (response && response.content) {
this.$set(this.aiMessages, loadingMessageIndex, {
role: 'assistant',
content: response.content
})
} else {
this.$set(this.aiMessages, loadingMessageIndex, {
role: 'assistant',
content: '暂时没有获取到回复,请稍后再试。'
})
}
} catch (error) {
this.$set(this.aiMessages, loadingMessageIndex, {
role: 'assistant',
content: '抱歉,当前服务繁忙,请稍后再试。'
})
} finally {
this.aiLoading = false
this.$nextTick(() => {
this.scrollAIChatToBottom()
})
}
},
escapeAIHtml(content) {
return String(content || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
},
formatAIInline(content) {
return this.escapeAIHtml(content).replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
},
formatAIContent(content) {
if (!content) return ''
const normalizedContent = String(content)
.replace(/\r\n/g, '\n')
.replace(/ {2,}/g, '\n')
.replace(/\n{3,}/g, '\n\n')
const lines = normalizedContent.split('\n')
return lines.map((line) => {
const trimmed = line.trim()
if (!trimmed) {
return '<div class="ai-chat__paragraph ai-chat__paragraph--space"></div>'
}
if (/^-\s+/.test(trimmed)) {
return `<div class="ai-chat__item"><span class="ai-chat__item-dot"></span><span>${this.formatAIInline(trimmed.replace(/^-\s+/, ''))}</span></div>`
}
if (/^\*\*(.+?)\*\*$/.test(trimmed)) {
return `<div class="ai-chat__section-title">${this.formatAIInline(trimmed)}</div>`
}
return `<div class="ai-chat__paragraph">${this.formatAIInline(trimmed)}</div>`
}).join('')
},
scrollAIChatToBottom() {
const messageBox = this.$refs.aiChatMessages
if (messageBox) {
messageBox.scrollTop = messageBox.scrollHeight
}
},
// 处理客服点击
handleServiceClick() {
this.$message.info({
message: '功能即将上线,敬请期待!',
duration: 3000,
showClose: true
});
},
// 处理消息图标点击
handleMessageClick() {
if (this.loginState) {
this.messageCenterVisible = true;
if (this.$refs.messageCenter) {
this.$refs.messageCenter.open();
}
} else {
this.handleGoLogin()
}
},
// 处理未读消息数量更新
handleUnreadCountUpdate(count) {
this.messageCount = count;
},
// 控制台按钮点击事件
goB() {
if (sessionStorage.getItem('jueseNew').includes('客户')) {
this.$router.push('/product/productHome')
} else if (sessionStorage.getItem('jueseNew').includes('运营')) {
this.$router.push('/operation/supplierManagement')
}else if(sessionStorage.getItem('jueseNew').includes('运维')){
this.$router.push('/operationAndMaintenance/workOrderProcessing')
}else if(sessionStorage.getItem('jueseNew').includes('销售')){
this.$router.push('/sales/distributorManagement')
}else if(sessionStorage.getItem('jueseNew').includes('财务')){
this.$router.push('/finance/supplierSettlementStatistics')
}else if(sessionStorage.getItem('jueseNew').includes('admin')){
this.$router.push('/superAdministrator/addAdmin')
}
},
// 初始化Logo信息
init() {
let remoteUrl = ''
if (window.location.href.includes("dev")) {
remoteUrl = 'https://www.kaiyuancloud.cn/dev/reseller/get_ipc_logo.dspy'
} else if (window.location.href.includes("localhost")) {
remoteUrl = 'http://localhost:9527/'
} else {
remoteUrl = 'https://www.kaiyuancloud.cn/reseller/get_ipc_logo.dspy'
}
let params = {
url_link: window.location.href.split("#")[0]
}
getLogoAPI(params).then((res) => {
if (res.status == true && res.data && res.data.length) {
this.$store.commit('setLogoInfoNew', res.data[0].additional_msg);
this.isShow = true
if (res.data[0].orgname == '中关村数智人工智能产业联盟') {
this.logoUrlOut = 'https://www.ncmatch.cn'
}
this.photosUrl = res.data[0];
if (this.photosUrl.orgname != '业主机构') {
this.isLogo = true
this.logoImg = this.photosUrl.logo;
this.$store.commit('setLogo', this.photosUrl.logo)
this.logoText = this.photosUrl.orgname;
} else {
this.$store.commit('setLogo', "")
}
} else {
this.$message({
message: "获取ipc失败~",
type: "error",
});
}
});
},
// 退出登录
async logout() {
this.$store.commit('setLoginState', false)
store.commit('tagsView/resetBreadcrumbState');
sessionStorage.removeItem("auths");
sessionStorage.removeItem("routes");
sessionStorage.removeItem("user");
sessionStorage.removeItem("userId");
sessionStorage.removeItem("org_type")
localStorage.removeItem("auths");
localStorage.removeItem("routes");
localStorage.removeItem("user");
localStorage.removeItem("userId");
localStorage.removeItem("org_type")
localStorage.removeItem('userId')
if (!window.location.href.includes('homePage/index')) {
await this.$router.push(getHomePath());
}
},
// 修改密码
edituserPassword() {
this.passwordDialog = true;
},
// 申请渠道管理
channelFirstBtn() {
reqApplyChannel({ user_id: this.userId }).then(res => {
if (res.code == "100" || res.code == "210") {
this.$message.success(res.msg)
} else if (res.code == "300") {
this.channelManagementShow = true
} else if (res.code == "200") {
this.$router.push('/customer/channelMangement')
}
})
},
// 初始化余额
initMybalance() {
return sessionStorage.getItem('mybalance')
},
// 复制用户ID
copyBtn() {
const textToCopy = this.$refs.contentToCopy.innerText;
navigator.clipboard.writeText(textToCopy).then(() => {
this.$message.success('复制成功!')
}).catch((error) => {
console.error('复制失败', error);
});
},
// 获取用户信息
async getUserInfo() {
if (!this.userId) return;
getUserInfoAPI({ id: this.userId }).then((res) => {
if (res.status) {
this.userInfoList = res.data;
sessionStorage.setItem("orgid", res.data[0].orgid);
} else {
this.$message({
message: res.msg,
type: "error",
});
}
});
},
// 处理下拉菜单显示/隐藏时的图标旋转
handleShow(isShow) {
let resverNode = document.getElementById('resverIcon')
if (isShow) {
if (this.role.includes('客户')) {
this.$store.dispatch('user/getCustmoersMoney')
}
resverNode.style.transform = 'rotate(180deg)'
} else {
resverNode.style.transform = 'rotate(0)'
}
},
// 鼠标进入三级菜单项
sildeThrMenu(item) {
let res = []
res.push(item)
this.showPanelRightData = res
},
// 点击菜单项进行跳转或打开新窗口
clickGo(item) {
this.$store.commit('setShowHomeNav', false)
let userId = sessionStorage.getItem('userId')
const orgType = window.sessionStorage.getItem('org_type');
let isKehu = orgType === '2' || orgType === '3';
if (item.type === '百度云') {
if (this.loginState) {
this.$store.commit('setRedirectUrl', item.url)
localStorage.setItem('redirectUrl', item.url)
localStorage.setItem('userRescourseUrl', item.listUrl)
this.$router.push({
name: 'baiduProductShow',
params: {
listUrl: item.listUrl,
url: item.url
}
})
} else {
this.$router.push({
path: "/login",
query: {
fromPath: 'homePage',
type: 'bd',
listUrl: item.listUrl,
url: item.url
}
})
}
} else if (item.type === '阿里云') {
if (this.loginState) {
reqNewHomeSync().then(response => {
if (response.status) {
reqNewHomeFestival().then(res => {
if (res.status) {
window.open(res.data)
} else {
this.$message.warning(res.msg)
}
})
} else {
this.$message.warning(response.msg)
}
})
} else {
this.$router.push({
path: "/login",
query: {
fromPath: 'homePage',
type: 'ali',
}
})
}
} else if (item.type === '智算' || item.type === '算力网络') {
if (item.name === '容器云') {
if (this.loginState) {
this.$router.push('/product/productHome/k8s/createK8s')
} else {
this.$router.push({
path: "/login",
query: {
fromPath: 'homePage',
type: 'rqy',
}
})
}
} else {
this.$router.push({
path: '/homePage/detail',
query: {
id: item.id,
}
})
}
} else if (item.name === '灵医智能体') {
this.$router.push('/homePage/hospital')
} else if (item.name === '客悦·智能客服') {
this.$router.push('/homePage/customerService')
}
},
// 改变面板数据
changeData(data) {
this.showPanelData = data || []
this.secMenuData = data?.[0]?.secMenu || []
if (this.secMenuData.length > 0) {
const firstSecMenu = this.secMenuData[0]
this.threeData = firstSecMenu.thrMenu || []
if (this.threeData.length > 0) {
this.showPanelRightData = this.threeData.map(item => ({
...item,
thrTitle: item.thrTitle || firstSecMenu.secTitle
}))
} else {
this.showPanelRightData = [{
thrTitle: firstSecMenu.secTitle,
value: []
}]
}
} else {
this.threeData = []
this.showPanelRightData = []
}
},
// 初始化导航数据
initData() {
reqNavList({ url_link: window.location.href }).then(res => {
if (res.status) {
this.product_service = this.buildData(res.data.product_service)
this.changeData(this.product_service)
}
}).catch(error => {
console.error("获取导航数据失败:", error)
this.product_service = []
this.changeData([])
})
},
// 构建导航数据结构
buildData(data) {
let res = [];
data.forEach(item => {
if (item.secMenu && Array.isArray(item.secMenu)) {
item.secMenu.forEach(secItem => {
if (secItem.thrMenu && Array.isArray(secItem.thrMenu)) {
secItem.thrMenu.forEach(thrItem => {
if (thrItem.value && Array.isArray(thrItem.value)) {
thrItem.value.forEach(valueItem => {
valueItem.type = secItem.secTitle;
});
}
});
}
});
}
});
return data;
},
// 鼠标进入二级菜单项
sildeSecMenu(item) {
this.secId = item.id
if (!item.thrMenu || item.thrMenu.length === 0) {
this.threeData = []
this.showPanelRightData = [{
thrTitle: item.secTitle,
value: []
}]
return
}
this.threeData = item.thrMenu.map(menu => ({
...menu,
thrTitle: menu.thrTitle || item.secTitle
}))
this.showPanelRightData = this.threeData
},
// 返回测试数据
networkData() {
return networkData
},
storageData() {
return storageData
},
testData() {
return testData
},
// 鼠标进入一级菜单项
sildeItemIn(item) {
this.firId = item.id
this.secId = item.secMenu[0].id
this.secMenuData = item.secMenu || []
this.showPanelRightData = this.secMenuData[0].thrMenu || []
},
computingData() {
return computingData
},
// 鼠标进入生态与合作菜单
sildeIn(item) {
this.changeData(item)
if (this.hideTimer) {
clearTimeout(this.hideTimer)
}
this.showTimer = setTimeout(() => {
this.$store.commit('setShowHomeNav', true)
}, 100)
},
// 鼠标离开导航区域
sildeOut() {
if (this.showTimer) {
clearTimeout(this.showTimer)
}
this.hideTimer = setTimeout(() => {
this.$store.commit('setShowHomeNav', false)
}, 200)
},
// 鼠标进入面板区域,取消隐藏
keepPanel() {
if (this.hideTimer) {
clearTimeout(this.hideTimer)
}
},
// 处理登录跳转逻辑
handleGoLogin() {
let token = this.$store.getters.token;
if (!token) {
this.$message.warning('请先登录哦~');
this.$router.push({
path: '/login'
});
}
},
// 首页跳转方法 - 根据域名判断跳转到不同的首页
goHome() {
console.log('=== goHome 方法开始 ===');
console.log('当前完整URL:', window.location.href);
console.log('当前hostname:', window.location.hostname);
console.log('isNcmatchDomain判断结果:', this.isNcmatchDomain);
console.log('准备跳转的路径:', this.isNcmatchDomain ? '/ncmatchHome/index' : '/homePage/index');
if (this.isNcmatchDomain) {
this.$router.push('/ncmatchHome/index');
} else {
this.$router.push('/homePage/index');
}
}
},
})
</script>
<style scoped lang="less">
.top-nav {
font-size: 18px !important;
* {
font-size: 18px !important;
}
font-weight: 500;
color: #222F60 !important;
position: relative;
background-color: #fff;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08);
width: 100%;
z-index: 1000;
.container {
font-size: 18px;
width: 100%;
height: 100%;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
}
.logo {
display: flex;
align-items: center;
.logoImg {
width: 148px;
cursor: pointer;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
}
.main-nav {
margin-left: 44px;
z-index: 10000;
.nav-list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
p {
height: 100%;
padding-right: 40px;
position: relative;
a {
text-decoration: none;
font-size: 18px !important;
padding: 5px 0;
transition: all 0.3s;
cursor: pointer;
&:hover {
color: #1E6FFF;
}
}
&.active a {
color: #1E6FFF;
font-weight: bold;
}
}
}
}
.user-area {
display: flex;
align-items: center;
.search-box {
display: flex;
align-items: center;
border: 2px solid #d2d7e6;
border-radius: 8px;
padding: 8px;
input {
margin-left: 10px;
border: 0;
outline: none;
}
}
a {
border-radius: 4px;
font-size: 14px;
text-decoration: none;
transition: all 0.3s;
cursor: pointer;
}
.login-btn {
margin-right: 20px;
color: #333;
&:hover {
color: #1E6FFF;
}
}
.register-btn {
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
border-radius: 10px;
padding: 6px 10px;
color: white;
&:hover {
opacity: 0.9;
}
}
.icon-xiaoxi {
font-size: 24px !important;
}
.functions {
padding-right: 20px;
font-size: 20px;
color: #000;
cursor: pointer;
&:hover {
color: #1E6FFF;
}
}
}
}
.panel {
.inPanel {
padding: 20px;
border-radius: 8px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: flex-start;
height: calc(100vh - 100px);
background: #fff;
margin: 10px 20px 20px;
margin-left: 0;
margin-right: 0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
transition: opacity 0.3s ease;
}
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
color: #091221;
position: absolute;
top: 84px;
left: 0;
width: calc(100% - 450px);
margin-left: 250px;
z-index: 999;
overflow-y: auto;
.panelLeft {
height: 100% !important;
justify-content: stretch;
font-size: 14px !important;
width: 200px;
overflow-y: auto;
li {
padding: 10px;
transition: background-color 0.3s;
&:hover {
cursor: pointer;
color: #1E6FFF;
}
}
}
.panelRight {
flex: 1;
padding-left: 20px;
overflow-y: auto;
}
}
.activeFir {
background-image: linear-gradient(90deg, rgba(210, 228, 253, .6), hsla(0, 0%, 100%, .6));
border-radius: 4px;
cursor: pointer;
}
.twoMenuStyle {
cursor: pointer;
position: relative;
color: #091221;
font-size: 14px !important;
&::after {
transition: all .3s ease-in-out;
content: '';
width: 0;
position: absolute;
bottom: -5px;
left: 0;
height: 2px;
background: #091221;
border-radius: 1px;
}
}
.activeSec {
font-weight: 600;
&::after {
width: 100% !important;
}
}
.subTitle {
font-size: 16px;
color: #2468f2;
font-weight: 600;
margin: 5px 0;
display: inline-block;
}
.leastMenu {
overflow-y: auto;
.inBox {
li {
font-size: 14px !important;
margin: 6px 0;
display: flex;
justify-content: flex-start;
align-items: flex-end;
&:hover {
cursor: pointer;
color: #1E6FFF !important;
}
}
}
}
.ge {
background-image: url('https://bce.bdstatic.com/portal-cloud-server/images/nav/dropdown_bar_middle.png');
box-sizing: border-box;
display: block;
height: 100%;
width: 20px;
margin: 0 10px;
}
.zhe {
font-size: 12px !important;
border-radius: 4px;
padding: 1px 2px;
color: red;
display: inline-block;
margin-left: 3px;
}
.outUl {
.outLi {
padding: 10px;
.tilte {
width: 100%;
display: inline-block;
padding: 5px;
}
.inUl {
.inLi {
padding: 10px 10px 10px 5px;
}
}
}
}
.logoImg {
width: 148px;
height: 48px;
}
.clickItem {
margin-bottom: 15px !important;
&:hover {
color: #1E6FFF !important;
}
}
.imgUser {
display: flex;
justify-content: center;
align-items: center;
background-color: #eee;
width: 46px;
height: 46px;
border-radius: 50%;
color: white;
margin-right: 8px;
img {
// border-radius: 50%;
width: 26px;
height: 26px;
}
}
.company-name {
font-size: 24px;
font-weight: bold;
color: #222F60;
cursor: pointer;
transition: color 0.3s ease;
&:hover {
color: #1E6FFF;
}
}
::v-deep .v-model{
z-index: 1;
}
.ai-chat-panel {
position: fixed;
right: 24px;
bottom: 24px;
width: 380px;
height: 620px;
background: #fff;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
z-index: 3000;
display: flex;
flex-direction: column;
overflow: hidden;
&__header {
height: 64px;
padding: 0 18px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
background: #fff;
cursor: move;
}
&__title {
display: flex;
align-items: center;
gap: 10px;
font-size: 16px !important;
font-weight: 600;
color: #222;
}
&__logo {
width: 28px;
height: 28px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background: linear-gradient(90deg, #d66bff 0%, #7d7cff 100%);
color: #fff;
font-size: 12px !important;
}
&__close {
cursor: pointer;
color: #666;
font-size: 18px !important;
}
&__body {
flex: 1;
overflow-y: auto;
padding: 18px 16px;
background: #fff;
}
&__welcome {
h3 {
margin: 0 0 12px;
color: #5f59ff;
font-size: 22px !important;
font-weight: 700;
}
p {
margin: 0 0 20px;
font-size: 14px !important;
line-height: 1.8;
color: #333;
}
}
&__suggestions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
&__tag {
display: inline-flex;
align-items: center;
padding: 10px 14px;
border-radius: 10px;
background: #f7f7f8;
color: #333;
font-size: 14px !important;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
color: #1E6FFF;
background: #eef5ff;
}
}
&__input-wrap {
border-top: 1px solid #f0f0f0;
padding: 12px 14px 14px;
background: #fff;
}
&__actions {
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
&__new {
font-size: 13px !important;
color: #666;
cursor: pointer;
&:hover {
color: #1E6FFF;
}
}
&__input {
position: relative;
}
&__send {
position: absolute;
right: 8px;
bottom: 8px;
padding: 0;
color: #7d7cff;
}
}
::v-deep .ai-chat-panel__input {
.el-textarea__inner {
min-height: 84px !important;
padding: 12px 48px 12px 12px;
border-radius: 14px;
border: 1px solid #e5eaf3;
font-size: 14px;
line-height: 1.6;
}
}
.ai-chat {
&__message {
display: flex;
gap: 12px;
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
&.is-user {
flex-direction: row-reverse;
.ai-chat__bubble {
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
color: #fff;
}
}
}
&__avatar {
flex-shrink: 0;
width: 34px;
height: 34px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px !important;
color: #1E6FFF;
background: rgba(30, 111, 255, 0.12);
}
&__bubble {
max-width: calc(100% - 46px);
padding: 12px 14px;
border-radius: 12px;
background: #f7f9fc;
color: #333;
font-size: 14px !important;
line-height: 1.7;
white-space: pre-wrap;
word-break: break-word;
}
&__rich-text {
font-size: 14px !important;
line-height: 1.7;
}
&__paragraph {
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
&--space {
height: 8px;
margin-bottom: 0;
}
}
&__section-title {
margin: 12px 0 8px;
font-weight: 700;
color: #222F60;
&:first-child {
margin-top: 0;
}
}
&__item {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 8px;
}
&__item-dot {
width: 6px;
height: 6px;
margin-top: 9px;
border-radius: 50%;
background: #7d7cff;
flex-shrink: 0;
}
&__tip {
color: #999;
font-size: 12px !important;
}
}
::v-deep .ai-chat__rich-text strong {
font-weight: 700;
color: #222F60;
}
</style>