2026-07-02 10:45:14 +08:00

1703 lines
48 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" @mouseleave="sildeOut">
<div class="top-nav-placeholder"></div>
<div id="topContainer" class="nav-container">
<div class="brand" @click="goHome">
<img
v-if="JSON.stringify(logoInfoNew) !== '{}'"
class="brand-logo"
:src="(logoInfoNew.home && logoInfoNew.home.logoImg) || ''"
alt=""
>
</div>
<nav class="site-nav">
<button type="button" class="nav-item" :class="{ active: isActiveHome }" @click.stop="goHome">首页</button>
<button
type="button"
class="nav-item nav-item--product"
:class="{ active: isShowPanel }"
@mouseenter="sildeIn(product_service)"
@click.stop="sildeIn(product_service)"
>
产品
<i class="iconfont" :class="isShowPanel ? 'icon-shang' : 'icon-xia'"></i>
</button>
<button type="button" class="nav-item" >案例</button>
<button type="button" class="nav-item">动态</button>
</nav>
<div class="user-actions">
<i v-if="loginState" class="iconfont icon-xiaoxi functions" @click="handleMessageClick"></i>
<button v-if="loginState" type="button" class="console-link" @click="goB">控制台</button>
<button
v-if="!loginState"
type="button"
class="login-btn"
@click="$router.push({ path: '/login', query: { fromPath: 'homePage' } })"
>
登录
</button>
<button
v-if="!loginState"
type="button"
class="register-btn"
@click="$router.push('/registrationPage')"
>
立即注册
</button>
<el-dropdown
v-if="loginState"
@visible-change="handleShow"
class="avatar-container right-menu-item hover-effect nick-name-style"
trigger="click"
>
<button type="button" class="login-pill login-pill--user">
{{ nick_name || '控制台' }}
<i id="resverIcon" class="el-icon-arrow-up el-icon--right resverIcon"></i>
</button>
<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.666666 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"></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 v-show="isShowPanel" class="product-panel" @mouseenter="keepPanel" @mouseleave="sildeOut">
<aside class="product-panel__side">
<button type="button" class="product-side-item active">精选产品</button>
<button type="button" class="product-side-item" @click="navigateTo('/homePage/opc')">OPC</button>
<button type="button" class="product-side-item" @click="goComputeMarket">算力市场</button>
<button type="button" class="product-side-item" @click="handleModelSquareClick">Token市集</button>
<button type="button" class="product-side-item" @click="goTrainPlatform">训推平台</button>
<button type="button" class="product-side-item" @click="goAgentStore">智能体商店</button>
<button type="button" class="product-side-item" @click="goSupplySquare">供需广场</button>
</aside>
<div class="product-panel__content">
<div class="product-panel__head">
<div>
<h3 class="product-panel__title">精选产品</h3>
<p>覆盖模型服务算力资源智能体应用与供需协同</p>
</div>
<button type="button" class="product-panel__more" @click="navigateTo('/homePage/opc')">查看 OPC</button>
</div>
<div class="product-group">
<h4>核心服务</h4>
<div class="product-grid">
<button type="button" class="product-card product-card--blue" @click="handleModelSquareClick">
<span class="product-card__icon">T</span>
<span class="product-card__text">
<strong>Token市集</strong>
<em>一站式 AI 模型交易与服务平台</em>
</span>
</button>
<button type="button" class="product-card product-card--purple" @click="goYuanjing">
<span class="product-card__icon">AI</span>
<span class="product-card__text">
<strong>创镱工坊</strong>
<em>AI 驱动的创意影像创作平台</em>
</span>
</button>
<button type="button" class="product-card product-card--green" @click="navigateTo('/homePage/opc')">
<span class="product-card__icon">OPC</span>
<span class="product-card__text">
<strong>云枢基座</strong>
<em>企业级 AI 基础设施与算力底座</em>
</span>
</button>
</div>
</div>
<div class="product-group product-group--compact">
<h4>应用入口</h4>
<div class="product-grid product-grid--compact">
<button type="button" class="product-card product-card--plain" @click="goComputeMarket">
<span class="product-card__text">
<strong>算力市场</strong>
<em>高性能算力资源灵活选购</em>
</span>
</button>
<button type="button" class="product-card product-card--plain" @click="goTrainPlatform">
<span class="product-card__text">
<strong>训推平台</strong>
<em>AI 模型全流程开发体验</em>
</span>
</button>
<button type="button" class="product-card product-card--plain" @click="goAgentStore">
<span class="product-card__text">
<strong>智能体商店</strong>
<em>即开即用的智能体服务</em>
</span>
</button>
<button type="button" class="product-card product-card--plain" @click="goSupplySquare">
<span class="product-card__text">
<strong>供需广场</strong>
<em>资源算力服务供需匹配</em>
</span>
</button>
</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'
import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
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(() => {
// 1. 导航栏强制透明
const topContainer = document.getElementById('topContainer');
if (topContainer) {
topContainer.style.background = 'transparent';
topContainer.style.backgroundColor = 'transparent';
topContainer.style.backgroundImage = 'none';
topContainer.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
// 父容器和占位符
const nav = document.querySelector('.top-nav');
if (nav) {
nav.style.background = 'transparent';
nav.style.backgroundColor = 'transparent';
nav.style.backgroundImage = 'none';
}
const placeholder = document.querySelector('.top-nav-placeholder');
if (placeholder) {
placeholder.style.background = 'transparent';
placeholder.style.backgroundColor = 'transparent';
placeholder.style.backgroundImage = 'none';
}
// 2. 页面背景强制透明(关键!解决根本问题)
document.body.style.background = 'transparent';
document.body.style.backgroundColor = 'transparent';
document.documentElement.style.background = 'transparent';
document.documentElement.style.backgroundColor = 'transparent';
});
window.addEventListener('kboss-open-ai-chat', this.handleExternalAIOpen);
},
beforeDestroy() {
window.removeEventListener('kboss-open-ai-chat', this.handleExternalAIOpen);
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') || '';
},
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');
}
},
isActiveTokenMarket() {
const category = this.$route.query.category || this.$route.query.tab || ''
return this.$route.path === '/tokenMarket' ||
(this.$route.path === '/product' && ['TOKEN市集', 'Token市集', 'token市集'].includes(category))
},
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: {
navigateTo(path) {
this.$store.commit('setShowHomeNav', false)
if (this.$route.path === path) return
this.$router.push(path)
},
handleModelSquareClick() {
this.$store.commit('setShowHomeNav', false)
this.$router.push({
path: '/homePage/tokenMarket',
query: {
category: 'TOKEN市集',
single: '1'
}
})
},
goComputeMarket() {
this.navigateTo('/homePage/computeMarket')
},
goTrainPlatform() {
this.navigateTo('/homePage/trainPlatform')
},
goAgentStore() {
this.navigateTo('/homePage/agentStore')
},
goSupplySquare() {
this.navigateTo('/ncmatchHome/supplyAndDemandSquare')
},
goAbout() {
this.navigateTo('/homePage/about')
},
async goYuanjing() {
this.$store.commit('setShowHomeNav', false)
const yuanJingBaseUrl = window.location.origin === 'https://dev.opencomputing.cn'
? 'https://aidev.opencomputing.cn'
: 'https://ai.opencomputing.cn'
if (!this.loginState) {
window.open(`${yuanJingBaseUrl}/`, '_blank')
return
}
const yuanJingWindow = window.open('', '_blank')
try {
const res = await gotoYuanJingAPI({
user_id: sessionStorage.getItem('userId')
})
const deerer = this.getYuanJingAuthorization(res)
if (!deerer) {
if (yuanJingWindow) yuanJingWindow.close()
this.$message.error((res && res.msg) || '获取元境授权参数失败')
return
}
const loginUrl = `${yuanJingBaseUrl}/#/getCookie?deerer=${encodeURIComponent(deerer)}`
if (yuanJingWindow) {
yuanJingWindow.location.href = loginUrl
} else {
window.location.href = loginUrl
}
} catch (error) {
if (yuanJingWindow) yuanJingWindow.close()
this.$message.error('跳转元境失败,请稍后重试')
}
},
getYuanJingAuthorization(res) {
if (!res) return ''
if (typeof res === 'string') return res
const data = res.data || res
if (typeof data === 'string') return data
return data.Authorization || data.authorization || data.token || data.header || data.value || ''
},
isPanelFirClickable(item) {
const title = (item && item.firTitle) || ''
return title === '元境' || title === 'TOKEN市集' || title === 'token市集'
},
handlePanelFirClick(item) {
const title = (item && item.firTitle) || ''
if (title === '元境') {
this.$store.commit('setShowHomeNav', false)
this.goYuanjing()
return
}
if (title === 'TOKEN市集' || title === 'token市集') {
this.$store.commit('setShowHomeNav', false)
this.handleModelSquareClick()
}
},
handleAIClick() {
this.aiDialogVisible = true
this.$nextTick(() => {
this.initAIPanelPosition()
this.scrollAIChatToBottom()
})
},
handleExternalAIOpen() {
this.handleAIClick()
},
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, 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
},
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')
}
},
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() {
let redirectPath = ''
if (this.$route && this.$route.fullPath && !this.$route.fullPath.includes('/login')) {
redirectPath = this.$route.fullPath
sessionStorage.setItem('loginRedirectPath', redirectPath)
}
this.$store.commit('setLoginState', false)
store.commit('tagsView/resetBreadcrumbState');
store.commit('permission/RESET_ROUTES');
sessionStorage.removeItem("auths");
sessionStorage.removeItem("routes");
sessionStorage.removeItem("user");
sessionStorage.removeItem("userId");
sessionStorage.removeItem("org_type")
sessionStorage.removeItem("userType");
sessionStorage.removeItem("orgType");
sessionStorage.removeItem("roles");
sessionStorage.removeItem("juese");
sessionStorage.removeItem("jueseNew");
localStorage.removeItem("auths");
localStorage.removeItem("routes");
localStorage.removeItem("user");
localStorage.removeItem("userId");
localStorage.removeItem("org_type")
localStorage.removeItem('userId')
await this.$router.push({
path: '/login',
query: redirectPath ? { redirect: redirectPath } : {}
});
},
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')
},
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() {
this.$store.commit('setShowHomeNav', false)
if (this.isNcmatchDomain) {
this.$router.push('/ncmatchHome/index');
} else {
this.$router.push('/homePage/index');
}
}
},
})
</script>
<style scoped lang="less">
/* ===== 强制透明重置(最高优先级) ===== */
.top-nav,
.top-nav-placeholder,
.nav-container {
background: transparent !important;
background-color: transparent !important;
background-image: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
box-shadow: none !important;
}
.top-nav {
position: relative;
height: 0;
width: 100%;
min-width: 1200px;
color: #111827;
font-family: PingFang SC, Microsoft YaHei, Arial, sans-serif;
box-sizing: border-box;
}
.top-nav-placeholder {
display: block;
height: 0;
width: 100%;
flex-shrink: 0;
}
.nav-container {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 3000;
width: 100%;
height: 82px;
padding: 0 42px;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
.brand {
display: inline-flex;
align-items: center;
min-width: 220px;
cursor: pointer;
}
.brand-logo {
width: 184px;
max-height: 52px;
object-fit: contain;
object-position: left center;
display: block;
}
.site-nav {
display: flex;
align-items: center;
gap: 38px;
margin-left: auto;
margin-right: 32px;
}
.nav-item {
padding: 0;
border: 0;
background: transparent;
color: #111827;
cursor: pointer;
font-size: 17px !important;
line-height: 82px;
font-weight: 600;
white-space: nowrap;
transition: color 0.2s ease;
&:hover,
&.active {
color: #1e6fff;
}
}
.nav-item--product {
display: inline-flex;
align-items: center;
gap: 6px;
.iconfont {
font-size: 13px !important;
line-height: 1;
}
}
.nav-lang {
color: #374151;
font-size: 15px !important;
}
.user-actions {
display: flex;
align-items: center;
gap: 16px;
}
.functions {
color: #000;
cursor: pointer;
font-size: 20px !important;
line-height: 1;
transition: color 0.2s ease;
&:hover {
color: #1E6FFF;
}
}
.console-link {
height: 36px;
padding: 0;
border: 0;
background: transparent;
color: #333;
cursor: pointer;
font-size: 15px !important;
font-weight: 600;
line-height: 36px;
transition: color 0.2s ease;
&:hover {
color: #1E6FFF;
}
}
.login-btn {
height: 36px;
padding: 0;
border: 0;
background: transparent;
color: #333;
cursor: pointer;
font-size: 15px !important;
font-weight: 500;
line-height: 36px;
transition: color 0.2s ease;
&:hover {
color: #1E6FFF;
}
}
.register-btn {
height: 36px;
padding: 0 16px;
border: 0;
border-radius: 10px;
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
color: #fff;
cursor: pointer;
font-size: 15px !important;
font-weight: 600;
line-height: 36px;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.9;
}
}
.login-pill {
min-width: 58px;
height: 36px;
padding: 0 20px;
border: 0;
border-radius: 999px;
background: #050505;
color: #fff;
cursor: pointer;
font-size: 15px !important;
font-weight: 600;
line-height: 36px;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.84;
}
}
.login-pill--user {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.product-panel {
position: fixed;
top: 82px;
left: 0;
right: 0;
z-index: 2999;
min-height: 340px;
display: flex;
background: rgba(255, 255, 255, 0.98);
backdrop-filter: blur(14px);
box-shadow: 0 20px 46px rgba(15, 23, 42, 0.1);
border-top: 1px solid rgba(15, 23, 42, 0.04);
}
.product-panel__side {
width: 210px;
padding: 20px 16px;
background: linear-gradient(180deg, #f4f8ff 0%, #f8fafc 100%);
box-sizing: border-box;
}
.product-side-item {
width: 100%;
height: 42px;
padding: 0 16px;
display: flex;
align-items: center;
border: 0;
border-radius: 12px;
background: transparent;
color: #374151;
cursor: pointer;
font-size: 14px !important;
text-align: left;
transition: all 0.2s ease;
&:hover,
&.active {
background: #e8f1ff;
color: #1e6fff;
font-weight: 600;
}
}
.product-panel__content {
flex: 1;
padding: 26px 46px 32px;
box-sizing: border-box;
}
.product-panel__head {
margin-bottom: 24px;
display: flex;
align-items: flex-start;
justify-content: space-between;
p {
margin: 6px 0 0;
color: #667085;
font-size: 14px;
line-height: 1.6;
}
}
.product-panel__title {
margin: 0;
color: #111827;
font-size: 22px !important;
font-weight: 700;
}
.product-panel__more {
height: 34px;
padding: 0 16px;
border: 1px solid #dbeafe;
border-radius: 999px;
background: #f4f8ff;
color: #1e6fff;
cursor: pointer;
font-size: 14px !important;
font-weight: 600;
}
.product-group {
margin-bottom: 24px;
h4 {
margin: 0 0 14px;
color: #111827;
font-size: 16px !important;
font-weight: 700;
}
}
.product-group--compact {
margin-bottom: 0;
}
.product-grid {
display: grid;
grid-template-columns: repeat(3, minmax(180px, 1fr));
gap: 16px;
}
.product-grid--compact {
grid-template-columns: repeat(4, minmax(150px, 1fr));
}
.product-card {
min-height: 86px;
padding: 16px;
border: 1px solid #edf1f7;
border-radius: 16px;
background: #fff;
color: #111827;
cursor: pointer;
display: flex;
align-items: center;
gap: 14px;
text-align: left;
font-size: 15px !important;
font-weight: 600;
line-height: 1.5;
box-shadow: 0 8px 22px rgba(31, 45, 61, 0.04);
transition: all 0.2s ease;
&:hover {
transform: translateY(-2px);
border-color: #cfe0ff;
box-shadow: 0 14px 28px rgba(30, 111, 255, 0.1);
}
strong {
display: block;
margin-bottom: 6px;
color: #111827;
font-size: 16px !important;
font-weight: 700;
}
em {
display: block;
color: #6b7280;
font-size: 13px !important;
font-weight: 400;
font-style: normal;
}
&:hover,
&:hover strong {
color: #1e6fff;
}
}
.product-card__icon {
width: 42px;
height: 42px;
border-radius: 12px;
display: inline-flex !important;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #fff !important;
font-size: 13px !important;
font-weight: 800 !important;
}
.product-card__text {
min-width: 0;
}
.product-card--blue .product-card__icon {
background: linear-gradient(135deg, #2563eb, #38bdf8);
}
.product-card--purple .product-card__icon {
background: linear-gradient(135deg, #7c3aed, #d946ef);
}
.product-card--green .product-card__icon {
background: linear-gradient(135deg, #059669, #22c55e);
}
.product-card--plain {
min-height: 74px;
padding: 14px 16px;
box-shadow: none;
}
.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>