diff --git a/main/manager-web/.env b/main/manager-web/.env index 1b5f8138..3fd5202e 100644 --- a/main/manager-web/.env +++ b/main/manager-web/.env @@ -1 +1,3 @@ -VUE_APP_TITLE=智控台 \ No newline at end of file +VUE_APP_TITLE=智控台 +VUE_APP_DESCRIPTION=是由华南理工大学刘思源教授团队研发的智能终端软硬件体系后端服务系统,专为xiaozhi-esp32开源硬件打造,具备多协议兼容、声纹识别、知识库管理等核心能力。 +VUE_APP_KEYWORDS=xiaozhi-server,小智服务端,智控台,AI硬件,智能硬件,AI玩具,情感陪伴,聊天机器人,智能家居,车载机器人 diff --git a/main/manager-web/public/index.html b/main/manager-web/public/index.html index e3ae5a53..3cd3bb73 100644 --- a/main/manager-web/public/index.html +++ b/main/manager-web/public/index.html @@ -5,6 +5,8 @@ + + <%= process.env.VUE_APP_TITLE %> @@ -17,10 +19,6 @@ </head> <body> - <noscript> - <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. - Please enable it to continue.</strong> - </noscript> <div id="app"></div> <!-- built files will be auto injected --> <% if (htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> diff --git a/main/manager-web/src/App.vue b/main/manager-web/src/App.vue index 180f8f0d..bf4c7a45 100644 --- a/main/manager-web/src/App.vue +++ b/main/manager-web/src/App.vue @@ -60,6 +60,11 @@ export default { isCDNEnabled: process.env.VUE_APP_USE_CDN === 'true' }; }, + created() { + // 挂载 store 状态 + this.$store.commit('setUserInfo', JSON.parse(localStorage.getItem('userInfo') || '{}')); + this.$store.commit('setPubConfig', JSON.parse(localStorage.getItem('pubConfig') || '{}')); + }, mounted() { // 检测是否为移动设备且VUE_APP_H5_URL不为空,如果两个条件都满足则跳转到H5页面 if (this.isMobileDevice() && process.env.VUE_APP_H5_URL) { diff --git a/main/manager-web/src/components/HeaderBar.vue b/main/manager-web/src/components/HeaderBar.vue index 24f55ab9..6e3196d8 100644 --- a/main/manager-web/src/components/HeaderBar.vue +++ b/main/manager-web/src/components/HeaderBar.vue @@ -26,7 +26,7 @@ <span class="nav-text">{{ $t("header.smartManagement") }}</span> </div> <!-- 普通用户显示音色克隆 --> - <div v-if="!isSuperAdmin && featureStatus.voiceClone" class="equipment-management" + <div v-if="!userInfo.superAdmin && featureStatus.voiceClone" class="equipment-management" :class="{ 'active-tab': $route.path === '/voice-clone-management' }" @click="goVoiceCloneManagement"> <img loading="lazy" alt="" src="@/assets/header/voice.png" :style="{ filter: @@ -38,7 +38,7 @@ </div> <!-- 超级管理员显示音色克隆下拉菜单 --> - <el-dropdown v-if="isSuperAdmin && featureStatus.voiceClone" trigger="click" class="equipment-management more-dropdown" :class="{ + <el-dropdown v-if="userInfo.superAdmin && featureStatus.voiceClone" trigger="click" class="equipment-management more-dropdown" :class="{ 'active-tab': $route.path === '/voice-clone-management' || $route.path === '/voice-resource-management', @@ -64,7 +64,7 @@ </el-dropdown-menu> </el-dropdown> - <div v-if="isSuperAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }" + <div v-if="userInfo.superAdmin" class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }" @click="goModelConfig"> <img loading="lazy" alt="" src="@/assets/header/model_config.png" :style="{ filter: @@ -81,7 +81,7 @@ }" /> <span class="nav-text">{{ $t("header.knowledgeBase") }}</span> </div> - <el-dropdown v-if="isSuperAdmin" trigger="click" class="equipment-management more-dropdown" :class="{ + <el-dropdown v-if="userInfo.superAdmin" trigger="click" class="equipment-management more-dropdown" :class="{ 'active-tab': $route.path === '/dict-management' || $route.path === '/params-management' || @@ -140,7 +140,7 @@ <!-- 右侧元素 --> <div class="header-right"> - <div class="search-container" v-if="$route.path === '/home' && !(isSuperAdmin && isSmallScreen)"> + <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 @@ -189,7 +189,7 @@ <script> import userApi from "@/apis/module/user"; import i18n, { changeLanguage } from "@/i18n"; -import { mapActions, mapGetters } from "vuex"; +import { mapActions, mapState } from "vuex"; import ChangePasswordDialog from "./ChangePasswordDialog.vue"; // 引入修改密码弹窗组件 import featureManager from "@/utils/featureManager"; // 引入功能管理工具类 @@ -202,10 +202,6 @@ export default { data() { return { search: "", - userInfo: { - username: "", - mobile: "", - }, isChangePasswordDialogVisible: false, // 控制修改密码弹窗的显示 paramDropdownVisible: false, voiceCloneDropdownVisible: false, @@ -224,18 +220,16 @@ export default { label: "label", children: "children", }, - // 功能状态 - featureStatus: { - voiceClone: false, // 音色克隆功能状态 - knowledgeBase: false, // 知识库功能状态 - }, }; }, computed: { - ...mapGetters(["getIsSuperAdmin"]), - isSuperAdmin() { - return this.getIsSuperAdmin; - }, + ...mapState({ + featureStatus: (state) => ({ + voiceClone: state.pubConfig.systemWebMenu?.features?.voiceClone?.enabled, // 音色克隆功能状态 + knowledgeBase: state.pubConfig.systemWebMenu?.features?.knowledgeBase?.enabled, // 知识库功能状态 + }), + userInfo: (state) => state.userInfo, + }), // 获取当前语言 currentLanguage() { return i18n.locale || "zh_CN"; @@ -325,7 +319,6 @@ export default { }, }, async mounted() { - this.fetchUserInfo(); this.checkScreenSize(); window.addEventListener("resize", this.checkScreenSize); // 从localStorage加载搜索历史 @@ -386,20 +379,6 @@ export default { async loadFeatureStatus() { // 等待featureManager初始化完成 await featureManager.waitForInitialization(); - - const config = featureManager.getConfig(); - - this.featureStatus.voiceClone = config.voiceClone; - this.featureStatus.knowledgeBase = config.knowledgeBase; - }, - // 获取用户信息 - 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; diff --git a/main/manager-web/src/components/ParamDialog.vue b/main/manager-web/src/components/ParamDialog.vue index 8c30ef0b..5e3abc17 100644 --- a/main/manager-web/src/components/ParamDialog.vue +++ b/main/manager-web/src/components/ParamDialog.vue @@ -17,17 +17,36 @@ class="custom-input"></el-input> </el-form-item> - <el-form-item :label="$t('paramDialog.paramValue')" prop="paramValue" class="form-item"> - <el-input v-model="form.paramValue" :placeholder="$t('paramDialog.paramValuePlaceholder')" - class="custom-input"></el-input> + <el-form-item :label="$t('paramDialog.valueType')" prop="valueType" class="form-item"> + <el-select + v-model="form.valueType" + :placeholder="$t('paramDialog.valueTypePlaceholder')" + class="custom-select" + > + <el-option + v-for="item in valueTypeOptions" + :key="item.value" + :label="$t(`paramDialog.${item.value}Type`)" + :value="item.value" + /> + </el-select> </el-form-item> - <el-form-item :label="$t('paramDialog.valueType')" prop="valueType" class="form-item"> - <el-select v-model="form.valueType" :placeholder="$t('paramDialog.valueTypePlaceholder')" - class="custom-select"> - <el-option v-for="item in valueTypeOptions" :key="item.value" :label="$t(`paramDialog.${item.value}Type`)" - :value="item.value" /> - </el-select> + <el-form-item :label="$t('paramDialog.paramValue')" prop="paramValue" class="form-item"> + <el-input + v-if="form.valueType !== 'json' && form.valueType !== 'array'" + v-model="form.paramValue" + :placeholder="$t('paramDialog.paramValuePlaceholder')" + class="custom-input" + ></el-input> + <el-input + v-else + type="textarea" + v-model="form.paramValue" + :placeholder="$t('paramDialog.paramValuePlaceholder')" + :rows="6" + class="custom-textarea" + ></el-input> </el-form-item> <el-form-item :label="$t('paramDialog.remark')" prop="remark" class="form-item remark-item"> @@ -98,8 +117,37 @@ export default { submit() { this.$refs.form.validate((valid) => { if (valid) { + const submitData = { ...this.form }; + + // 如果是 array 类型,校验格式并转换 + if (submitData.valueType === 'array' && submitData.paramValue) { + const lines = submitData.paramValue.split('\n').filter(line => line.trim()); + + // 检查除最后一行外的每行是否以分号结尾 + for (let i = 0; i < lines.length - 1; i++) { + if (!lines[i].trim().endsWith(';')) { + this.$message.error('数组格式错误,需要使用英文分号结尾'); + return; + } + } + + const items = lines + .map(item => item.trim().replace(/;$/, '')) + .filter(item => item); + submitData.paramValue = items.join(';'); + } + // 如果是 json 类型,压缩 JSON 格式后再提交 + else if (submitData.valueType === 'json' && submitData.paramValue) { + try { + const parsed = JSON.parse(submitData.paramValue); + submitData.paramValue = JSON.stringify(parsed); + } catch (e) { + // 如果解析失败,保持原值 + } + } + this.saving = true; // 开始加载 - this.$emit('submit', this.form); + this.$emit('submit', submitData); } }); }, @@ -116,7 +164,24 @@ export default { }, watch: { visible(newVal) { - if (!newVal) { + if (newVal) { + if (this.form.paramValue) { + // 如果是 json 类型,格式化显示 + if (this.form.valueType === 'json') { + try { + const parsed = JSON.parse(this.form.paramValue); + this.form.paramValue = JSON.stringify(parsed, null, 2); + } catch (e) { + // 如果解析失败,保持原值 + } + } + // 如果是 array 类型,将分号分隔的字符串转换为每行一个项目 + else if (this.form.valueType === 'array') { + const items = this.form.paramValue.split(';').filter(item => item.trim()); + this.form.paramValue = items.join(';\n'); + } + } + } else { // 当对话框关闭时,重置saving状态 this.saving = false; } diff --git a/main/manager-web/src/components/ProviderDialog.vue b/main/manager-web/src/components/ProviderDialog.vue index 61e5b9de..7abd7ea9 100644 --- a/main/manager-web/src/components/ProviderDialog.vue +++ b/main/manager-web/src/components/ProviderDialog.vue @@ -88,7 +88,6 @@ <el-option :label="$t('providerDialog.booleanType')" value="boolean"></el-option> <el-option :label="$t('providerDialog.dictType')" value="dict"></el-option> <el-option :label="$t('providerDialog.arrayType')" value="array"></el-option> - <el-option :label="$t('providerDialog.ragType')" value="RAG"></el-option> </el-select> </template> <template v-else> diff --git a/main/manager-web/src/store/index.js b/main/manager-web/src/store/index.js index b6b61685..271c5827 100644 --- a/main/manager-web/src/store/index.js +++ b/main/manager-web/src/store/index.js @@ -10,7 +10,6 @@ export default new Vuex.Store({ state: { token: '', userInfo: {}, // 添加用户信息存储 - isSuperAdmin: false, // 添加superAdmin状态 pubConfig: { // 添加公共配置存储 version: '', beianIcpNum: 'null', @@ -29,12 +28,6 @@ export default new Vuex.Store({ getUserInfo(state) { return state.userInfo }, - getIsSuperAdmin(state) { - if (localStorage.getItem('isSuperAdmin') === null) { - return state.isSuperAdmin - } - return localStorage.getItem('isSuperAdmin') === 'true' - }, getPubConfig(state) { return state.pubConfig } @@ -46,19 +39,17 @@ export default new Vuex.Store({ }, setUserInfo(state, userInfo) { state.userInfo = userInfo - const isSuperAdmin = userInfo.superAdmin === 1 - state.isSuperAdmin = isSuperAdmin - localStorage.setItem('isSuperAdmin', isSuperAdmin) + localStorage.setItem('userInfo', JSON.stringify(userInfo)) }, setPubConfig(state, config) { state.pubConfig = config + localStorage.setItem('pubConfig', JSON.stringify(config)) }, clearAuth(state) { state.token = '' state.userInfo = {} - state.isSuperAdmin = false localStorage.removeItem('token') - localStorage.removeItem('isSuperAdmin') + localStorage.removeItem('userInfo') } }, actions: { diff --git a/main/manager-web/src/utils/featureManager.js b/main/manager-web/src/utils/featureManager.js index 2a4a85e0..dff84cad 100644 --- a/main/manager-web/src/utils/featureManager.js +++ b/main/manager-web/src/utils/featureManager.js @@ -1,5 +1,6 @@ //功能配置工具 import Api from "@/apis/api"; +import store from "@/store"; class FeatureManager { constructor() { @@ -72,7 +73,13 @@ class FeatureManager { this.initialized = true; } - + /** + * 更新config缓存 + */ + updateConfigCache(config) { + store.commit('setPubConfig', config); + localStorage.setItem('pubConfig', JSON.stringify(config)); + } /** * 从pub-config接口获取配置 @@ -85,6 +92,7 @@ class FeatureManager { if (result && result.status === 200) { // 检查是否有data字段 if (result.data) { + const configCache = result.data.data || {}; // 检查是否有code字段,如果有则按照code判断 if (result.data.code !== undefined) { if (result.data.code === 0 && result.data.data && result.data.data.systemWebMenu) { @@ -110,6 +118,7 @@ class FeatureManager { console.warn('配置中缺少features对象,使用默认配置'); resolve(this.defaultFeatures); } + configCache.systemWebMenu = config; } catch (error) { console.warn('处理systemWebMenu配置失败:', error); resolve(null); @@ -143,6 +152,7 @@ class FeatureManager { console.warn('配置中缺少features对象,使用默认配置'); resolve(this.defaultFeatures); } + configCache.systemWebMenu = config; } catch (error) { console.warn('处理systemWebMenu配置失败:', error); resolve(null); @@ -152,6 +162,7 @@ class FeatureManager { resolve(null); } } + this.updateConfigCache(configCache) } else { console.warn('接口返回数据中缺少data字段,使用默认配置'); resolve(null); @@ -183,6 +194,8 @@ class FeatureManager { // 异步保存到后端API this.saveConfigToAPI(config).catch(error => { console.warn('保存配置到API失败:', error); + }).finally(() => { + this.init() }); // 触发配置变更事件 diff --git a/main/manager-web/src/views/AgentTemplateManagement.vue b/main/manager-web/src/views/AgentTemplateManagement.vue index d09738d8..ef935f0a 100644 --- a/main/manager-web/src/views/AgentTemplateManagement.vue +++ b/main/manager-web/src/views/AgentTemplateManagement.vue @@ -136,17 +136,22 @@ </div> </div> </div> + <el-footer> + <version-footer /> + </el-footer> </div> </template> <script> import HeaderBar from "@/components/HeaderBar"; import agentApi from "@/apis/module/agent"; +import VersionFooter from "@/components/VersionFooter.vue"; export default { name: "AgentTemplateManagement", components: { HeaderBar, + VersionFooter }, data() { @@ -587,7 +592,7 @@ export default { justify-content: space-between !important; align-items: center; margin-top: auto; - padding: 0 20px 15px !important; + padding: 0 20px !important; width: 100% !important; box-sizing: border-box !important; } diff --git a/main/manager-web/src/views/DictManagement.vue b/main/manager-web/src/views/DictManagement.vue index 3b38f25a..e7acdbb8 100644 --- a/main/manager-web/src/views/DictManagement.vue +++ b/main/manager-web/src/views/DictManagement.vue @@ -48,7 +48,7 @@ <el-table ref="dictDataTable" :data="dictDataList" style="width: 100%" v-loading="dictDataLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading" - element-loading-background="rgba(255, 255, 255, 0.7)" class="data-table" + element-loading-background="rgba(255, 255, 255, 0.7)" class="transparent-table" header-row-class-name="table-header"> <el-table-column :label="$t('modelConfig.select')" align="center" width="70"> <template slot-scope="scope"> @@ -463,9 +463,7 @@ export default { .main-wrapper { margin: 5px 22px; border-radius: 15px; - min-height: calc(100vh - 24vh); - height: auto; - max-height: 80vh; + height: 80vh; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); position: relative; background: rgba(237, 242, 255, 0.5); @@ -555,13 +553,14 @@ export default { .content-area { flex: 1; - padding: 24px; + padding: 24px 24px 0; height: 100%; min-width: 600px; overflow: hidden; background-color: white; display: flex; flex-direction: column; + box-sizing: border-box; } .dict-data-card { @@ -574,31 +573,51 @@ export default { overflow: hidden; } -.data-table { - border-radius: 6px; - overflow-y: hidden; - background-color: transparent !important; - --table-max-height: calc(100vh - 40vh); - max-height: var(--table-max-height); +:deep(.transparent-table) { + background: white; + flex: 1; + width: 100%; + display: flex; + flex-direction: column; - :deep(.el-table__body-wrapper) { - max-height: calc(var(--table-max-height) - 40px); + .el-table__body-wrapper { + flex: 1; overflow-y: auto; + max-height: none !important; } - :deep(.el-table__body) { - tr:last-child td { - border-bottom: none; + .el-table__header-wrapper { + flex-shrink: 0; + } + + .el-table__header th { + background: white !important; + color: black; + font-weight: 600; + height: 40px; + padding: 8px 0; + font-size: 14px; + border-bottom: 1px solid #e4e7ed; + } + + .el-table__body tr { + background-color: white; + + td { + border-top: 1px solid rgba(0, 0, 0, 0.04); + border-bottom: 1px solid rgba(0, 0, 0, 0.04); + padding: 8px 0; + height: 40px; + color: #606266; + font-size: 14px; } } -} -:deep(.el-table) { - &::before { - display: none; + .el-table__row:hover>td { + background-color: #f5f7fa !important; } - &::after { + &::before { display: none; } } @@ -607,7 +626,6 @@ export default { display: flex; justify-content: space-between; align-items: center; - padding: 16px 0; width: 100%; flex-shrink: 0; min-height: 60px; @@ -889,7 +907,7 @@ export default { } :deep(.el-card__body) { - padding: 15px; + padding: 0; display: flex; flex-direction: column; flex: 1; diff --git a/main/manager-web/src/views/FeatureManagement.vue b/main/manager-web/src/views/FeatureManagement.vue index 96c5f3cd..1b7dd1a6 100644 --- a/main/manager-web/src/views/FeatureManagement.vue +++ b/main/manager-web/src/views/FeatureManagement.vue @@ -131,9 +131,7 @@ export default { async created() { // 等待功能配置管理器初始化完成 try { - console.log('等待功能配置管理器初始化...') await featureManager.waitForInitialization() - console.log('功能配置管理器初始化完成,开始加载功能配置') await this.loadFeatures() this.setupConfigChangeListener() } catch (error) { @@ -152,14 +150,8 @@ export default { async getFeaturesByIds(featureIds) { try { const featureConfig = await featureManager.getAllFeatures() - console.log('获取到的功能配置:', JSON.stringify(featureConfig, null, 2)) - console.log('请求的功能ID列表:', featureIds) - const result = featureIds.map(id => { const feature = featureConfig[id] - console.log(`功能 ${id} 的配置:`, feature) - console.log(`功能 ${id} 的启用状态:`, feature?.enabled) - return { id: id, name: this.$t(`feature.${id}.name`), @@ -168,7 +160,6 @@ export default { } }) - console.log('最终返回的功能列表:', JSON.stringify(result, null, 2)) return result } catch (error) { console.error('获取功能配置失败:', error) @@ -245,7 +236,6 @@ export default { setTimeout(() => { this.loadFeatures() - this.$router.go(0) }, 1000) } catch (error) { console.error('保存配置失败:', error) @@ -261,7 +251,6 @@ export default { // 设置配置变化监听器 setupConfigChangeListener() { this.configChangeHandler = () => { - console.log('检测到配置变化,重新加载功能列表') this.loadFeatures() } window.addEventListener('featureConfigReloaded', this.configChangeHandler) diff --git a/main/manager-web/src/views/KnowledgeBaseManagement.vue b/main/manager-web/src/views/KnowledgeBaseManagement.vue index a672e324..0065c9c2 100644 --- a/main/manager-web/src/views/KnowledgeBaseManagement.vue +++ b/main/manager-web/src/views/KnowledgeBaseManagement.vue @@ -555,7 +555,6 @@ export default { justify-content: space-between; align-items: center; margin-top: auto; - padding-bottom: 10px; width: 100%; } diff --git a/main/manager-web/src/views/ModelConfig.vue b/main/manager-web/src/views/ModelConfig.vue index 63c84eb0..28dd48b2 100644 --- a/main/manager-web/src/views/ModelConfig.vue +++ b/main/manager-web/src/views/ModelConfig.vue @@ -69,7 +69,7 @@ element-loading-background="rgba(255, 255, 255, 0.7)" :header-cell-style="{ background: 'transparent' }" :data="modelList" - class="data-table" + class="transparent-table" header-row-class-name="table-header" :header-cell-class-name="headerCellClassName" @selection-change="handleSelectionChange" @@ -637,7 +637,7 @@ export default { }; </script> -<style scoped> +<style lang="scss" scoped> .el-switch { height: 23px; } @@ -662,9 +662,7 @@ export default { .main-wrapper { margin: 5px 22px; border-radius: 15px; - min-height: calc(100vh - 26vh); - height: auto; - max-height: 80vh; + height: 80vh; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); position: relative; background: rgba(237, 242, 255, 0.5); @@ -752,13 +750,14 @@ export default { .content-area { flex: 1; - padding: 24px; + padding: 24px 24px 0; height: 100%; min-width: 600px; overflow: hidden; background-color: white; display: flex; flex-direction: column; + box-sizing: border-box; } .action-group { @@ -843,15 +842,15 @@ export default { outline: none; } -.data-table { - border-radius: 6px; - overflow: hidden; - background-color: transparent !important; -} +// .data-table { +// border-radius: 6px; +// overflow: hidden; +// background-color: transparent !important; +// } -.data-table /deep/ .el-table__row { - background-color: transparent !important; -} +// .data-table ::v-deep .el-table__row { +// background-color: transparent !important; +// } .table-header th { background-color: transparent !important; @@ -863,7 +862,7 @@ export default { display: flex; justify-content: space-between; align-items: center; - padding: 16px 0; + // padding: 16px 0; width: 100%; flex-shrink: 0; min-height: 60px; @@ -918,7 +917,7 @@ export default { background: linear-gradient(135deg, #3a8ee6, #5a7cff); } -.el-table th /deep/ .el-table__cell { +.el-table th ::v-deep .el-table__cell { overflow: hidden; -webkit-user-select: none; -moz-user-select: none; @@ -966,21 +965,6 @@ export default { color: #fff !important; } -::v-deep .data-table { - &.el-table::before, - &.el-table::after, - &.el-table__inner-wrapper::before { - display: none !important; - } -} - -::v-deep .data-table .el-table__header-wrapper { - border-bottom: 1px solid rgb(224, 227, 237); -} - -::v-deep .data-table .el-table__body td { - border-bottom: 1px solid rgb(224, 227, 237) !important; -} .el-button img { height: 1em; @@ -1118,16 +1102,55 @@ export default { flex: 1; overflow: hidden; } +:deep(.transparent-table) { + background: white; + flex: 1; + width: 100%; + display: flex; + flex-direction: column; -.data-table { - --table-max-height: calc(100vh - 45vh); - max-height: var(--table-max-height); + .el-table__body-wrapper { + flex: 1; + overflow-y: auto; + max-height: none !important; + } + + .el-table__header-wrapper { + flex-shrink: 0; + } + + .el-table__header th { + background: white !important; + color: black; + font-weight: 600; + height: 40px; + padding: 8px 0; + font-size: 14px; + border-bottom: 1px solid #e4e7ed; + } + + .el-table__body tr { + background-color: white; + + td { + border-top: 1px solid rgba(0, 0, 0, 0.04); + border-bottom: 1px solid rgba(0, 0, 0, 0.04); + padding: 8px 0; + height: 40px; + color: #606266; + font-size: 14px; + } + } + + .el-table__row:hover>td { + background-color: #f5f7fa !important; + } + + &::before { + display: none; + } } -.data-table ::v-deep .el-table__body-wrapper { - max-height: calc(var(--table-max-height) - 80px); - overflow-y: auto; -} ::v-deep .el-loading-mask { background-color: rgba(255, 255, 255, 0.6) !important; diff --git a/main/manager-web/src/views/OtaManagement.vue b/main/manager-web/src/views/OtaManagement.vue index dbf41cec..24bad3ce 100644 --- a/main/manager-web/src/views/OtaManagement.vue +++ b/main/manager-web/src/views/OtaManagement.vue @@ -505,7 +505,6 @@ export default { justify-content: space-between; align-items: center; margin-top: 10px; - padding-bottom: 10px; } .ctrl_btn { @@ -735,7 +734,7 @@ export default { } .el-table { - --table-max-height: calc(100vh - 40vh); + // --table-max-height: calc(100vh - 40vh); max-height: var(--table-max-height); .el-table__body-wrapper { diff --git a/main/manager-web/src/views/ParamsManagement.vue b/main/manager-web/src/views/ParamsManagement.vue index 31645165..3b181f41 100644 --- a/main/manager-web/src/views/ParamsManagement.vue +++ b/main/manager-web/src/views/ParamsManagement.vue @@ -464,7 +464,7 @@ export default { justify-content: space-between; align-items: center; margin-top: 10px; - padding-bottom: 10px; + // padding-bottom: 10px; } .ctrl_btn { @@ -712,7 +712,7 @@ export default { } .el-table { - --table-max-height: calc(100vh - 40vh); + // --table-max-height: calc(100vh - 40vh); max-height: var(--table-max-height); .el-table__body-wrapper { diff --git a/main/manager-web/src/views/ProviderManagement.vue b/main/manager-web/src/views/ProviderManagement.vue index 3c55c4b4..830d2867 100644 --- a/main/manager-web/src/views/ProviderManagement.vue +++ b/main/manager-web/src/views/ProviderManagement.vue @@ -546,7 +546,6 @@ export default { justify-content: space-between; align-items: center; margin-top: 10px; - padding-bottom: 10px; } .ctrl_btn { @@ -794,7 +793,7 @@ export default { } .el-table { - --table-max-height: calc(100vh - 40vh); + // --table-max-height: calc(100vh - 40vh); max-height: var(--table-max-height); .el-table__body-wrapper { diff --git a/main/manager-web/src/views/UserManagement.vue b/main/manager-web/src/views/UserManagement.vue index 8132ba01..fd7b3fbc 100644 --- a/main/manager-web/src/views/UserManagement.vue +++ b/main/manager-web/src/views/UserManagement.vue @@ -721,7 +721,7 @@ export default { } .el-table { - --table-max-height: calc(100vh - 40vh); + // --table-max-height: calc(100vh - 40vh); max-height: var(--table-max-height); .el-table__body-wrapper { diff --git a/main/manager-web/src/views/VoiceCloneManagement.vue b/main/manager-web/src/views/VoiceCloneManagement.vue index ea0b7921..e32f90a4 100644 --- a/main/manager-web/src/views/VoiceCloneManagement.vue +++ b/main/manager-web/src/views/VoiceCloneManagement.vue @@ -594,7 +594,6 @@ export default { justify-content: space-between; align-items: center; margin-top: 10px; - padding-bottom: 10px; } .ctrl_btn { @@ -852,7 +851,7 @@ export default { .el-table { --table-max-height: calc(100vh - 40vh); - max-height: var(--table-max-height); + // max-height: var(--table-max-height); .el-table__body-wrapper { max-height: calc(var(--table-max-height) - 40px); @@ -864,7 +863,6 @@ export default { display: flex; justify-content: space-between; align-items: center; - margin-top: 40px; } :deep(.transparent-table) { diff --git a/main/manager-web/src/views/VoiceResourceManagement.vue b/main/manager-web/src/views/VoiceResourceManagement.vue index 089b3683..caf2e40f 100644 --- a/main/manager-web/src/views/VoiceResourceManagement.vue +++ b/main/manager-web/src/views/VoiceResourceManagement.vue @@ -433,7 +433,7 @@ export default { overflow: hidden; ::v-deep .el-card__body { - padding: 15px; + padding: 15px 15px 0; display: flex; flex-direction: column; flex: 1; @@ -676,7 +676,7 @@ export default { } .el-table { - --table-max-height: calc(100vh - 40vh); + // --table-max-height: calc(100vh - 40vh); max-height: var(--table-max-height); .el-table__body-wrapper { diff --git a/main/manager-web/src/views/auth.scss b/main/manager-web/src/views/auth.scss index 2ed9db58..95f71e0a 100644 --- a/main/manager-web/src/views/auth.scss +++ b/main/manager-web/src/views/auth.scss @@ -76,7 +76,6 @@ margin-top: 20px; align-items: center; border-radius: 10px; - background: #f6f8fb; border: 1px solid #e4e6ef; height: 40px; padding: 0 15px; diff --git a/main/manager-web/src/views/login.vue b/main/manager-web/src/views/login.vue index 828e6a83..a34f2343 100644 --- a/main/manager-web/src/views/login.vue +++ b/main/manager-web/src/views/login.vue @@ -238,13 +238,6 @@ export default { this.$store.dispatch("fetchPubConfig").then(() => { // 根据配置决定默认登录方式 this.isMobileLogin = this.enableMobileRegister; - - // pub-config接口调用完成后,重新初始化featureManager以确保使用最新的配置 - featureManager.waitForInitialization().then(() => { - console.log('featureManager重新初始化完成,使用pub-config配置'); - }).catch(error => { - console.warn('featureManager重新初始化失败:', error); - }); }); }, methods: { @@ -256,7 +249,9 @@ export default { window.open(url, '_blank'); }, fetchCaptcha() { - if (this.$store.getters.getToken) { + // 处理手动清空localstorage导致无法获取验证码的问题 + const token = localStorage.getItem('token') + if (token) { if (this.$route.path !== "/home") { this.$router.push("/home"); } @@ -308,6 +303,17 @@ export default { } return true; }, + + getUserInfo() { + Api.user.getUserInfo(({ data }) => { + if (data.code === 0) { + this.$store.commit("setUserInfo", data.data); + goToPage("/home"); + } else { + showDanger("用户信息获取失败"); + } + }); + }, async login() { if (this.isMobileLogin) { @@ -361,7 +367,7 @@ export default { ({ data }) => { showSuccess(this.$t('login.loginSuccess')); this.$store.commit("setToken", JSON.stringify(data.data)); - goToPage("/home"); + this.getUserInfo(); }, (err) => { // 直接使用后端返回的国际化消息