mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-27 01:23:55 +08:00
feat: 完成导航菜单组件样式优化
This commit is contained in:
@@ -101,7 +101,8 @@
|
|||||||
$route.path === '/agent-template-management' ||
|
$route.path === '/agent-template-management' ||
|
||||||
$route.path === '/ota-management' ||
|
$route.path === '/ota-management' ||
|
||||||
$route.path === '/user-management' ||
|
$route.path === '/user-management' ||
|
||||||
$route.path === '/feature-management',
|
$route.path === '/feature-management' ||
|
||||||
|
$route.path === '/replacement-word-management'
|
||||||
}" @visible-change="handleParamDropdownVisibleChange">
|
}" @visible-change="handleParamDropdownVisibleChange">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
<img loading="lazy" alt="" src="@/assets/header/param_management.png" :style="{
|
<img loading="lazy" alt="" src="@/assets/header/param_management.png" :style="{
|
||||||
@@ -113,7 +114,8 @@
|
|||||||
$route.path === '/agent-template-management' ||
|
$route.path === '/agent-template-management' ||
|
||||||
$route.path === '/ota-management' ||
|
$route.path === '/ota-management' ||
|
||||||
$route.path === '/user-management' ||
|
$route.path === '/user-management' ||
|
||||||
$route.path === '/feature-management'
|
$route.path === '/feature-management' ||
|
||||||
|
$route.path === '/replacement-word-management'
|
||||||
? 'brightness(0) invert(1)'
|
? 'brightness(0) invert(1)'
|
||||||
: 'None',
|
: 'None',
|
||||||
}" />
|
}" />
|
||||||
@@ -154,32 +156,6 @@
|
|||||||
|
|
||||||
<!-- 右侧元素 -->
|
<!-- 右侧元素 -->
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="search-container" v-if="$route.path === '/home' && !(userInfo.superAdmin && 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" />
|
<img loading="lazy" alt="" src="@/assets/home/avatar.png" class="avatar-img" @click="handleAvatarClick" />
|
||||||
<span class="el-user-dropdown" @click="handleAvatarClick">
|
<span class="el-user-dropdown" @click="handleAvatarClick">
|
||||||
{{ userInfo.username || "加载中..." }}
|
{{ userInfo.username || "加载中..." }}
|
||||||
@@ -220,12 +196,6 @@ export default {
|
|||||||
voiceCloneDropdownVisible: false,
|
voiceCloneDropdownVisible: false,
|
||||||
userMenuVisible: false, // 添加用户菜单可见状态
|
userMenuVisible: false, // 添加用户菜单可见状态
|
||||||
menuVisibleTimer: null, // 菜单显示定时器,防止够快触发
|
menuVisibleTimer: null, // 菜单显示定时器,防止够快触发
|
||||||
isSmallScreen: false,
|
|
||||||
// 搜索历史相关
|
|
||||||
searchHistory: [],
|
|
||||||
showHistory: false,
|
|
||||||
SEARCH_HISTORY_KEY: "xiaozhi_search_history",
|
|
||||||
MAX_HISTORY_COUNT: 3,
|
|
||||||
// Cascader 配置
|
// Cascader 配置
|
||||||
cascaderProps: {
|
cascaderProps: {
|
||||||
expandTrigger: "click",
|
expandTrigger: "click",
|
||||||
@@ -351,17 +321,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.checkScreenSize();
|
|
||||||
window.addEventListener("resize", this.checkScreenSize);
|
|
||||||
// 从localStorage加载搜索历史
|
|
||||||
this.loadSearchHistory();
|
|
||||||
// 等待featureManager初始化完成后再加载功能状态
|
// 等待featureManager初始化完成后再加载功能状态
|
||||||
await this.loadFeatureStatus();
|
await this.loadFeatureStatus();
|
||||||
},
|
},
|
||||||
//移除事件监听器
|
|
||||||
beforeDestroy() {
|
|
||||||
window.removeEventListener("resize", this.checkScreenSize);
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
handleRouter(type) {
|
handleRouter(type) {
|
||||||
this.$router.push(this.routerPaths[type]);
|
this.$router.push(this.routerPaths[type]);
|
||||||
@@ -371,104 +333,6 @@ export default {
|
|||||||
// 等待featureManager初始化完成
|
// 等待featureManager初始化完成
|
||||||
await featureManager.waitForInitialization();
|
await featureManager.waitForInitialization();
|
||||||
},
|
},
|
||||||
checkScreenSize() {
|
|
||||||
this.isSmallScreen = window.innerWidth <= 1386;
|
|
||||||
},
|
|
||||||
// 处理搜索
|
|
||||||
handleSearch() {
|
|
||||||
const searchValue = this.search.trim();
|
|
||||||
|
|
||||||
// 如果搜索内容为空,触发重置事件
|
|
||||||
if (!searchValue) {
|
|
||||||
this.$emit("search-reset");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存搜索历史
|
|
||||||
this.saveSearchHistory(searchValue);
|
|
||||||
|
|
||||||
// 触发搜索事件,将搜索关键词传递给父组件
|
|
||||||
this.$emit("search", searchValue);
|
|
||||||
|
|
||||||
// 搜索完成后让输入框失去焦点,从而触发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() {
|
showChangePasswordDialog() {
|
||||||
this.isChangePasswordDialogVisible = true;
|
this.isChangePasswordDialogVisible = true;
|
||||||
@@ -631,11 +495,9 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.header {
|
.header {
|
||||||
background: #f6fcfe66;
|
background: linear-gradient(180deg, #dfeafe, #eff4ff);
|
||||||
border: 1px solid #fff;
|
|
||||||
height: 63px !important;
|
height: 63px !important;
|
||||||
min-width: 900px;
|
min-width: 900px;
|
||||||
/* 设置最小宽度防止过度压缩 */
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,7 +513,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 120px;
|
min-width: 130px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,42 +530,53 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 25px;
|
gap: 25px;
|
||||||
position: absolute;
|
background: white;
|
||||||
left: 50%;
|
border-radius: 30px;
|
||||||
transform: translateX(calc(-50% - 80px));
|
box-shadow: 0 0 6px 0px #cfe1fb;
|
||||||
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-right {
|
.header-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
min-width: 300px;
|
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.equipment-management {
|
.equipment-management {
|
||||||
height: 30px;
|
padding: 8px 16px;
|
||||||
border-radius: 15px;
|
border-radius: 30px;
|
||||||
background: #deeafe;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
color: #3d4566;
|
color: #6c79a8;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
/* 防止导航按钮被压缩 */
|
|
||||||
padding: 0 15px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.equipment-management.active-tab {
|
.equipment-management.active-tab {
|
||||||
background: #5778ff !important;
|
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
|
background: linear-gradient(90deg, #2983fe 0%, #5251fc 100%);
|
||||||
|
box-shadow: 0 1px 8px rgba(41, 131, 254, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-management.active-tab::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 50%;
|
||||||
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.equipment-management img {
|
.equipment-management img {
|
||||||
@@ -711,121 +584,6 @@ export default {
|
|||||||
height: 13px;
|
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 {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.more-dropdown .el-dropdown-link {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
.avatar-img {
|
||||||
width: 21px;
|
width: 21px;
|
||||||
height: 21px;
|
height: 21px;
|
||||||
|
|||||||
Reference in New Issue
Block a user