Merge pull request #2326 from xinnan-tech/css

修复修改密码无法重复点击和恢复样式
This commit is contained in:
FAN-yeB
2025-10-11 15:45:29 +08:00
committed by GitHub
+389 -153
View File
@@ -9,80 +9,183 @@
<!-- 中间导航菜单 --> <!-- 中间导航菜单 -->
<div class="header-center"> <div class="header-center">
<div class="equipment-management" <div
:class="{ 'active-tab': $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' }" class="equipment-management"
@click="goHome"> :class="{
<img loading="lazy" alt="" src="@/assets/header/robot.png" 'active-tab':
:style="{ filter: $route.path === '/home' || $route.path === '/role-config' || $route.path === '/device-management' ? 'brightness(0) invert(1)' : 'None' }" /> $route.path === '/home' ||
<span class="nav-text">{{ $t('header.smartManagement') }}</span> $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>
<!-- 普通用户显示音色克隆 --> <!-- 普通用户显示音色克隆 -->
<div v-if="!isSuperAdmin" class="equipment-management" <div
:class="{ 'active-tab': $route.path === '/voice-clone-management' }" @click="goVoiceCloneManagement"> v-if="!isSuperAdmin"
<img loading="lazy" alt="" src="@/assets/header/voice.png" class="equipment-management"
:style="{ filter: $route.path === '/voice-clone-management' ? 'brightness(0) invert(1)' : 'None' }" /> :class="{ 'active-tab': $route.path === '/voice-clone-management' }"
<span class="nav-text">{{ $t('header.voiceCloneManagement') }}</span> @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> </div>
<!-- 超级管理员显示音色克隆下拉菜单 --> <!-- 超级管理员显示音色克隆下拉菜单 -->
<el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown" <el-dropdown
:class="{ 'active-tab': $route.path === '/voice-clone-management' || $route.path === '/voice-resource-management' }" v-if="isSuperAdmin"
@visible-change="handleVoiceCloneDropdownVisibleChange"> 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"> <span class="el-dropdown-link">
<img loading="lazy" alt="" src="@/assets/header/voice.png" <img
:style="{ filter: $route.path === '/voice-clone-management' || $route.path === '/voice-resource-management' ? 'brightness(0) invert(1)' : 'None' }" /> loading="lazy"
<span class="nav-text">{{ $t('header.voiceCloneManagement') }}</span> alt=""
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': voiceCloneDropdownVisible }"></i> 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> </span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="goVoiceCloneManagement"> <el-dropdown-item @click.native="goVoiceCloneManagement">
{{ $t('header.voiceCloneManagement') }} {{ $t("header.voiceCloneManagement") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goVoiceResourceManagement"> <el-dropdown-item @click.native="goVoiceResourceManagement">
{{ $t('header.voiceResourceManagement') }} {{ $t("header.voiceResourceManagement") }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<div v-if="isSuperAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }" <div
@click="goModelConfig"> v-if="isSuperAdmin"
<img loading="lazy" alt="" src="@/assets/header/model_config.png" class="equipment-management"
:style="{ filter: $route.path === '/model-config' ? 'brightness(0) invert(1)' : 'None' }" /> :class="{ 'active-tab': $route.path === '/model-config' }"
<span class="nav-text">{{ $t('header.modelConfig') }}</span> @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>
<div v-if="isSuperAdmin" class="equipment-management" <div
:class="{ 'active-tab': $route.path === '/user-management' }" @click="goUserManagement"> v-if="isSuperAdmin"
<img loading="lazy" alt="" src="@/assets/header/user_management.png" class="equipment-management"
:style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }" /> :class="{ 'active-tab': $route.path === '/user-management' }"
<span class="nav-text">{{ $t('header.userManagement') }}</span> @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> </div>
<el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown" <el-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' }" v-if="isSuperAdmin"
@visible-change="handleParamDropdownVisibleChange"> 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"> <span class="el-dropdown-link">
<img loading="lazy" alt="" src="@/assets/header/param_management.png" <img
: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' }" /> loading="lazy"
<span class="nav-text">{{ $t('header.paramDictionary') }}</span> alt=""
<i class="el-icon-arrow-down el-icon--right" :class="{ 'rotate-down': paramDropdownVisible }"></i> 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> </span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="goOtaManagement"> <el-dropdown-item @click.native="goOtaManagement">
{{ $t('header.otaManagement') }} {{ $t("header.otaManagement") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goParamManagement"> <el-dropdown-item @click.native="goParamManagement">
{{ $t('header.paramManagement') }} {{ $t("header.paramManagement") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goDictManagement"> <el-dropdown-item @click.native="goDictManagement">
{{ $t('header.dictManagement') }} {{ $t("header.dictManagement") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goProviderManagement"> <el-dropdown-item @click.native="goProviderManagement">
{{ $t('header.providerManagement') }} {{ $t("header.providerManagement") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goAgentTemplateManagement"> <el-dropdown-item @click.native="goAgentTemplateManagement">
{{ $t('header.agentTemplate') }} {{ $t("header.agentTemplate") }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="goServerSideManagement"> <el-dropdown-item @click.native="goServerSideManagement">
{{ $t('header.serverSideManagement') }} {{ $t("header.serverSideManagement") }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
@@ -90,40 +193,85 @@
<!-- 右侧元素 --> <!-- 右侧元素 -->
<div class="header-right"> <div class="header-right">
<div class="search-container" v-if="$route.path === '/home' && !(isSuperAdmin && isSmallScreen)"> <div
class="search-container"
v-if="$route.path === '/home' && !(isSuperAdmin && isSmallScreen)"
>
<div class="search-wrapper"> <div class="search-wrapper">
<el-input v-model="search" :placeholder="$t('header.searchPlaceholder')" class="custom-search-input" <el-input
@keyup.enter.native="handleSearch" @focus="showSearchHistory" @blur="hideSearchHistory" clearable v-model="search"
ref="searchInput"> :placeholder="$t('header.searchPlaceholder')"
<i slot="suffix" class="el-icon-search search-icon" @click="handleSearch"></i> 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> </el-input>
<!-- 搜索历史下拉框 --> <!-- 搜索历史下拉框 -->
<div v-if="showHistory && searchHistory.length > 0" class="search-history-dropdown"> <div
v-if="showHistory && searchHistory.length > 0"
class="search-history-dropdown"
>
<div class="search-history-header"> <div class="search-history-header">
<span>{{ $t('header.searchHistory') }}</span> <span>{{ $t("header.searchHistory") }}</span>
<el-button type="text" size="small" class="clear-history-btn" @click="clearSearchHistory"> <el-button
{{ $t('header.clearHistory') }} type="text"
size="small"
class="clear-history-btn"
@click="clearSearchHistory"
>
{{ $t("header.clearHistory") }}
</el-button> </el-button>
</div> </div>
<div class="search-history-list"> <div class="search-history-list">
<div v-for="(item, index) in searchHistory" :key="index" class="search-history-item" <div
@click.stop="selectSearchHistory(item)"> v-for="(item, index) in searchHistory"
:key="index"
class="search-history-item"
@click.stop="selectSearchHistory(item)"
>
<span class="history-text">{{ item }}</span> <span class="history-text">{{ item }}</span>
<i class="el-icon-close clear-item-icon" @click.stop="removeSearchHistory(index)"></i> <i
class="el-icon-close clear-item-icon"
@click.stop="removeSearchHistory(index)"
></i>
</div> </div>
</div> </div>
</div> </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-dropdown-link" @click="handleAvatarClick"> <span class="el-dropdown-link" @click="handleAvatarClick">
{{ userInfo.username || '加载中...' }} {{ userInfo.username || "加载中..." }}
<i class="el-icon-arrow-down el-icon--right"></i> <i
class="el-icon-arrow-down el-icon--right"
:class="{ 'rotate-down': userMenuVisible }"
></i>
</span> </span>
<el-cascader :options="userMenuOptions" trigger="click" :props="cascaderProps" <el-cascader
style="width: 0px;overflow: hidden;" :show-all-levels="false" @change="handleCascaderChange" :options="userMenuOptions"
ref="userCascader"> trigger="click"
:props="cascaderProps"
style="width: 0px; overflow: hidden"
:show-all-levels="false"
@change="handleCascaderChange"
@visible-change="handleUserMenuVisibleChange"
ref="userCascader"
>
<template slot-scope="{ data }"> <template slot-scope="{ data }">
<span>{{ data.label }}</span> <span>{{ data.label }}</span>
</template> </template>
@@ -137,63 +285,64 @@
</template> </template>
<script> <script>
import userApi from '@/apis/module/user'; import userApi from "@/apis/module/user";
import i18n, { changeLanguage } from '@/i18n'; import i18n, { changeLanguage } from "@/i18n";
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from "vuex";
import ChangePasswordDialog from './ChangePasswordDialog.vue'; // 引入修改密码弹窗组件 import ChangePasswordDialog from "./ChangePasswordDialog.vue"; // 引入修改密码弹窗组件
export default { export default {
name: 'HeaderBar', name: "HeaderBar",
components: { components: {
ChangePasswordDialog ChangePasswordDialog,
}, },
props: ['devices'], // 接收父组件设备列表 props: ["devices"], // 接收父组件设备列表
data() { data() {
return { return {
search: '', search: "",
userInfo: { userInfo: {
username: '', username: "",
mobile: '' mobile: "",
}, },
isChangePasswordDialogVisible: false, // 控制修改密码弹窗的显示 isChangePasswordDialogVisible: false, // 控制修改密码弹窗的显示
paramDropdownVisible: false, paramDropdownVisible: false,
voiceCloneDropdownVisible: false, voiceCloneDropdownVisible: false,
userMenuVisible: false, // 添加用户菜单可见状态
isSmallScreen: false, isSmallScreen: false,
// 搜索历史相关 // 搜索历史相关
searchHistory: [], searchHistory: [],
showHistory: false, showHistory: false,
SEARCH_HISTORY_KEY: 'xiaozhi_search_history', SEARCH_HISTORY_KEY: "xiaozhi_search_history",
MAX_HISTORY_COUNT: 3, MAX_HISTORY_COUNT: 3,
// Cascader 配置 // Cascader 配置
cascaderProps: { cascaderProps: {
expandTrigger: 'click', expandTrigger: "click",
value: 'value', value: "value",
label: 'label', label: "label",
children: 'children' children: "children",
} },
} };
}, },
computed: { computed: {
...mapGetters(['getIsSuperAdmin']), ...mapGetters(["getIsSuperAdmin"]),
isSuperAdmin() { isSuperAdmin() {
return this.getIsSuperAdmin; return this.getIsSuperAdmin;
}, },
// 获取当前语言 // 获取当前语言
currentLanguage() { currentLanguage() {
return i18n.locale || 'zh_CN'; return i18n.locale || "zh_CN";
}, },
// 获取当前语言显示文本 // 获取当前语言显示文本
currentLanguageText() { currentLanguageText() {
const currentLang = this.currentLanguage; const currentLang = this.currentLanguage;
switch (currentLang) { switch (currentLang) {
case 'zh_CN': case "zh_CN":
return this.$t('language.zhCN'); return this.$t("language.zhCN");
case 'zh_TW': case "zh_TW":
return this.$t('language.zhTW'); return this.$t("language.zhTW");
case 'en': case "en":
return this.$t('language.en'); return this.$t("language.en");
default: default:
return this.$t('language.zhCN'); return this.$t("language.zhCN");
} }
}, },
// 用户菜单选项 // 用户菜单选项
@@ -201,90 +350,90 @@ export default {
return [ return [
{ {
label: this.currentLanguageText, label: this.currentLanguageText,
value: 'language', value: "language",
children: [ children: [
{ {
label: this.$t('language.zhCN'), label: this.$t("language.zhCN"),
value: 'zh_CN' value: "zh_CN",
}, },
{ {
label: this.$t('language.zhTW'), label: this.$t("language.zhTW"),
value: 'zh_TW' value: "zh_TW",
}, },
{ {
label: this.$t('language.en'), label: this.$t("language.en"),
value: 'en' value: "en",
} },
] ],
}, },
{ {
label: this.$t('header.changePassword'), label: this.$t("header.changePassword"),
value: 'changePassword' value: "changePassword",
}, },
{ {
label: this.$t('header.logout'), label: this.$t("header.logout"),
value: 'logout' value: "logout",
} },
]; ];
} },
}, },
mounted() { mounted() {
this.fetchUserInfo(); this.fetchUserInfo();
this.checkScreenSize(); this.checkScreenSize();
window.addEventListener('resize', this.checkScreenSize); window.addEventListener("resize", this.checkScreenSize);
// 从localStorage加载搜索历史 // 从localStorage加载搜索历史
this.loadSearchHistory(); this.loadSearchHistory();
}, },
//移除事件监听器 //移除事件监听器
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.checkScreenSize); window.removeEventListener("resize", this.checkScreenSize);
}, },
methods: { methods: {
goHome() { goHome() {
// 跳转到首页 // 跳转到首页
this.$router.push('/home') this.$router.push("/home");
}, },
goUserManagement() { goUserManagement() {
this.$router.push('/user-management') this.$router.push("/user-management");
}, },
goModelConfig() { goModelConfig() {
this.$router.push('/model-config') this.$router.push("/model-config");
}, },
goVoiceCloneManagement() { goVoiceCloneManagement() {
this.$router.push('/voice-clone-management') this.$router.push("/voice-clone-management");
}, },
goParamManagement() { goParamManagement() {
this.$router.push('/params-management') this.$router.push("/params-management");
}, },
goOtaManagement() { goOtaManagement() {
this.$router.push('/ota-management') this.$router.push("/ota-management");
}, },
goDictManagement() { goDictManagement() {
this.$router.push('/dict-management') this.$router.push("/dict-management");
}, },
goProviderManagement() { goProviderManagement() {
this.$router.push('/provider-management') this.$router.push("/provider-management");
}, },
goServerSideManagement() { goServerSideManagement() {
this.$router.push('/server-side-management') this.$router.push("/server-side-management");
}, },
// 跳转到音色资源管理 // 跳转到音色资源管理
goVoiceResourceManagement() { goVoiceResourceManagement() {
this.$router.push('/voice-resource-management') this.$router.push("/voice-resource-management");
}, },
// 添加默认角色模板管理导航方法 // 添加默认角色模板管理导航方法
goAgentTemplateManagement() { goAgentTemplateManagement() {
this.$router.push('/agent-template-management') this.$router.push("/agent-template-management");
}, },
// 获取用户信息 // 获取用户信息
fetchUserInfo() { fetchUserInfo() {
userApi.getUserInfo(({ data }) => { userApi.getUserInfo(({ data }) => {
this.userInfo = data.data this.userInfo = data.data;
if (data.data.superAdmin !== undefined) { if (data.data.superAdmin !== undefined) {
this.$store.commit('setUserInfo', data.data); this.$store.commit("setUserInfo", data.data);
} }
}) });
}, },
checkScreenSize() { checkScreenSize() {
this.isSmallScreen = window.innerWidth <= 1386; this.isSmallScreen = window.innerWidth <= 1386;
@@ -295,7 +444,7 @@ export default {
// 如果搜索内容为空,触发重置事件 // 如果搜索内容为空,触发重置事件
if (!searchValue) { if (!searchValue) {
this.$emit('search-reset'); this.$emit("search-reset");
return; return;
} }
@@ -304,14 +453,14 @@ export default {
try { try {
// 创建不区分大小写的正则表达式 // 创建不区分大小写的正则表达式
const regex = new RegExp(searchValue, 'i'); const regex = new RegExp(searchValue, "i");
// 触发搜索事件,将正则表达式传递给父组件 // 触发搜索事件,将正则表达式传递给父组件
this.$emit('search', regex); this.$emit("search", regex);
} catch (error) { } catch (error) {
console.error('正则表达式创建失败:', error); console.error("正则表达式创建失败:", error);
this.$message.error({ this.$message.error({
message: this.$t('message.error'), message: this.$t("message.error"),
showClose: true showClose: true,
}); });
} }
@@ -342,7 +491,7 @@ export default {
this.searchHistory = JSON.parse(history); this.searchHistory = JSON.parse(history);
} }
} catch (error) { } catch (error) {
console.error('加载搜索历史失败:', error); console.error("加载搜索历史失败:", error);
this.searchHistory = []; this.searchHistory = [];
} }
}, },
@@ -365,7 +514,7 @@ export default {
try { try {
localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory)); localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory));
} catch (error) { } catch (error) {
console.error('保存搜索历史失败:', error); console.error("保存搜索历史失败:", error);
} }
}, },
@@ -381,7 +530,7 @@ export default {
try { try {
localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory)); localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory));
} catch (error) { } catch (error) {
console.error('更新搜索历史失败:', error); console.error("更新搜索历史失败:", error);
} }
}, },
@@ -391,12 +540,14 @@ export default {
try { try {
localStorage.removeItem(this.SEARCH_HISTORY_KEY); localStorage.removeItem(this.SEARCH_HISTORY_KEY);
} catch (error) { } catch (error) {
console.error('清空搜索历史失败:', error); console.error("清空搜索历史失败:", error);
} }
}, },
// 显示修改密码弹窗 // 显示修改密码弹窗
showChangePasswordDialog() { showChangePasswordDialog() {
this.isChangePasswordDialogVisible = true; this.isChangePasswordDialogVisible = true;
// 添加:显示修改密码弹窗后重置用户菜单可见状态
this.userMenuVisible = false;
}, },
// 退出登录 // 退出登录
async handleLogout() { async handleLogout() {
@@ -404,14 +555,14 @@ export default {
// 调用 Vuex 的 logout action // 调用 Vuex 的 logout action
await this.logout(); await this.logout();
this.$message.success({ this.$message.success({
message: this.$t('message.success'), message: this.$t("message.success"),
showClose: true showClose: true,
}); });
} catch (error) { } catch (error) {
console.error('退出登录失败:', error); console.error("退出登录失败:", error);
this.$message.error({ this.$message.error({
message: this.$t('message.error'), message: this.$t("message.error"),
showClose: true showClose: true,
}); });
} }
}, },
@@ -424,6 +575,7 @@ export default {
handleVoiceCloneDropdownVisibleChange(visible) { handleVoiceCloneDropdownVisibleChange(visible) {
this.voiceCloneDropdownVisible = visible; this.voiceCloneDropdownVisible = visible;
}, },
// 在data中添加一个key用于强制重新渲染组件
// 处理 Cascader 选择变化 // 处理 Cascader 选择变化
handleCascaderChange(value) { handleCascaderChange(value) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
@@ -433,41 +585,117 @@ export default {
const action = value[value.length - 1]; const action = value[value.length - 1];
// 处理语言切换 // 处理语言切换
if (value.length === 2 && value[0] === 'language') { if (value.length === 2 && value[0] === "language") {
this.changeLanguage(action); this.changeLanguage(action);
return; } else {
// 处理其他操作
switch (action) {
case "changePassword":
this.showChangePasswordDialog();
break;
case "logout":
this.handleLogout();
break;
}
} }
// 处理其他操作 // 操作完成后立即清空选择
switch (action) { setTimeout(() => {
case 'changePassword': this.completeResetCascader();
this.showChangePasswordDialog(); }, 300);
break;
case 'logout':
this.handleLogout();
break;
}
}, },
// 切换语言 // 切换语言
changeLanguage(lang) { changeLanguage(lang) {
changeLanguage(lang); changeLanguage(lang);
this.$message.success({ this.$message.success({
message: this.$t('message.success'), message: this.$t("message.success"),
showClose: true showClose: true,
}); });
// 添加:切换语言后重置用户菜单可见状态
this.userMenuVisible = false;
},
// 完全重置级联选择器
completeResetCascader() {
if (this.$refs.userCascader) {
try {
// 尝试所有可能的方法来清空选择
// 1. 尝试使用组件提供的clearValue方法
if (this.$refs.userCascader.clearValue) {
this.$refs.userCascader.clearValue();
}
// 2. 直接清空内部属性
if (this.$refs.userCascader.$data) {
this.$refs.userCascader.$data.selectedPaths = [];
this.$refs.userCascader.$data.displayLabels = [];
this.$refs.userCascader.$data.inputValue = "";
this.$refs.userCascader.$data.checkedValue = [];
this.$refs.userCascader.$data.showAllLevels = false;
}
// 3. 操作DOM清除选中状态
const menuElement = this.$refs.userCascader.$refs.menu;
if (menuElement && menuElement.$el) {
const activeItems = menuElement.$el.querySelectorAll(
".el-cascader-node.is-active"
);
activeItems.forEach((item) => item.classList.remove("is-active"));
const checkedItems = menuElement.$el.querySelectorAll(
".el-cascader-node.is-checked"
);
checkedItems.forEach((item) => item.classList.remove("is-checked"));
}
console.log("Cascader values cleared");
} catch (error) {
console.error("清空选择值失败:", error);
}
}
}, },
// 点击头像触发cascader下拉菜单 // 点击头像触发cascader下拉菜单
handleAvatarClick() { handleAvatarClick() {
if (this.$refs.userCascader) { if (this.$refs.userCascader) {
this.$refs.userCascader.toggleDropDownVisible(); // 切换菜单可见状态
this.userMenuVisible = !this.userMenuVisible;
// 菜单收起时清空选择值
if (!this.userMenuVisible) {
this.completeResetCascader();
}
// 直接设置菜单的显隐状态
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");
}
}
}
},
// 处理用户菜单可见性变化
handleUserMenuVisibleChange(visible) {
this.userMenuVisible = visible;
// 如果菜单关闭了,也要清空选择值
if (!visible) {
this.completeResetCascader();
} }
}, },
// 使用 mapActions 引入 Vuex 的 logout action // 使用 mapActions 引入 Vuex 的 logout action
...mapActions(['logout']) ...mapActions(["logout"]),
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -627,7 +855,6 @@ export default {
gap: 7px; gap: 7px;
} }
.search-history-item:hover .clear-item-icon { .search-history-item:hover .clear-item-icon {
visibility: visible; visibility: visible;
} }
@@ -636,7 +863,7 @@ export default {
color: #ff4949; color: #ff4949;
} }
.custom-search-input>>>.el-input__inner { .custom-search-input >>> .el-input__inner {
height: 18px; height: 18px;
border-radius: 9px; border-radius: 9px;
background-color: #fff; background-color: #fff;
@@ -706,4 +933,13 @@ export default {
color: #606266; color: #606266;
white-space: nowrap; white-space: nowrap;
} }
</style> /* 添加倒三角旋转样式 */
.rotate-down {
transform: rotate(180deg);
transition: transform 0.3s ease;
}
.el-icon-arrow-down {
transition: transform 0.3s ease;
}
</style>