mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
733 lines
21 KiB
Vue
733 lines
21 KiB
Vue
<template>
|
|
<el-header class="header">
|
|
<div class="header-container">
|
|
<!-- 左侧元素 -->
|
|
<div class="header-left" @click="goHome">
|
|
<img loading="lazy" alt="" src="@/assets/xiaozhi-logo.png" class="logo-img" />
|
|
<img loading="lazy" alt="" src="@/assets/xiaozhi-ai.png" class="brand-img" />
|
|
</div>
|
|
|
|
<!-- 中间导航菜单 -->
|
|
<div class="header-center">
|
|
<div class="equipment-management"
|
|
:class="{ 'active-tab': $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' }"
|
|
@click="goHome">
|
|
<img loading="lazy" alt="" src="@/assets/header/robot.png"
|
|
:style="{ filter: $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.smartManagement') }}</span>
|
|
</div>
|
|
<!-- 普通用户显示音色克隆 -->
|
|
<div v-if="!isSuperAdmin" class="equipment-management"
|
|
:class="{ 'active-tab': $route.path === '/voice-clone-management' }" @click="goVoiceCloneManagement">
|
|
<img loading="lazy" alt="" src="@/assets/header/voice.png"
|
|
:style="{ filter: $route.path === '/voice-clone-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.voiceCloneManagement') }}</span>
|
|
</div>
|
|
|
|
<!-- 超级管理员显示音色克隆下拉菜单 -->
|
|
<el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown"
|
|
:class="{ 'active-tab': $route.path === '/voice-clone-management' || $route.path === '/voice-resource-management' }"
|
|
@visible-change="handleVoiceCloneDropdownVisibleChange">
|
|
<span class="el-dropdown-link">
|
|
<img loading="lazy" alt="" src="@/assets/header/voice.png"
|
|
:style="{ filter: $route.path === '/voice-clone-management' || $route.path === '/voice-resource-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.voiceCloneManagement') }}</span>
|
|
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': voiceCloneDropdownVisible }"></i>
|
|
</span>
|
|
<el-dropdown-menu slot="dropdown">
|
|
<el-dropdown-item @click.native="goVoiceCloneManagement">
|
|
{{ $t('header.voiceCloneManagement') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goVoiceResourceManagement">
|
|
{{ $t('header.voiceResourceManagement') }}
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
|
|
<div v-if="isSuperAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }"
|
|
@click="goModelConfig">
|
|
<img loading="lazy" alt="" src="@/assets/header/model_config.png"
|
|
:style="{ filter: $route.path === '/model-config' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.modelConfig') }}</span>
|
|
</div>
|
|
<div v-if="isSuperAdmin" class="equipment-management"
|
|
:class="{ 'active-tab': $route.path === '/user-management' }" @click="goUserManagement">
|
|
<img loading="lazy" alt="" src="@/assets/header/user_management.png"
|
|
:style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.userManagement') }}</span>
|
|
</div>
|
|
<el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown"
|
|
:class="{ 'active-tab': $route.path === '/dict-management' || $route.path === '/params-management' || $route.path === '/provider-management' || $route.path === '/server-side-management' || $route.path === '/agent-template-management' || $route.path === '/ota-management' }"
|
|
@visible-change="handleParamDropdownVisibleChange">
|
|
<span class="el-dropdown-link">
|
|
<img loading="lazy" alt="" src="@/assets/header/param_management.png"
|
|
:style="{ filter: $route.path === '/dict-management' || $route.path === '/params-management' || $route.path === '/provider-management' || $route.path === '/server-side-management' || $route.path === '/agent-template-management' || $route.path === '/ota-management' ? 'brightness(0) invert(1)' : 'None' }" />
|
|
<span class="nav-text">{{ $t('header.paramDictionary') }}</span>
|
|
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': paramDropdownVisible }"></i>
|
|
</span>
|
|
<el-dropdown-menu slot="dropdown">
|
|
<el-dropdown-item @click.native="goOtaManagement">
|
|
{{ $t('header.otaManagement') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goParamManagement">
|
|
{{ $t('header.paramManagement') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goDictManagement">
|
|
{{ $t('header.dictManagement') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goProviderManagement">
|
|
{{ $t('header.providerManagement') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goAgentTemplateManagement">
|
|
{{ $t('header.agentTemplate') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item @click.native="goServerSideManagement">
|
|
{{ $t('header.serverSideManagement') }}
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
</div>
|
|
|
|
<!-- 右侧元素 -->
|
|
<div class="header-right">
|
|
<div class="search-container" v-if="$route.path === '/home' && !(isSuperAdmin && isSmallScreen)">
|
|
<div class="search-wrapper">
|
|
<el-input v-model="search" :placeholder="$t('header.searchPlaceholder')" class="custom-search-input"
|
|
@keyup.enter.native="handleSearch" @focus="showSearchHistory" @blur="hideSearchHistory" clearable
|
|
ref="searchInput">
|
|
<i slot="suffix" class="el-icon-search search-icon" @click="handleSearch"></i>
|
|
</el-input>
|
|
<!-- 搜索历史下拉框 -->
|
|
<div v-if="showHistory && searchHistory.length > 0" class="search-history-dropdown">
|
|
<div class="search-history-header">
|
|
<span>{{ $t('header.searchHistory') }}</span>
|
|
<el-button type="text" size="small" class="clear-history-btn" @click="clearSearchHistory">
|
|
{{ $t('header.clearHistory') }}
|
|
</el-button>
|
|
</div>
|
|
<div class="search-history-list">
|
|
<div v-for="(item, index) in searchHistory" :key="index" class="search-history-item"
|
|
@click.stop="selectSearchHistory(item)">
|
|
<span class="history-text">{{ item }}</span>
|
|
<i class="el-icon-close clear-item-icon" @click.stop="removeSearchHistory(index)"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<img loading="lazy" alt="" src="@/assets/home/avatar.png" class="avatar-img" @click="handleAvatarClick" />
|
|
<span class="el-dropdown-link" @click="handleAvatarClick">
|
|
{{ userInfo.username || '加载中...' }}
|
|
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': userMenuVisible }"></i>
|
|
</span>
|
|
<el-cascader :options="userMenuOptions" trigger="click" :props="cascaderProps"
|
|
style="width: 0px;overflow: hidden;" :show-all-levels="false" @change="handleCascaderChange"
|
|
ref="userCascader">
|
|
<template slot-scope="{ data }">
|
|
<span>{{ data.label }}</span>
|
|
</template>
|
|
</el-cascader>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 修改密码弹窗 -->
|
|
<ChangePasswordDialog v-model="isChangePasswordDialogVisible" />
|
|
</el-header>
|
|
</template>
|
|
|
|
<script>
|
|
import userApi from '@/apis/module/user';
|
|
import i18n, { changeLanguage } from '@/i18n';
|
|
import { mapActions, mapGetters } from 'vuex';
|
|
import ChangePasswordDialog from './ChangePasswordDialog.vue'; // 引入修改密码弹窗组件
|
|
|
|
export default {
|
|
name: 'HeaderBar',
|
|
components: {
|
|
ChangePasswordDialog
|
|
},
|
|
props: ['devices'], // 接收父组件设备列表
|
|
data() {
|
|
return {
|
|
search: '',
|
|
userInfo: {
|
|
username: '',
|
|
mobile: ''
|
|
},
|
|
isChangePasswordDialogVisible: false, // 控制修改密码弹窗的显示
|
|
paramDropdownVisible: false,
|
|
voiceCloneDropdownVisible: false,
|
|
userMenuVisible: false, // 添加用户菜单可见状态
|
|
isSmallScreen: false,
|
|
// 搜索历史相关
|
|
searchHistory: [],
|
|
showHistory: false,
|
|
SEARCH_HISTORY_KEY: 'xiaozhi_search_history',
|
|
MAX_HISTORY_COUNT: 3,
|
|
// Cascader 配置
|
|
cascaderProps: {
|
|
expandTrigger: 'click',
|
|
value: 'value',
|
|
label: 'label',
|
|
children: 'children'
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
...mapGetters(['getIsSuperAdmin']),
|
|
isSuperAdmin() {
|
|
return this.getIsSuperAdmin;
|
|
},
|
|
// 获取当前语言
|
|
currentLanguage() {
|
|
return i18n.locale || 'zh_CN';
|
|
},
|
|
// 获取当前语言显示文本
|
|
currentLanguageText() {
|
|
const currentLang = this.currentLanguage;
|
|
switch (currentLang) {
|
|
case 'zh_CN':
|
|
return this.$t('language.zhCN');
|
|
case 'zh_TW':
|
|
return this.$t('language.zhTW');
|
|
case 'en':
|
|
return this.$t('language.en');
|
|
default:
|
|
return this.$t('language.zhCN');
|
|
}
|
|
},
|
|
// 用户菜单选项
|
|
userMenuOptions() {
|
|
return [
|
|
{
|
|
label: this.currentLanguageText,
|
|
value: 'language',
|
|
children: [
|
|
{
|
|
label: this.$t('language.zhCN'),
|
|
value: 'zh_CN'
|
|
},
|
|
{
|
|
label: this.$t('language.zhTW'),
|
|
value: 'zh_TW'
|
|
},
|
|
{
|
|
label: this.$t('language.en'),
|
|
value: 'en'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
label: this.$t('header.changePassword'),
|
|
value: 'changePassword'
|
|
},
|
|
{
|
|
label: this.$t('header.logout'),
|
|
value: 'logout'
|
|
}
|
|
];
|
|
}
|
|
},
|
|
mounted() {
|
|
this.fetchUserInfo();
|
|
this.checkScreenSize();
|
|
window.addEventListener('resize', this.checkScreenSize);
|
|
// 从localStorage加载搜索历史
|
|
this.loadSearchHistory();
|
|
},
|
|
//移除事件监听器
|
|
beforeDestroy() {
|
|
window.removeEventListener('resize', this.checkScreenSize);
|
|
},
|
|
methods: {
|
|
goHome() {
|
|
// 跳转到首页
|
|
this.$router.push('/home')
|
|
},
|
|
goUserManagement() {
|
|
this.$router.push('/user-management')
|
|
},
|
|
goModelConfig() {
|
|
this.$router.push('/model-config')
|
|
},
|
|
goVoiceCloneManagement() {
|
|
this.$router.push('/voice-clone-management')
|
|
},
|
|
goParamManagement() {
|
|
this.$router.push('/params-management')
|
|
},
|
|
goOtaManagement() {
|
|
this.$router.push('/ota-management')
|
|
},
|
|
goDictManagement() {
|
|
this.$router.push('/dict-management')
|
|
},
|
|
goProviderManagement() {
|
|
this.$router.push('/provider-management')
|
|
},
|
|
goServerSideManagement() {
|
|
this.$router.push('/server-side-management')
|
|
},
|
|
|
|
// 跳转到音色资源管理
|
|
goVoiceResourceManagement() {
|
|
this.$router.push('/voice-resource-management')
|
|
},
|
|
// 添加默认角色模板管理导航方法
|
|
goAgentTemplateManagement() {
|
|
this.$router.push('/agent-template-management')
|
|
},
|
|
// 获取用户信息
|
|
fetchUserInfo() {
|
|
userApi.getUserInfo(({ data }) => {
|
|
this.userInfo = data.data
|
|
if (data.data.superAdmin !== undefined) {
|
|
this.$store.commit('setUserInfo', data.data);
|
|
}
|
|
})
|
|
},
|
|
checkScreenSize() {
|
|
this.isSmallScreen = window.innerWidth <= 1386;
|
|
},
|
|
// 处理搜索
|
|
handleSearch() {
|
|
const searchValue = this.search.trim();
|
|
|
|
// 如果搜索内容为空,触发重置事件
|
|
if (!searchValue) {
|
|
this.$emit('search-reset');
|
|
return;
|
|
}
|
|
|
|
// 保存搜索历史
|
|
this.saveSearchHistory(searchValue);
|
|
|
|
try {
|
|
// 创建不区分大小写的正则表达式
|
|
const regex = new RegExp(searchValue, 'i');
|
|
// 触发搜索事件,将正则表达式传递给父组件
|
|
this.$emit('search', regex);
|
|
} catch (error) {
|
|
console.error('正则表达式创建失败:', error);
|
|
this.$message.error({
|
|
message: this.$t('message.error'),
|
|
showClose: true
|
|
});
|
|
}
|
|
|
|
// 搜索完成后让输入框失去焦点,从而触发blur事件隐藏搜索历史
|
|
if (this.$refs.searchInput) {
|
|
this.$refs.searchInput.blur();
|
|
}
|
|
},
|
|
|
|
// 显示搜索历史
|
|
showSearchHistory() {
|
|
this.showHistory = true;
|
|
},
|
|
|
|
// 隐藏搜索历史
|
|
hideSearchHistory() {
|
|
// 延迟隐藏,以便点击事件能够执行
|
|
setTimeout(() => {
|
|
this.showHistory = false;
|
|
}, 200);
|
|
},
|
|
|
|
// 加载搜索历史
|
|
loadSearchHistory() {
|
|
try {
|
|
const history = localStorage.getItem(this.SEARCH_HISTORY_KEY);
|
|
if (history) {
|
|
this.searchHistory = JSON.parse(history);
|
|
}
|
|
} catch (error) {
|
|
console.error('加载搜索历史失败:', error);
|
|
this.searchHistory = [];
|
|
}
|
|
},
|
|
|
|
// 保存搜索历史
|
|
saveSearchHistory(keyword) {
|
|
if (!keyword || this.searchHistory.includes(keyword)) {
|
|
return;
|
|
}
|
|
|
|
// 添加到历史记录开头
|
|
this.searchHistory.unshift(keyword);
|
|
|
|
// 限制历史记录数量
|
|
if (this.searchHistory.length > this.MAX_HISTORY_COUNT) {
|
|
this.searchHistory = this.searchHistory.slice(0, this.MAX_HISTORY_COUNT);
|
|
}
|
|
|
|
// 保存到localStorage
|
|
try {
|
|
localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory));
|
|
} catch (error) {
|
|
console.error('保存搜索历史失败:', error);
|
|
}
|
|
},
|
|
|
|
// 选择搜索历史项
|
|
selectSearchHistory(keyword) {
|
|
this.search = keyword;
|
|
this.handleSearch();
|
|
},
|
|
|
|
// 移除单个搜索历史项
|
|
removeSearchHistory(index) {
|
|
this.searchHistory.splice(index, 1);
|
|
try {
|
|
localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory));
|
|
} catch (error) {
|
|
console.error('更新搜索历史失败:', error);
|
|
}
|
|
},
|
|
|
|
// 清空所有搜索历史
|
|
clearSearchHistory() {
|
|
this.searchHistory = [];
|
|
try {
|
|
localStorage.removeItem(this.SEARCH_HISTORY_KEY);
|
|
} catch (error) {
|
|
console.error('清空搜索历史失败:', error);
|
|
}
|
|
},
|
|
// 显示修改密码弹窗
|
|
showChangePasswordDialog() {
|
|
this.isChangePasswordDialogVisible = true;
|
|
},
|
|
// 退出登录
|
|
async handleLogout() {
|
|
try {
|
|
// 调用 Vuex 的 logout action
|
|
await this.logout();
|
|
this.$message.success({
|
|
message: this.$t('message.success'),
|
|
showClose: true
|
|
});
|
|
} catch (error) {
|
|
console.error('退出登录失败:', error);
|
|
this.$message.error({
|
|
message: this.$t('message.error'),
|
|
showClose: true
|
|
});
|
|
}
|
|
},
|
|
// 监听参数字典下拉菜单的可见状态变化
|
|
handleParamDropdownVisibleChange(visible) {
|
|
this.paramDropdownVisible = visible;
|
|
},
|
|
|
|
// 监听音色克隆下拉菜单的可见状态变化
|
|
handleVoiceCloneDropdownVisibleChange(visible) {
|
|
this.voiceCloneDropdownVisible = visible;
|
|
},
|
|
// 处理 Cascader 选择变化
|
|
handleCascaderChange(value) {
|
|
if (!value || value.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const action = value[value.length - 1];
|
|
|
|
// 处理语言切换
|
|
if (value.length === 2 && value[0] === 'language') {
|
|
this.changeLanguage(action);
|
|
return;
|
|
}
|
|
|
|
// 处理其他操作
|
|
switch (action) {
|
|
case 'changePassword':
|
|
this.showChangePasswordDialog();
|
|
break;
|
|
case 'logout':
|
|
this.handleLogout();
|
|
break;
|
|
}
|
|
},
|
|
// 切换语言
|
|
changeLanguage(lang) {
|
|
changeLanguage(lang);
|
|
this.$message.success({
|
|
message: this.$t('message.success'),
|
|
showClose: true
|
|
});
|
|
},
|
|
|
|
// 点击头像触发cascader下拉菜单
|
|
handleAvatarClick() {
|
|
if (this.$refs.userCascader) {
|
|
// 切换菜单可见状态
|
|
this.userMenuVisible = !this.userMenuVisible;
|
|
|
|
// 直接设置菜单的显隐状态
|
|
try {
|
|
// 尝试使用toggleDropDownVisible方法
|
|
this.$refs.userCascader.toggleDropDownVisible(this.userMenuVisible);
|
|
} catch (error) {
|
|
// 如果toggle方法失败,尝试直接设置属性
|
|
if (this.$refs.userCascader.$refs.menu) {
|
|
this.$refs.userCascader.$refs.menu.showMenu(this.userMenuVisible);
|
|
} else {
|
|
console.error('Cannot access menu component');
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// 使用 mapActions 引入 Vuex 的 logout action
|
|
...mapActions(['logout'])
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.header {
|
|
background: #f6fcfe66;
|
|
border: 1px solid #fff;
|
|
height: 63px !important;
|
|
min-width: 900px;
|
|
/* 设置最小宽度防止过度压缩 */
|
|
overflow: visible;
|
|
}
|
|
|
|
.header-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
height: 100%;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.logo-img {
|
|
width: 42px;
|
|
height: 42px;
|
|
}
|
|
|
|
.brand-img {
|
|
height: 20px;
|
|
}
|
|
|
|
.header-center {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 25px;
|
|
position: absolute;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.header-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
min-width: 300px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.equipment-management {
|
|
height: 30px;
|
|
border-radius: 15px;
|
|
background: #deeafe;
|
|
display: flex;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
gap: 7px;
|
|
color: #3d4566;
|
|
margin-left: 1px;
|
|
align-items: center;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
/* 防止导航按钮被压缩 */
|
|
padding: 0 15px;
|
|
position: relative;
|
|
}
|
|
|
|
.equipment-management.active-tab {
|
|
background: #5778ff !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
.equipment-management img {
|
|
width: 15px;
|
|
height: 13px;
|
|
}
|
|
|
|
.search-container {
|
|
margin-right: 5px;
|
|
flex: 0.9;
|
|
min-width: 60px;
|
|
max-width: none;
|
|
}
|
|
|
|
.search-wrapper {
|
|
position: relative;
|
|
}
|
|
|
|
.search-history-dropdown {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
background: white;
|
|
border: 1px solid #e4e6ef;
|
|
border-radius: 4px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
z-index: 1000;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.search-history-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 12px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
font-size: 12px;
|
|
color: #909399;
|
|
}
|
|
|
|
.clear-history-btn {
|
|
color: #909399;
|
|
font-size: 11px;
|
|
padding: 0;
|
|
height: auto;
|
|
}
|
|
|
|
.clear-history-btn:hover {
|
|
color: #606266;
|
|
}
|
|
|
|
.search-history-list {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.search-history-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 12px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
color: #606266;
|
|
}
|
|
|
|
.search-history-item:hover {
|
|
background-color: #f5f7fa;
|
|
}
|
|
|
|
.clear-item-icon {
|
|
font-size: 10px;
|
|
color: #909399;
|
|
visibility: hidden;
|
|
}
|
|
|
|
.more-dropdown .el-dropdown-link {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
}
|
|
|
|
|
|
.search-history-item:hover .clear-item-icon {
|
|
visibility: visible;
|
|
}
|
|
|
|
.clear-item-icon:hover {
|
|
color: #ff4949;
|
|
}
|
|
|
|
.custom-search-input>>>.el-input__inner {
|
|
height: 18px;
|
|
border-radius: 9px;
|
|
background-color: #fff;
|
|
border: 1px solid #e4e6ef;
|
|
padding-left: 8px;
|
|
font-size: 9px;
|
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
width: 100%;
|
|
}
|
|
|
|
.search-icon {
|
|
cursor: pointer;
|
|
color: #909399;
|
|
margin-right: 3px;
|
|
font-size: 9px;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.custom-search-input::v-deep .el-input__suffix-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
|
|
.avatar-img {
|
|
width: 21px;
|
|
height: 21px;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* 导航文本样式 - 支持中英文换行 */
|
|
.nav-text {
|
|
white-space: normal;
|
|
text-align: center;
|
|
max-width: 80px;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
/* 响应式调整 */
|
|
@media (max-width: 1200px) {
|
|
.header-center {
|
|
gap: 14px;
|
|
}
|
|
|
|
.equipment-management {
|
|
width: 79px;
|
|
font-size: 9px;
|
|
}
|
|
}
|
|
|
|
.equipment-management.more-dropdown {
|
|
position: relative;
|
|
}
|
|
|
|
.equipment-management.more-dropdown .el-dropdown-menu {
|
|
position: absolute;
|
|
right: 0;
|
|
min-width: 120px;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.el-dropdown-menu__item {
|
|
min-width: 60px;
|
|
padding: 8px 20px;
|
|
font-size: 14px;
|
|
color: #606266;
|
|
white-space: nowrap;
|
|
}
|
|
/* 添加倒三角旋转样式 */
|
|
.rotate-down {
|
|
transform: rotate(180deg);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.el-icon-arrow-down {
|
|
transition: transform 0.3s ease;
|
|
}
|
|
</style> |