移除设备状态相关功能及页面,优化权限管理,提升代码整洁性
This commit is contained in:
@@ -43,10 +43,6 @@
|
|||||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></i>
|
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg></i>
|
||||||
<span>公告管理</span>
|
<span>公告管理</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item v-if="canAccessDeviceStatus" index="DeviceStatus" :route="{ name: 'DeviceStatus' }">
|
|
||||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M21 6h-7V4h-4v2H3c-1.1 0-2 .9-2 2v10a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2m0 12H3V8h18v10M8 10h2v6H8v-6m3 0h2v6h-2v-6m3 0h2v6h-2v-6Z"/></svg></i>
|
|
||||||
<span>设备状态</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item v-if="canAccessSettings" index="Settings" :route="{ name: 'Settings' }">
|
<el-menu-item v-if="canAccessSettings" index="Settings" :route="{ name: 'Settings' }">
|
||||||
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="m12 8l-2 4h4l-2 4"/></svg></i>
|
<i class="el-icon"><svg viewBox="0 0 24 24"><path fill="currentColor" d="m12 8l-2 4h4l-2 4"/></svg></i>
|
||||||
<span>系统设置</span>
|
<span>系统设置</span>
|
||||||
@@ -118,7 +114,6 @@ const pageTitleMap = {
|
|||||||
'Links': '链接管理',
|
'Links': '链接管理',
|
||||||
'Refund': '退单管理',
|
'Refund': '退单管理',
|
||||||
'Announcements': '公告管理',
|
'Announcements': '公告管理',
|
||||||
'DeviceStatus': '设备状态',
|
|
||||||
'Settings': '系统设置'
|
'Settings': '系统设置'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +130,6 @@ const canAccessUsers = computed(() => canAccessRoute('Users'))
|
|||||||
const canAccessLinks = computed(() => canAccessRoute('Links'))
|
const canAccessLinks = computed(() => canAccessRoute('Links'))
|
||||||
const canAccessRefund = computed(() => canAccessRoute('Refund'))
|
const canAccessRefund = computed(() => canAccessRoute('Refund'))
|
||||||
const canAccessAnnouncements = computed(() => canAccessRoute('Announcements'))
|
const canAccessAnnouncements = computed(() => canAccessRoute('Announcements'))
|
||||||
const canAccessDeviceStatus = computed(() => canAccessRoute('DeviceStatus'))
|
|
||||||
const canAccessSettings = computed(() => canAccessRoute('Settings'))
|
const canAccessSettings = computed(() => canAccessRoute('Settings'))
|
||||||
|
|
||||||
// 获取积分余额
|
// 获取积分余额
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const AnnouncementList = () => import('@/views/announcements/AnnouncementList.vu
|
|||||||
const RefundManagement = () => import('@/views/refund/RefundManagement.vue')
|
const RefundManagement = () => import('@/views/refund/RefundManagement.vue')
|
||||||
const Play = () => import('@/views/Play.vue')
|
const Play = () => import('@/views/Play.vue')
|
||||||
const NotFound = () => import('@/views/NotFound.vue')
|
const NotFound = () => import('@/views/NotFound.vue')
|
||||||
const DeviceStatus = () => import('@/views/devices/DeviceStatus.vue')
|
|
||||||
|
|
||||||
export const routes = [
|
export const routes = [
|
||||||
{ path: '/login', name: 'Login', component: Login, meta: { public: true, title: '登录' } },
|
{ path: '/login', name: 'Login', component: Login, meta: { public: true, title: '登录' } },
|
||||||
@@ -39,7 +38,6 @@ export const routes = [
|
|||||||
{ path: 'links', name: 'Links', component: LinkGenerate, meta: { title: '链接管理' } },
|
{ path: 'links', name: 'Links', component: LinkGenerate, meta: { title: '链接管理' } },
|
||||||
{ path: 'refund', name: 'Refund', component: RefundManagement, meta: { title: '退单管理' } },
|
{ path: 'refund', name: 'Refund', component: RefundManagement, meta: { title: '退单管理' } },
|
||||||
{ path: 'announcements', name: 'Announcements', component: AnnouncementList, meta: { title: '公告管理' } },
|
{ path: 'announcements', name: 'Announcements', component: AnnouncementList, meta: { title: '公告管理' } },
|
||||||
{ path: 'devices', name: 'DeviceStatus', component: DeviceStatus, meta: { title: '设备状态' } },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound, meta: { public: true, title: '未找到' } },
|
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound, meta: { public: true, title: '未找到' } },
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ export const PERMISSIONS = {
|
|||||||
REFUND_EXECUTE: 'refund:execute',
|
REFUND_EXECUTE: 'refund:execute',
|
||||||
REFUND_VIEW: 'refund:view',
|
REFUND_VIEW: 'refund:view',
|
||||||
|
|
||||||
// 设备状态查看
|
|
||||||
DEVICE_VIEW: 'device:view',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色权限映射
|
// 角色权限映射
|
||||||
@@ -96,7 +94,6 @@ export const ROUTE_PERMISSIONS = {
|
|||||||
'Links': [PERMISSIONS.LINK_VIEW],
|
'Links': [PERMISSIONS.LINK_VIEW],
|
||||||
'Refund': [PERMISSIONS.REFUND_VIEW],
|
'Refund': [PERMISSIONS.REFUND_VIEW],
|
||||||
'Announcements': [PERMISSIONS.ANNOUNCEMENT_VIEW],
|
'Announcements': [PERMISSIONS.ANNOUNCEMENT_VIEW],
|
||||||
'DeviceStatus': [PERMISSIONS.DEVICE_VIEW],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户信息
|
// 获取当前用户信息
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="device-status-view">
|
|
||||||
<el-card class="header-card">
|
|
||||||
<template #header>
|
|
||||||
<div class="header">
|
|
||||||
<h3 class="title">设备状态</h3>
|
|
||||||
<div class="actions">
|
|
||||||
<el-button type="primary" :loading="loading" @click="fetchData">
|
|
||||||
<el-icon><Refresh /></el-icon>
|
|
||||||
刷新
|
|
||||||
</el-button>
|
|
||||||
<el-switch v-model="autoRefresh" active-text="自动刷新" @change="onToggleAuto" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="stats">
|
|
||||||
<el-statistic title="设备总数" :value="stats.totalDevices" />
|
|
||||||
<el-statistic title="已占用" :value="stats.runningCount" />
|
|
||||||
<el-statistic title="使用中" :value="stats.usingCount" />
|
|
||||||
<el-statistic title="冷却空闲" :value="stats.idleCooldownCount" />
|
|
||||||
<el-statistic title="空闲" :value="stats.idleFreeCount" />
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="table-card">
|
|
||||||
<el-table :data="categoryRows" v-loading="loading" border style="width: 100%">
|
|
||||||
<el-table-column prop="label" label="类别" width="140" />
|
|
||||||
<el-table-column prop="count" label="数量" width="100" />
|
|
||||||
<el-table-column label="设备列表" min-width="300">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div class="devices-list" :title="row.devices.join(', ')">{{ row.preview }}</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
|
|
||||||
import { Refresh } from '@element-plus/icons-vue'
|
|
||||||
import { getAllDeviceStatus } from '@/api/devices'
|
|
||||||
import { isAdmin, canAccessRoute } from '@/utils/permission'
|
|
||||||
|
|
||||||
// 仅管理员可访问(路由守卫已做拦截,这里再次防御)
|
|
||||||
const allowed = computed(() => isAdmin() || canAccessRoute('DeviceStatus'))
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const autoRefresh = ref(true)
|
|
||||||
const timer = ref(null)
|
|
||||||
|
|
||||||
const stats = reactive({ totalDevices: 0, runningCount: 0, usingCount: 0, idleCooldownCount: 0, idleFreeCount: 0 })
|
|
||||||
const categoryToDevices = ref({})
|
|
||||||
|
|
||||||
const categoryOrder = ['RUNNING', 'USING', 'IDLE_COOLDOWN', 'IDLE_FREE']
|
|
||||||
const categoryLabelMap = { RUNNING: '已占用', USING: '使用中', IDLE_COOLDOWN: '冷却空闲', IDLE_FREE: '空闲' }
|
|
||||||
const categoryRows = computed(() => {
|
|
||||||
const map = categoryToDevices.value || {}
|
|
||||||
return categoryOrder.map(key => {
|
|
||||||
const devices = Array.isArray(map[key]) ? map[key] : []
|
|
||||||
const preview = devices.join(', ')
|
|
||||||
return { key, label: categoryLabelMap[key] || key, count: devices.length, devices, preview }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
async function fetchData() {
|
|
||||||
if (!allowed.value) return
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
const data = await getAllDeviceStatus()
|
|
||||||
categoryToDevices.value = data?.categoryToDevices || {}
|
|
||||||
stats.totalDevices = data?.totalDevices ?? 0
|
|
||||||
stats.runningCount = data?.runningCount ?? (categoryToDevices.value['RUNNING']?.length || 0)
|
|
||||||
stats.usingCount = data?.usingCount ?? (categoryToDevices.value['USING']?.length || 0)
|
|
||||||
stats.idleCooldownCount = data?.idleCooldownCount ?? (categoryToDevices.value['IDLE_COOLDOWN']?.length || 0)
|
|
||||||
stats.idleFreeCount = data?.idleFreeCount ?? (categoryToDevices.value['IDLE_FREE']?.length || 0)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onToggleAuto(val) {
|
|
||||||
if (val) startAuto()
|
|
||||||
else stopAuto()
|
|
||||||
}
|
|
||||||
|
|
||||||
function startAuto() {
|
|
||||||
stopAuto()
|
|
||||||
timer.value = setInterval(fetchData, 2000)
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopAuto() {
|
|
||||||
if (timer.value) {
|
|
||||||
clearInterval(timer.value)
|
|
||||||
timer.value = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
fetchData()
|
|
||||||
if (autoRefresh.value) startAuto()
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
stopAuto()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.device-status-view { padding: 0; }
|
|
||||||
.header-card { margin-bottom: 16px; }
|
|
||||||
.header { display: flex; align-items: center; justify-content: space-between; }
|
|
||||||
.title { margin: 0; font-size: 18px; font-weight: 600; }
|
|
||||||
.actions { display: flex; gap: 12px; align-items: center; }
|
|
||||||
.stats { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
|
|
||||||
.inline-tag { height: 24px; align-items: center; }
|
|
||||||
.devices-list { white-space: normal; word-break: break-all; line-height: 1.6; }
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.title { font-size: 20px; }
|
|
||||||
.actions { gap: 8px; }
|
|
||||||
.stats { gap: 12px; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user