Merge pull request #645 from xinnan-tech/Web_Device_Api

Web device api
This commit is contained in:
CGD
2025-04-02 15:41:08 +08:00
committed by GitHub
10 changed files with 212 additions and 186 deletions
@@ -0,0 +1,54 @@
import RequestService from '../httpRequest'
import {getServiceUrl} from '../api'
export default {
// 已绑设备
getAgentBindDevices(agentId, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/device/bind/${agentId}`)
.method('GET')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('获取设备列表失败:', err);
RequestService.reAjaxFun(() => {
this.getAgentBindDevices(agentId, callback);
});
}).send();
},
// 解绑设备
unbindDevice(device_id, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/device/unbind`)
.method('POST')
.data({ deviceId: device_id })
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('解绑设备失败:', err);
RequestService.reAjaxFun(() => {
this.unbindDevice(device_id, callback);
});
}).send();
},
// 绑定设备
bindDevice(agentId, deviceCode, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/device/bind/${agentId}/${deviceCode}`)
.method('POST')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('绑定设备失败:', err);
RequestService.reAjaxFun(() => {
this.bindDevice(agentId, deviceCode, callback);
});
}).send();
},
}
-52
View File
@@ -21,7 +21,6 @@ export default {
},
// 获取验证码
getCaptcha(uuid, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/user/captcha?uuid=${uuid}`)
.method('GET')
@@ -52,7 +51,6 @@ export default {
.fail(() => {
}).send()
},
// 保存设备配置
saveDeviceConfig(device_id, configData, callback) {
RequestService.sendRequest()
@@ -106,54 +104,4 @@ export default {
})
.send();
},
// 已绑设备
getAgentBindDevices(agentId, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/user/agent/device/bind/${agentId}`)
.method('GET')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('获取设备列表失败:', err);
RequestService.reAjaxFun(() => {
this.getAgentBindDevices(agentId, callback);
});
}).send();
},
// 解绑设备
unbindDevice(device_id, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/user/device/unbind/${device_id}`)
.method('PUT')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('解绑设备失败:', err);
RequestService.reAjaxFun(() => {
this.unbindDevice(device_id, callback);
});
}).send();
},
// 绑定设备
bindDevice(agentId, code, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/api/v1/user/agent/device/bind/${agentId}`)
.method('POST')
.data({ code })
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.fail((err) => {
console.error('绑定设备失败:', err);
RequestService.reAjaxFun(() => {
this.bindDevice(agentId, code, callback);
});
}).send();
},
}
@@ -49,8 +49,8 @@ export default {
return;
}
this.loading = true;
import('@/apis/module/user').then(({ default: userApi }) => {
userApi.bindDevice(
import('@/apis/module/device').then(({ default: deviceApi }) => {
deviceApi.bindDevice(
this.agentId,
this.deviceCode, ({data}) => {
this.loading = false;
@@ -1,9 +1,11 @@
<template>
<form>
<el-dialog :visible.sync="value" width="400px" center>
<div
style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
<img loading="lazy" src="@/assets/login/shield.png" alt="" style="width: 19px;height: 23px; filter: brightness(0) invert(1);" />
<div style="margin: 0 10px 10px;display: flex;align-items: center;gap: 10px;font-weight: 700;font-size: 20px;text-align: left;color: #3d4566;">
<div style="width: 40px;height: 40px;border-radius: 50%;background: #5778ff;display: flex;align-items: center;justify-content: center;">
<img loading="lazy" src="@/assets/login/shield.png" alt="" style="width: 19px;height: 23px; filter: brightness(0) invert(1);" />
</div>
修改密码
</div>
<div style="height: 1px;background: #e8f0ff;"/>
<div style="margin: 22px 15px;">
@@ -109,7 +111,6 @@ export default {
<style scoped>
.input-46 {
border: 1px solid #e4e6ef;
background: #f6f8fb;
border-radius: 15px;
}
@@ -10,9 +10,7 @@
<!-- 中间导航菜单 -->
<div class="header-center">
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/home' }" @click="goHome">
<img loading="lazy" alt="" src="@/assets/header/robot.png" :style="{ filter: $route.path === '/home' ? 'brightness(0) invert(1)' : 'None' }"/>
智能体管理
</div>
<div class="equipment-management" :class="{ 'active-tab': $route.path === '/user-management' }" @click="goUserManagement">
@@ -43,7 +41,6 @@
{{ userInfo.username || '加载中...' }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<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-outline" @click.native="handleLogout">退出登录</el-dropdown-item>
</el-dropdown-menu>
@@ -128,9 +125,7 @@ export default {
try {
// 调用 Vuex 的 logout action
await this.logout();
this.$message.success('退出登录成功');
this.$router.push('/login');
} catch (error) {
console.error('退出登录失败:', error);
+8 -1
View File
@@ -1,5 +1,5 @@
<template>
<el-dialog :visible.sync="visible" width="80%" @close="handleClose">
<el-dialog :visible.sync="localVisible" width="80%" @close="handleClose">
<el-row class="main-container">
<el-col :span="4">
<el-menu class="model-menu" :default-active="activeModel" mode="vertical" @select="handleModelSelect">
@@ -57,6 +57,7 @@ export default {
},
data() {
return {
localVisible: this.visible,
activeModel: 'EdgeTTS',
searchQuery: '',
editDialogVisible: false,
@@ -72,6 +73,11 @@ export default {
total: 20
};
},
watch: {
visible(newVal) {
this.localVisible = newVal;
}
},
computed: {
filteredTtsModels() {
return this.ttsModels.filter(model =>
@@ -81,6 +87,7 @@ export default {
},
methods: {
handleClose() {
this.localVisible = false;
this.$emit('update:visible', false);
},
handleModelSelect(index) {
+1
View File
@@ -8,6 +8,7 @@ import 'element-ui/lib/theme-chalk/index.css';
import './styles/global.scss'
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
+6 -3
View File
@@ -54,7 +54,8 @@ export function showDanger(msg) {
}
Message({
message: msg,
type: 'error'
type: 'error',
showClose: true
})
}
@@ -68,7 +69,8 @@ export function showWarning(msg) {
}
Message({
message: msg,
type: 'warning'
type: 'warning',
showClose: true
});
}
@@ -81,7 +83,8 @@ export function showWarning(msg) {
export function showSuccess(msg) {
Message({
message: msg,
type: 'success'
type: 'success',
showClose: true
})
}
+23 -22
View File
@@ -124,31 +124,32 @@ export default {
handleCurrentChange(val) {
this.currentPage = val;
},
fetchBindDevices(agentId) {
this.loading = true;
import('@/apis/module/user').then(({ default: userApi }) => {
userApi.getAgentBindDevices(agentId, ({ data }) => {
fetchBindDevices(agentId) {
this.loading = true;
import('@/apis/module/device').then(({ default: deviceApi }) => {
deviceApi.getAgentBindDevices(agentId, ({ data }) => {
this.loading = false;
if (data.code === 0) {
this.deviceList = data.data[0].list.map(device => ({
device_id: device.id,
model: device.device_type,
firmwareVersion: device.app_version,
macAddress: device.mac_address,
lastConversation: device.recent_chat_time,
remark: '',
isEdit: false,
otaSwitch: device.ota_upgrade === 1
}));
} else {
this.$message.error(data.msg || '获取设备列表失败');
}
});
}).catch(error => {
console.error('模块加载失败:', error);
this.$message.error('功能模块加载失败');
this.deviceList = data.data.map(device => ({
device_id: device.id,
model: device.board,
firmwareVersion: device.appVersion,
macAddress: device.macAddress,
bindTime: device.createDate,
lastConversation: device.lastConnectedAt,
remark: device.alias,
isEdit: false,
otaSwitch: device.autoUpdate === 1
}));
} else {
this.$message.error(data.msg || '获取设备列表失败');
}
});
},
}).catch(error => {
console.error('模块加载失败:', error);
this.$message.error('功能模块加载失败');
});
},
}
};
</script>
+113 -97
View File
@@ -106,7 +106,7 @@
<ModelEditDialog :visible.sync="editDialogVisible" :modelData="editModelData" @save="handleModelSave"/>
<TtsModel :visible.sync="ttsDialogVisible" />
<AddModelDialog :visible.sync="addDialogVisible" @confirm="handleAddConfirm"/>
<AddModelDialog :modelType="activeTab" :visible.sync="addDialogVisible" @confirm="handleAddConfirm"/>
</div>
<div class="copyright">
@@ -234,6 +234,10 @@ export default {
</script>
<style scoped>
::v-deep .el-table tr{
background: transparent;
}
.welcome {
min-width: 900px;
min-height: 506px;
@@ -241,8 +245,8 @@ export default {
display: flex;
position: relative;
flex-direction: column;
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
background-size: cover;
background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
-webkit-background-size: cover;
-o-background-size: cover;
}
@@ -349,6 +353,7 @@ export default {
height: 100%;
min-width: 600px;
overflow-x: auto;
}
.title-bar {
@@ -365,12 +370,6 @@ export default {
margin: 0;
}
.title-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.action-group {
display: flex;
align-items: center;
@@ -386,33 +385,31 @@ export default {
width: 240px;
}
.search-input .el-input__inner::placeholder {
::v-deep .search-input .el-input__inner::placeholder {
color: black;
opacity: 0.6;
}
.search-input .el-input__inner {
::v-deep .search-input .el-input__inner {
background: transparent;
}
.search-btn {
background: linear-gradient(135deg, #6B8CFF, #A966FF);
border: none;
color: white;
}
.data-table {
border-radius: 6px;
overflow: hidden;
background-color: transparent !important;
}
.data-table .el-table__row {
.data-table /deep/ .el-table__row {
background-color: transparent !important;
}
.data-table .el-table__header-wrapper {
border-bottom: 1px solid rgb(224,227,237);
}
.data-table .el-table__body td {
border-bottom: 1px solid rgb(224,227,237) !important;
}
.table-header th {
background-color: transparent !important;
color: #606266;
@@ -433,11 +430,6 @@ export default {
gap: 8px;
}
.pagination-container {
display: flex;
justify-content: flex-end;
}
.copyright {
text-align: center;
color: #979db1;
@@ -452,16 +444,6 @@ export default {
width: 100%;
}
/* 按钮样式 */
.el-button {
img {
height: 1em;
vertical-align: middle;
padding-right: 2px;
padding-bottom: 2px;
}
}
.add-btn {
background: #cce5f9;
width: 75px;
@@ -470,12 +452,94 @@ export default {
padding: 8px 16px;
}
.search-btn {
background: linear-gradient(135deg, #6B8CFF, #A966FF);
.title-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.batch-actions .el-button:first-child {
background: linear-gradient(135deg, #409EFF, #6B8CFF);
border: none;
color: white;
}
.batch-actions .el-button:first-child:hover {
background: linear-gradient(135deg, #3A8EE6, #5A7CFF);
}
.el-table th /deep/ .el-table__cell {
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
background-color: transparent !important;
}
.pagination-container {
display: flex;
justify-content: flex-end;
}
::v-deep .el-table .custom-selection-header .cell .el-checkbox__inner {
display: none !important; /* 使表头复选框不可见 */
}
::v-deep .el-table .custom-selection-header .cell::before {
content: '选择';
display: block;
text-align: center;
line-height: 0;
color: black;
margin-top: 23px;
}
::v-deep .el-table__body .el-checkbox__inner {
display: inline-block !important;
background: #e6edfa;
}
::v-deep .el-table thead th:not(:first-child) .cell {
color: #303133 !important;
}
::v-deep .nav-panel .el-menu-item.is-active .menu-text {
color: #409EFF !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;
vertical-align: middle;
padding-right: 2px;
padding-bottom: 2px;
}
::v-deep .el-checkbox__inner {
border-color: #cfcfcf !important;
transition: all 0.2s ease-in-out;
}
::v-deep .el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #409EFF !important;
border-color: #409EFF !important;
}
.voice-management-btn {
background: #9db3ea;
color: white;
@@ -487,75 +551,27 @@ export default {
}
.voice-management-btn:hover {
background: #8aa2e0;
background: #8aa2e0; /* 悬停时颜色加深 */
transform: scale(1.05);
}
::v-deep .el-table .el-table-column--selection .cell {
padding-left: 15px !important;
}
::v-deep .el-table .el-table__fixed-right .cell {
padding-right: 15px !important;
}
.edit-btn, .delete-btn {
margin: 0 8px;
color: #7079aa !important;
}
/* 深度选择器合并 */
::v-deep {
.el-table tr {
background: transparent;
}
.el-table .custom-selection-header .cell .el-checkbox__inner {
display: none !important;
}
.el-table .custom-selection-header .cell::before {
content: '选择';
display: block;
text-align: center;
line-height: 0;
color: black;
margin-top: 23px;
}
.el-table__body .el-checkbox__inner {
display: inline-block !important;
background: #e6edfa;
}
.el-table thead th:not(:first-child) .cell {
color: #303133 !important;
}
.nav-panel .el-menu-item.is-active .menu-text {
color: #409EFF !important;
}
.el-checkbox__inner {
border-color: #cfcfcf !important;
transition: all 0.2s ease-in-out;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #409EFF !important;
border-color: #409EFF !important;
}
.el-table .el-table-column--selection .cell {
padding-left: 15px !important;
}
.el-table .el-table__fixed-right .cell {
padding-right: 15px !important;
}
.el-table .cell {
padding-left: 10px;
padding-right: 10px;
}
.data-table.el-table::before,
.data-table.el-table::after,
.data-table.el-table__inner-wrapper::before {
display: none !important;
}
::v-deep .el-table .cell {
padding-left: 10px;
padding-right: 10px;
}
</style>