Merge pull request #647 from xinnan-tech/web_HeaderBar_Classify

调整设备管理接口与页面,运用本地缓存增加HeaderBar管理员和用户分类
This commit is contained in:
CGD
2025-04-02 17:55:41 +08:00
committed by GitHub
5 changed files with 76 additions and 27 deletions
+3 -2
View File
@@ -7,7 +7,7 @@ import admin from './module/admin.js'
* 如果你想调用8002端口,就用'/xiaozhi-esp32-api/api/v1',请与vue.config.js的devServer配置相结合,方便跨域请求 * 如果你想调用8002端口,就用'/xiaozhi-esp32-api/api/v1',请与vue.config.js的devServer配置相结合,方便跨域请求
* *
*/ */
// const DEV_API_SERVICE = 'https://2662r3426b.vicp.fun/xiaozhi-esp32-api' const DEV_API_SERVICE = 'https://2662r3426b.vicp.fun/xiaozhi-esp32-api'
// 8002开发完成完成后使用这个 // 8002开发完成完成后使用这个
// const DEV_API_SERVICE = '/xiaozhi-esp32-api' // const DEV_API_SERVICE = '/xiaozhi-esp32-api'
@@ -16,7 +16,8 @@ import admin from './module/admin.js'
* @returns {string} * @returns {string}
*/ */
export function getServiceUrl() { export function getServiceUrl() {
return '/xiaozhi-esp32-api' // return '/xiaozhi-esp32-api'
return DEV_API_SERVICE
} }
@@ -56,10 +56,16 @@ export default {
this.loading = false; this.loading = false;
if (data.code === 0) { if (data.code === 0) {
this.$emit('refresh'); this.$emit('refresh');
this.$message.success('设备绑定成功'); this.$message.success({
message: '设备绑定成功',
showClose: true
});
this.closeDialog(); this.closeDialog();
} else { } else {
this.$message.error(data.msg || '绑定失败'); this.$message.error({
message: data.msg || '绑定失败',
showClose: true
});
} }
} }
); );
+15 -3
View File
@@ -13,11 +13,11 @@
<img loading="lazy" alt="" src="@/assets/header/robot.png" :style="{ filter: $route.path === '/home' ? 'brightness(0) invert(1)' : 'None' }"/> <img loading="lazy" alt="" src="@/assets/header/robot.png" :style="{ filter: $route.path === '/home' ? 'brightness(0) invert(1)' : 'None' }"/>
智能体管理 智能体管理
</div> </div>
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/user-management' }" @click="goUserManagement"> <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' }"/> <img loading="lazy" alt="" src="@/assets/header/user_management.png" :style="{ filter: $route.path === '/user-management' ? 'brightness(0) invert(1)' : 'None' }"/>
用户管理 用户管理
</div> </div>
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/model-config' }" @click="goModelConfig"> <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' }"/> <img loading="lazy" alt="" src="@/assets/header/model_config.png" :style="{ filter: $route.path === '/model-config' ? 'brightness(0) invert(1)' : 'None' }"/>
模型配置 模型配置
</div> </div>
@@ -41,6 +41,7 @@
{{ userInfo.username || '加载中...' }}<i class="el-icon-arrow-down el-icon--right"></i> {{ userInfo.username || '加载中...' }}<i class="el-icon-arrow-down el-icon--right"></i>
</span> </span>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-plus" @click.native="">个人中心</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus" @click.native="showChangePasswordDialog">修改密码</el-dropdown-item> <el-dropdown-item icon="el-icon-circle-plus" @click.native="showChangePasswordDialog">修改密码</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus-outline" @click.native="handleLogout">退出登录</el-dropdown-item> <el-dropdown-item icon="el-icon-circle-plus-outline" @click.native="handleLogout">退出登录</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@@ -56,7 +57,7 @@
<script> <script>
import userApi from '@/apis/module/user'; import userApi from '@/apis/module/user';
import ChangePasswordDialog from './ChangePasswordDialog.vue'; // 引入修改密码弹窗组件 import ChangePasswordDialog from './ChangePasswordDialog.vue'; // 引入修改密码弹窗组件
import { mapActions } from 'vuex'; // 导入 mapActions import { mapActions, mapGetters } from 'vuex';
export default { export default {
@@ -75,6 +76,12 @@ export default {
isChangePasswordDialogVisible: false // 控制修改密码弹窗的显示 isChangePasswordDialogVisible: false // 控制修改密码弹窗的显示
} }
}, },
computed: {
...mapGetters(['getIsSuperAdmin']),
isSuperAdmin() {
return this.getIsSuperAdmin;
}
},
mounted() { mounted() {
this.fetchUserInfo() this.fetchUserInfo()
}, },
@@ -93,6 +100,9 @@ export default {
fetchUserInfo() { fetchUserInfo() {
userApi.getUserInfo(({data}) => { userApi.getUserInfo(({data}) => {
this.userInfo = data.data this.userInfo = data.data
if (data.data.superAdmin !== undefined) {
this.$store.commit('setUserInfo', data.data);
}
}) })
}, },
@@ -125,7 +135,9 @@ export default {
try { try {
// 调用 Vuex 的 logout action // 调用 Vuex 的 logout action
await this.logout(); await this.logout();
this.$message.success('退出登录成功'); this.$message.success('退出登录成功');
this.$router.push('/login'); this.$router.push('/login');
} catch (error) { } catch (error) {
console.error('退出登录失败:', error); console.error('退出登录失败:', error);
+13 -1
View File
@@ -7,7 +7,8 @@ Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
token: '', token: '',
userInfo: {} // 添加用户信息存储 userInfo: {}, // 添加用户信息存储
isSuperAdmin: false // 添加superAdmin状态
}, },
getters: { getters: {
getToken(state) { getToken(state) {
@@ -18,6 +19,12 @@ export default new Vuex.Store({
}, },
getUserInfo(state) { getUserInfo(state) {
return state.userInfo return state.userInfo
},
getIsSuperAdmin(state) {
if (localStorage.getItem('isSuperAdmin') === null) {
return state.isSuperAdmin
}
return localStorage.getItem('isSuperAdmin') === 'true'
} }
}, },
mutations: { mutations: {
@@ -27,11 +34,16 @@ export default new Vuex.Store({
}, },
setUserInfo(state, userInfo) { setUserInfo(state, userInfo) {
state.userInfo = userInfo state.userInfo = userInfo
const isSuperAdmin = userInfo.superAdmin === 1
state.isSuperAdmin = isSuperAdmin
localStorage.setItem('isSuperAdmin', isSuperAdmin)
}, },
clearAuth(state) { clearAuth(state) {
state.token = '' state.token = ''
state.userInfo = {} state.userInfo = {}
state.isSuperAdmin = false
localStorage.removeItem('token') localStorage.removeItem('token')
localStorage.removeItem('isSuperAdmin')
} }
}, },
actions: { actions: {
+37 -19
View File
@@ -59,6 +59,7 @@
<script> <script>
import HeaderBar from "@/components/HeaderBar.vue"; import HeaderBar from "@/components/HeaderBar.vue";
import AddDeviceDialog from "@/components/AddDeviceDialog.vue"; import AddDeviceDialog from "@/components/AddDeviceDialog.vue";
export default { export default {
components: {HeaderBar, AddDeviceDialog }, components: {HeaderBar, AddDeviceDialog },
data() { data() {
@@ -81,8 +82,8 @@ export default {
}, },
mounted() { mounted() {
const agentId = this.$route.query.agentId; const agentId = this.$route.query.agentId;
import('@/apis/module/user').then(({ default: userApi }) => { import('@/apis/module/device').then(({ default: deviceApi }) => {
this.userApi = userApi; this.deviceApi = deviceApi;
if (agentId) { if (agentId) {
this.fetchBindDevices(agentId); this.fetchBindDevices(agentId);
} }
@@ -99,7 +100,7 @@ export default {
this.deviceList[index].isEdit = false; this.deviceList[index].isEdit = false;
}, },
handleUnbind(device_id) { handleUnbind(device_id) {
if (!this.userApi) { if (!this.deviceApi) {
this.$message.error('功能模块加载失败'); this.$message.error('功能模块加载失败');
return; return;
} }
@@ -108,12 +109,18 @@ export default {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.userApi.unbindDevice(device_id, ({ data }) => { this.deviceApi.unbindDevice(device_id, ({ data }) => {
if (data.code === 0) { if (data.code === 0) {
this.$message.success('设备解绑成功'); this.$message.success({
message: '设备解绑成功',
showClose: true
});
this.fetchBindDevices(this.$route.query.agentId); this.fetchBindDevices(this.$route.query.agentId);
} else { } else {
this.$message.error(data.msg || '设备解绑失败'); this.$message.error({
message: data.msg || '设备解绑失败',
showClose: true
});
} }
}); });
}); });
@@ -128,19 +135,29 @@ export default {
this.loading = true; this.loading = true;
import('@/apis/module/device').then(({ default: deviceApi }) => { import('@/apis/module/device').then(({ default: deviceApi }) => {
deviceApi.getAgentBindDevices(agentId, ({ data }) => { deviceApi.getAgentBindDevices(agentId, ({ data }) => {
this.loading = false; this.loading = false;
if (data.code === 0) { if (data.code === 0) {
this.deviceList = data.data.map(device => ({ // 格式化日期并按照绑定时间降序排列
device_id: device.id, this.deviceList = data.data.map(device => {
model: device.board, // 格式化绑定时间
firmwareVersion: device.appVersion, const bindDate = new Date(device.createDate);
macAddress: device.macAddress, const formattedBindTime = `${bindDate.getFullYear()}-${(bindDate.getMonth()+1).toString().padStart(2, '0')}-${bindDate.getDate().toString().padStart(2, '0')} ${bindDate.getHours().toString().padStart(2, '0')}:${bindDate.getMinutes().toString().padStart(2, '0')}:${bindDate.getSeconds().toString().padStart(2, '0')}`;
bindTime: device.createDate, return {
lastConversation: device.lastConnectedAt, device_id: device.id,
remark: device.alias, model: device.board,
isEdit: false, firmwareVersion: device.appVersion,
otaSwitch: device.autoUpdate === 1 macAddress: device.macAddress,
})); bindTime: formattedBindTime, // 使用格式化后的时间
lastConversation: device.lastConnectedAt,
remark: device.alias,
isEdit: false,
otaSwitch: device.autoUpdate === 1,
// 添加原始时间用于排序
rawBindTime: new Date(device.createDate).getTime()
};
})
// 按照绑定时间降序排序
.sort((a, b) => a.rawBindTime - b.rawBindTime);
} else { } else {
this.$message.error(data.msg || '获取设备列表失败'); this.$message.error(data.msg || '获取设备列表失败');
} }
@@ -152,6 +169,7 @@ export default {
}, },
} }
}; };
</script> </script>
<style scoped> <style scoped>