mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 10:03:54 +08:00
Merge pull request #647 from xinnan-tech/web_HeaderBar_Classify
调整设备管理接口与页面,运用本地缓存增加HeaderBar管理员和用户分类
This commit is contained in:
@@ -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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user