Files
abot/plugins/stats_dashboard/templates/robot_management.html

656 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}群机器人管理 - 机器人管理后台{% endblock %}
{% block content %}
<!-- 群机器人管理 -->
<div>
<el-row :gutter="20">
<el-col :span="24">
<el-card shadow="hover">
<div slot="header" class="clearfix">
<span>群机器人管理</span>
<el-button
type="primary"
size="small"
style="float: right; margin-left: 10px;"
@click="showAddGroupDialog">
添加群组
</el-button>
<el-input
placeholder="搜索群组..."
v-model="searchQuery"
style="width: 200px; float: right"
clearable>
</el-input>
</div>
<!-- 群组列表 -->
<el-table
:data="filteredGroups"
style="width: 100%"
border
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="群组信息">
<template slot-scope="scope">
{% raw %}{{ scope.row.group_name || scope.row.group_id }} ({{ scope.row.group_id }}){% endraw %}
</template>
</el-table-column>
<el-table-column label="机器人状态" width="120">
<template slot-scope="scope">
<el-tag
:type="scope.row.robot_status === 'enabled' ? 'success' : 'danger'">
{% raw %}{{ scope.row.robot_status === 'enabled' ? '已启用' : '已关闭' }}{% endraw %}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="280">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="viewGroupPermissions(scope.row)">
查看权限
</el-button>
<el-button
size="mini"
:type="scope.row.robot_status === 'enabled' ? 'danger' : 'success'"
@click="toggleRobotStatus(scope.row)">
{% raw %}{{ scope.row.robot_status === 'enabled' ? '关闭' : '启用' }}{% endraw %}
</el-button>
<el-button
size="mini"
type="info"
@click="viewMessageTrend(scope.row)">
消息趋势
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 批量操作按钮 -->
<div style="margin-top: 20px" v-if="selectedGroups.length > 0">
<el-alert
title="批量操作"
type="info"
:closable="false">
<span>已选择 {% raw %}{{ selectedGroups.length }}{% endraw %} 个群组</span>
</el-alert>
<div style="margin-top: 10px">
<el-button type="success" size="small" @click="batchEnableRobot">批量启用</el-button>
<el-button type="danger" size="small" @click="batchDisableRobot">批量关闭</el-button>
<el-button type="warning" size="small" @click="batchRemoveGroups">批量移除</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 群组权限管理对话框 -->
<el-dialog
:title="currentGroupName + ' 功能权限管理'"
:visible.sync="permissionDialogVisible"
width="70%">
<el-table :data="permissions" style="width: 100%" border>
<el-table-column prop="feature_id" label="功能ID" width="80"></el-table-column>
<el-table-column prop="feature_description" label="功能描述"></el-table-column>
<el-table-column label="状态" width="100">
<template slot-scope="scope">
<el-tag
:type="scope.row.status === 'enabled' ? 'success' : 'danger'">
{% raw %}{{ scope.row.status === 'enabled' ? '已启用' : '已关闭' }}{% endraw %}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template slot-scope="scope">
<el-switch
v-model="scope.row.statusBool"
active-color="#13ce66"
inactive-color="#ff4949"
@change="togglePermission(scope.row)">
</el-switch>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="enableAllPermissions" type="success">一键启用全部</el-button>
<el-button @click="disableAllPermissions" type="danger">一键关闭全部</el-button>
<el-button @click="permissionDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 添加群组对话框 -->
<el-dialog
title="添加群组"
:visible.sync="addGroupDialogVisible"
width="30%">
<el-form :model="addGroupForm" :rules="addGroupRules" ref="addGroupForm">
<el-form-item label="群组ID" prop="groupId">
<el-input v-model="addGroupForm.groupId" placeholder="请输入群组ID"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addGroupDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddGroup">确定</el-button>
</span>
</el-dialog>
<!-- 群组消息趋势图对话框 -->
<el-dialog
:title="currentGroupName + ' 消息趋势'"
:visible.sync="trendDialogVisible"
width="70%"
@opened="onTrendDialogOpened"
destroy-on-close>
<div v-if="trendLoading" style="text-align: center; padding: 20px;">
<i class="el-icon-loading"></i>
<p>加载中...</p>
</div>
<div v-else>
<div style="width: 100%; height: 400px;">
<canvas ref="trendChart" style="width: 100%; height: 100%;"></canvas>
</div>
<div style="margin-top: 20px; text-align: center;">
<el-radio-group v-model="trendDays" @change="loadMessageTrend">
<el-radio-button :label="7">最近7天</el-radio-button>
<el-radio-button :label="14">最近14天</el-radio-button>
<el-radio-button :label="30">最近30天</el-radio-button>
</el-radio-group>
</div>
</div>
</el-dialog>
</div>
{% endblock %}
{% block scripts %}
<script>
new Vue({
el: '#app',
mixins: [baseApp],
data() {
return {
groups: [],
permissions: [],
currentGroupId: null,
currentGroupName: '',
searchQuery: '',
selectedGroups: [],
permissionDialogVisible: false,
// 添加群组相关数据
addGroupDialogVisible: false,
addGroupForm: {
groupId: ''
},
addGroupRules: {
groupId: [
{ required: true, message: '请输入群组ID', trigger: 'blur' },
{ pattern: /^\S+$/, message: '群组ID不能包含空格', trigger: 'blur' }
]
},
// 趋势图相关数据
trendDialogVisible: false,
trendLoading: false,
trendDays: 7,
trendChart: null
}
},
computed: {
filteredGroups() {
if (!this.searchQuery) return this.groups;
const query = this.searchQuery.toLowerCase();
return this.groups.filter(group =>
(group.group_id && group.group_id.toLowerCase().includes(query)) ||
(group.group_name && group.group_name.toLowerCase().includes(query))
);
}
},
mounted() {
this.currentView = '6'; // 设置当前菜单项
this.loadGroups();
},
methods: {
loadGroups() {
axios.get('/api/robot/groups')
.then(response => {
if (response.data.success) {
this.groups = response.data.data || [];
} else {
this.$message.error('加载群组失败');
}
})
.catch(error => {
console.error('加载群组失败:', error);
this.$message.error('加载群组失败: ' + error.message);
});
},
handleSelectionChange(selection) {
this.selectedGroups = selection.map(item => item.group_id);
},
viewGroupPermissions(group) {
this.currentGroupId = group.group_id;
this.currentGroupName = group.group_name || group.group_id;
axios.get(`/api/robot/group/${group.group_id}/permissions`)
.then(response => {
if (response.data.success) {
// 添加布尔值属性用于switch组件
const permissionsData = response.data.data || [];
this.permissions = permissionsData.map(p => ({
...p,
statusBool: p.status === 'enabled'
}));
this.permissionDialogVisible = true;
} else {
this.$message.error('加载权限失败');
}
})
.catch(error => {
console.error('加载权限失败:', error);
this.$message.error('加载权限失败: ' + error.message);
});
},
toggleRobotStatus(group) {
const newStatus = group.robot_status === 'enabled' ? 'disabled' : 'enabled';
// 找到ROBOT功能对应的feature_id
const robotFeatureId = 1; // 根据Feature枚举ROBOT的值为1
axios.post(`/api/robot/group/${group.group_id}/permissions`, {
feature_id: robotFeatureId,
status: newStatus
})
.then(response => {
if (response.data.success) {
group.robot_status = newStatus;
this.$message.success(`机器人已${newStatus === 'enabled' ? '启用' : '关闭'}`);
} else {
this.$message.error('操作失败');
}
})
.catch(error => {
console.error('更新权限失败:', error);
this.$message.error('更新权限失败: ' + error.message);
});
},
togglePermission(permission) {
const newStatus = permission.statusBool ? 'enabled' : 'disabled';
axios.post(`/api/robot/group/${this.currentGroupId}/permissions`, {
feature_id: permission.feature_id,
status: newStatus
})
.then(response => {
if (response.data.success) {
permission.status = newStatus;
// 如果是ROBOT功能更新群组列表中的状态
if (permission.feature_id === 1) { // ROBOT的feature_id为1
const group = this.groups.find(g => g.group_id === this.currentGroupId);
if (group) {
group.robot_status = newStatus;
}
}
this.$message.success(`功能已${newStatus === 'enabled' ? '启用' : '关闭'}`);
} else {
// 恢复原状态
permission.statusBool = !permission.statusBool;
this.$message.error('操作失败');
}
})
.catch(error => {
// 恢复原状态
permission.statusBool = !permission.statusBool;
console.error('更新权限失败:', error);
this.$message.error('更新权限失败: ' + error.message);
});
},
// 显示添加群组对话框
showAddGroupDialog() {
this.addGroupForm.groupId = '';
this.addGroupDialogVisible = true;
},
// 提交添加群组
submitAddGroup() {
this.$refs.addGroupForm.validate((valid) => {
if (valid) {
axios.post('/api/robot/add_group', {
group_id: this.addGroupForm.groupId
})
.then(response => {
if (response.data.success) {
this.$message.success(response.data.message);
// 添加新群组到列表
if (response.data.group) {
this.groups.push(response.data.group);
}
this.addGroupDialogVisible = false;
} else {
this.$message.error(response.data.error || '添加失败');
}
})
.catch(error => {
console.error('添加群组失败:', error);
this.$message.error('添加群组失败: ' + (error.response?.data?.error || error.message));
});
}
});
},
enableAllPermissions() {
this.$confirm('确定要启用所有功能吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const promises = this.permissions.map(permission => {
if (permission.status !== 'enabled') {
permission.statusBool = true;
return axios.post(`/api/robot/group/${this.currentGroupId}/permissions`, {
feature_id: permission.feature_id,
status: 'enabled'
});
}
return Promise.resolve();
});
Promise.all(promises)
.then(() => {
this.permissions.forEach(p => {
p.status = 'enabled';
p.statusBool = true;
});
// 更新群组列表中的机器人状态
const group = this.groups.find(g => g.group_id === this.currentGroupId);
if (group) {
group.robot_status = 'enabled';
}
this.$message.success('所有功能已启用');
})
.catch(error => {
console.error('批量启用失败:', error);
this.$message.error('批量启用失败');
});
}).catch(() => {});
},
disableAllPermissions() {
this.$confirm('确定要关闭所有功能吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const promises = this.permissions.map(permission => {
if (permission.status !== 'disabled') {
permission.statusBool = false;
return axios.post(`/api/robot/group/${this.currentGroupId}/permissions`, {
feature_id: permission.feature_id,
status: 'disabled'
});
}
return Promise.resolve();
});
Promise.all(promises)
.then(() => {
this.permissions.forEach(p => {
p.status = 'disabled';
p.statusBool = false;
});
// 更新群组列表中的机器人状态
const group = this.groups.find(g => g.group_id === this.currentGroupId);
if (group) {
group.robot_status = 'disabled';
}
this.$message.success('所有功能已关闭');
})
.catch(error => {
console.error('批量关闭失败:', error);
this.$message.error('批量关闭失败');
});
}).catch(() => {});
},
batchEnableRobot() {
if (this.selectedGroups.length === 0) {
this.$message.warning('请先选择群组');
return;
}
this.$confirm(`确定要批量启用 ${this.selectedGroups.length} 个群组的机器人吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const promises = this.selectedGroups.map(groupId => {
return axios.post(`/api/robot/group/${groupId}/permissions`, {
feature_id: 1, // ROBOT的feature_id为1
status: 'enabled'
});
});
Promise.all(promises)
.then(() => {
// 更新本地数据
this.selectedGroups.forEach(groupId => {
const group = this.groups.find(g => g.group_id === groupId);
if (group) {
group.robot_status = 'enabled';
}
});
this.$message.success('批量启用成功');
})
.catch(error => {
console.error('批量启用失败:', error);
this.$message.error('批量启用失败');
});
}).catch(() => {});
},
batchDisableRobot() {
if (this.selectedGroups.length === 0) {
this.$message.warning('请先选择群组');
return;
}
this.$confirm(`确定要批量关闭 ${this.selectedGroups.length} 个群组的机器人吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const promises = this.selectedGroups.map(groupId => {
return axios.post(`/api/robot/group/${groupId}/permissions`, {
feature_id: 1, // ROBOT的feature_id为1
status: 'disabled'
});
});
Promise.all(promises)
.then(() => {
// 更新本地数据
this.selectedGroups.forEach(groupId => {
const group = this.groups.find(g => g.group_id === groupId);
if (group) {
group.robot_status = 'disabled';
}
});
this.$message.success('批量关闭成功');
})
.catch(error => {
console.error('批量关闭失败:', error);
this.$message.error('批量关闭失败');
});
}).catch(() => {});
},
batchRemoveGroups() {
if (this.selectedGroups.length === 0) {
this.$message.warning('请先选择群组');
return;
}
this.$confirm(`确定要批量移除 ${this.selectedGroups.length} 个群组吗? 此操作将清除这些群组的所有设置。`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger'
}).then(() => {
axios.post('/api/robot/batch_operation', {
operation: 'remove_groups',
group_ids: this.selectedGroups
})
.then(response => {
if (response.data.success) {
// 从列表中移除这些群组
this.groups = this.groups.filter(g => !this.selectedGroups.includes(g.group_id));
this.selectedGroups = [];
this.$message.success('批量移除成功');
} else {
this.$message.error('批量移除失败');
}
})
.catch(error => {
console.error('批量移除失败:', error);
this.$message.error('批量移除失败: ' + error.message);
});
}).catch(() => {});
},
// 查看消息趋势
viewMessageTrend(group) {
this.currentGroupId = group.group_id;
this.currentGroupName = group.group_name || group.group_id;
this.trendDialogVisible = true;
// 不在这里立即加载,而是等对话框打开后再加载
},
// 对话框打开后的回调
onTrendDialogOpened() {
console.log('对话框已打开,准备加载数据');
// 重置图表
this.trendChart = null;
this.loadMessageTrend();
},
loadMessageTrend() {
this.trendLoading = true;
console.log('开始加载消息趋势数据');
axios.get(`/api/robot/group/${this.currentGroupId}/message_trend?days=${this.trendDays}`)
.then(response => {
if (response.data.success) {
console.log('数据加载成功,准备渲染图表');
// 延迟渲染确保DOM已更新
this.$nextTick(() => {
setTimeout(() => {
this.renderTrendChart(response.data.data);
}, 300);
});
} else {
this.$message.error('加载消息趋势失败');
this.trendLoading = false;
}
})
.catch(error => {
console.error('加载消息趋势失败:', error);
this.$message.error('加载消息趋势失败: ' + error.message);
this.trendLoading = false;
});
},
renderTrendChart(data) {
try {
console.log('开始渲染图表');
this.trendLoading = false;
// 使用Vue的$refs获取Canvas元素
const canvas = this.$refs.trendChart;
console.log('Canvas元素:', canvas);
if (!canvas) {
console.error('找不到Canvas元素 (通过$refs)');
// 尝试通过ID获取
const canvasById = document.getElementById('messageTrendChart');
console.log('通过ID获取Canvas元素:', canvasById);
if (!canvasById) {
this.$message.error('无法找到图表元素,请尝试重新打开对话框');
return;
}
// 如果能通过ID获取使用它
canvas = canvasById;
}
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('无法获取Canvas上下文');
this.$message.error('无法初始化图表,请尝试重新打开对话框');
return;
}
// 如果已有图表,先销毁
if (this.trendChart) {
this.trendChart.destroy();
}
console.log('创建新图表');
// 创建新图表
this.trendChart = new Chart(ctx, {
type: 'line',
data: {
labels: data.dates,
datasets: [{
label: '消息数量',
data: data.counts,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 2,
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '群聊消息数量趋势'
},
tooltip: {
mode: 'index',
intersect: false
},
legend: {
position: 'top',
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: '消息数量'
}
},
x: {
ticks: {
maxRotation: 45,
minRotation: 45
}
}
}
}
});
console.log('图表创建完成');
} catch (error) {
console.error('渲染图表出错:', error);
this.$message.error('渲染图表出错: ' + error.message);
this.trendLoading = false;
}
}
}
});
</script>
{% endblock %}