Initial commit of akmon project
This commit is contained in:
837
pages/mall/nfc/admin/index.uvue
Normal file
837
pages/mall/nfc/admin/index.uvue
Normal file
@@ -0,0 +1,837 @@
|
||||
<template>
|
||||
<view class="admin-dashboard">
|
||||
<!-- 顶部统计卡片 -->
|
||||
<view class="stats-cards">
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon users">
|
||||
<image src="/static/icons/users.png" />
|
||||
</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ statsData.totalUsers }}</text>
|
||||
<text class="stat-label">总用户数</text>
|
||||
<text class="stat-change positive">+{{ statsData.userGrowth }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon transactions">
|
||||
<image src="/static/icons/transaction.png" />
|
||||
</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ statsData.todayTransactions }}</text>
|
||||
<text class="stat-label">今日交易</text>
|
||||
<text class="stat-change" :class="{ 'positive': statsData.transactionGrowth > 0 }">
|
||||
{{ statsData.transactionGrowth > 0 ? '+' : '' }}{{ statsData.transactionGrowth }}%
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon revenue">
|
||||
<image src="/static/icons/revenue.png" />
|
||||
</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">¥{{ statsData.todayRevenue }}</text>
|
||||
<text class="stat-label">今日营收</text>
|
||||
<text class="stat-change positive">+{{ statsData.revenueGrowth }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-card">
|
||||
<view class="stat-icon devices">
|
||||
<image src="/static/icons/devices.png" />
|
||||
</view>
|
||||
<view class="stat-info">
|
||||
<text class="stat-value">{{ statsData.onlineDevices }}/{{ statsData.totalDevices }}</text>
|
||||
<text class="stat-label">在线设备</text>
|
||||
<text class="stat-change" :class="{ 'negative': deviceOfflineRate > 10 }">
|
||||
{{ (100 - deviceOfflineRate).toFixed(1) }}%
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 系统状态监控 -->
|
||||
<view class="system-status">
|
||||
<view class="section-header">
|
||||
<text class="section-title">系统状态</text>
|
||||
<text class="last-update">更新时间: {{ formatTime(lastUpdateTime) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="status-grid">
|
||||
<view class="status-item" v-for="service in systemServices" :key="service.name">
|
||||
<view class="service-info">
|
||||
<view class="service-status" :class="service.status">
|
||||
<view class="status-dot"></view>
|
||||
</view>
|
||||
<text class="service-name">{{ service.name }}</text>
|
||||
</view>
|
||||
<view class="service-metrics">
|
||||
<text class="metric-item">CPU: {{ service.cpu }}%</text>
|
||||
<text class="metric-item">内存: {{ service.memory }}%</text>
|
||||
<text class="metric-item">响应: {{ service.responseTime }}ms</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 实时告警 -->
|
||||
<view class="alerts-section" v-if="alerts.length > 0">
|
||||
<view class="section-header">
|
||||
<text class="section-title">实时告警</text>
|
||||
<text class="alert-count">{{ alerts.length }}条未处理</text>
|
||||
</view>
|
||||
|
||||
<view class="alerts-list">
|
||||
<view class="alert-item"
|
||||
v-for="alert in alerts"
|
||||
:key="alert.id"
|
||||
:class="alert.level"
|
||||
@click="handleAlert(alert)">
|
||||
<view class="alert-icon">
|
||||
<image :src="getAlertIcon(alert.level)" />
|
||||
</view>
|
||||
<view class="alert-content">
|
||||
<text class="alert-title">{{ alert.title }}</text>
|
||||
<text class="alert-desc">{{ alert.description }}</text>
|
||||
<text class="alert-time">{{ formatTime(alert.time) }}</text>
|
||||
</view>
|
||||
<view class="alert-actions">
|
||||
<button class="action-btn" @click.stop="resolveAlert(alert.id)">处理</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
<view class="quick-operations">
|
||||
<view class="section-title">快速操作</view>
|
||||
<view class="operations-grid">
|
||||
<view class="operation-item" @click="goToUserManagement">
|
||||
<image class="operation-icon" src="/static/icons/user-manage.png" />
|
||||
<text class="operation-text">用户管理</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToCardManagement">
|
||||
<image class="operation-icon" src="/static/icons/card-manage.png" />
|
||||
<text class="operation-text">卡片管理</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToFinancialManagement">
|
||||
<image class="operation-icon" src="/static/icons/finance.png" />
|
||||
<text class="operation-text">财务管理</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToDeviceManagement">
|
||||
<image class="operation-icon" src="/static/icons/device-manage.png" />
|
||||
<text class="operation-text">设备管理</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToReports">
|
||||
<image class="operation-icon" src="/static/icons/reports.png" />
|
||||
<text class="operation-text">数据报表</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToSystemConfig">
|
||||
<image class="operation-icon" src="/static/icons/settings.png" />
|
||||
<text class="operation-text">系统配置</text>
|
||||
</view>
|
||||
<view class="operation-item" @click="goToSecurityMonitor">
|
||||
<image class="operation-icon" src="/static/icons/security.png" />
|
||||
<text class="operation-text">安全监控</text>
|
||||
</view>
|
||||
<view class="operation-item emergency" @click="goToEmergencyControl">
|
||||
<image class="operation-icon" src="/static/icons/emergency.png" />
|
||||
<text class="operation-text">应急控制</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 数据概览图表 -->
|
||||
<view class="charts-section">
|
||||
<view class="section-title">数据概览</view>
|
||||
|
||||
<view class="chart-container">
|
||||
<view class="chart-header">
|
||||
<text class="chart-title">交易趋势</text>
|
||||
<view class="chart-filters">
|
||||
<text class="filter-item"
|
||||
:class="{ 'active': chartPeriod === 'day' }"
|
||||
@click="changeChartPeriod('day')">今日</text>
|
||||
<text class="filter-item"
|
||||
:class="{ 'active': chartPeriod === 'week' }"
|
||||
@click="changeChartPeriod('week')">本周</text>
|
||||
<text class="filter-item"
|
||||
:class="{ 'active': chartPeriod === 'month' }"
|
||||
@click="changeChartPeriod('month')">本月</text>
|
||||
</view>
|
||||
</view>
|
||||
<canvas canvas-id="transactionChart" class="chart-canvas"></canvas>
|
||||
</view>
|
||||
|
||||
<view class="mini-charts">
|
||||
<view class="mini-chart">
|
||||
<text class="mini-chart-title">消费分布</text>
|
||||
<canvas canvas-id="consumptionChart" class="mini-chart-canvas"></canvas>
|
||||
</view>
|
||||
<view class="mini-chart">
|
||||
<text class="mini-chart-title">设备状态</text>
|
||||
<canvas canvas-id="deviceChart" class="mini-chart-canvas"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<view class="system-info">
|
||||
<view class="section-title">系统信息</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="info-label">系统版本</text>
|
||||
<text class="info-value">v2.1.0</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">数据库</text>
|
||||
<text class="info-value">PostgreSQL 14.2</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">在线时长</text>
|
||||
<text class="info-value">{{ systemUptime }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">存储使用</text>
|
||||
<text class="info-value">{{ storageUsage }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statsData: {
|
||||
totalUsers: 1247,
|
||||
userGrowth: 12.5,
|
||||
todayTransactions: 3652,
|
||||
transactionGrowth: 8.3,
|
||||
todayRevenue: '45,680',
|
||||
revenueGrowth: 15.2,
|
||||
onlineDevices: 285,
|
||||
totalDevices: 310
|
||||
},
|
||||
systemServices: [
|
||||
{
|
||||
name: '核心服务',
|
||||
status: 'healthy',
|
||||
cpu: 35,
|
||||
memory: 68,
|
||||
responseTime: 120
|
||||
},
|
||||
{
|
||||
name: 'NFC服务',
|
||||
status: 'healthy',
|
||||
cpu: 28,
|
||||
memory: 45,
|
||||
responseTime: 95
|
||||
},
|
||||
{
|
||||
name: '支付服务',
|
||||
status: 'warning',
|
||||
cpu: 78,
|
||||
memory: 82,
|
||||
responseTime: 350
|
||||
},
|
||||
{
|
||||
name: '门禁服务',
|
||||
status: 'healthy',
|
||||
cpu: 22,
|
||||
memory: 38,
|
||||
responseTime: 85
|
||||
}
|
||||
],
|
||||
alerts: [
|
||||
{
|
||||
id: 1,
|
||||
level: 'warning',
|
||||
title: '支付服务响应缓慢',
|
||||
description: '支付服务平均响应时间超过300ms',
|
||||
time: new Date(Date.now() - 300000)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
level: 'error',
|
||||
title: '设备离线',
|
||||
description: '饭堂POS机#003失去连接',
|
||||
time: new Date(Date.now() - 600000)
|
||||
}
|
||||
],
|
||||
lastUpdateTime: new Date(),
|
||||
chartPeriod: 'day',
|
||||
systemUptime: '15天8小时',
|
||||
storageUsage: 67
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
deviceOfflineRate() {
|
||||
return ((this.statsData.totalDevices - this.statsData.onlineDevices) / this.statsData.totalDevices * 100)
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.initDashboard()
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.refreshDashboard()
|
||||
},
|
||||
methods: {
|
||||
initDashboard() {
|
||||
this.loadStatsData()
|
||||
this.loadSystemStatus()
|
||||
this.loadAlerts()
|
||||
this.initCharts()
|
||||
this.startRealTimeUpdate()
|
||||
},
|
||||
loadStatsData() {
|
||||
uni.request({
|
||||
url: '/api/v1/admin/dashboard-stats',
|
||||
success: (res) => {
|
||||
this.statsData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
loadSystemStatus() {
|
||||
uni.request({
|
||||
url: '/api/v1/admin/system-status',
|
||||
success: (res) => {
|
||||
this.systemServices = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
loadAlerts() {
|
||||
uni.request({
|
||||
url: '/api/v1/admin/alerts',
|
||||
success: (res) => {
|
||||
this.alerts = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
refreshDashboard() {
|
||||
Promise.all([
|
||||
this.loadStatsData(),
|
||||
this.loadSystemStatus(),
|
||||
this.loadAlerts()
|
||||
]).then(() => {
|
||||
this.lastUpdateTime = new Date()
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
startRealTimeUpdate() {
|
||||
// 每30秒更新一次数据
|
||||
setInterval(() => {
|
||||
this.loadStatsData()
|
||||
this.loadSystemStatus()
|
||||
this.lastUpdateTime = new Date()
|
||||
}, 30000)
|
||||
},
|
||||
initCharts() {
|
||||
// 初始化图表
|
||||
this.drawTransactionChart()
|
||||
this.drawConsumptionChart()
|
||||
this.drawDeviceChart()
|
||||
},
|
||||
drawTransactionChart() {
|
||||
// 绘制交易趋势图
|
||||
const ctx = uni.createCanvasContext('transactionChart', this)
|
||||
// 这里实现具体的图表绘制逻辑
|
||||
ctx.draw()
|
||||
},
|
||||
drawConsumptionChart() {
|
||||
// 绘制消费分布图
|
||||
const ctx = uni.createCanvasContext('consumptionChart', this)
|
||||
// 实现饼图或柱状图
|
||||
ctx.draw()
|
||||
},
|
||||
drawDeviceChart() {
|
||||
// 绘制设备状态图
|
||||
const ctx = uni.createCanvasContext('deviceChart', this)
|
||||
// 实现状态分布图
|
||||
ctx.draw()
|
||||
},
|
||||
changeChartPeriod(period) {
|
||||
this.chartPeriod = period
|
||||
this.drawTransactionChart()
|
||||
},
|
||||
handleAlert(alert) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/nfc/admin/alert-detail?id=${alert.id}`
|
||||
})
|
||||
},
|
||||
resolveAlert(alertId) {
|
||||
uni.request({
|
||||
url: '/api/v1/admin/resolve-alert',
|
||||
method: 'POST',
|
||||
data: { alertId },
|
||||
success: () => {
|
||||
this.alerts = this.alerts.filter(alert => alert.id !== alertId)
|
||||
uni.showToast({
|
||||
title: '告警已处理',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getAlertIcon(level) {
|
||||
const icons = {
|
||||
info: '/static/icons/info.png',
|
||||
warning: '/static/icons/warning.png',
|
||||
error: '/static/icons/error.png',
|
||||
critical: '/static/icons/critical.png'
|
||||
}
|
||||
return icons[level] || icons.info
|
||||
},
|
||||
// 导航方法
|
||||
goToUserManagement() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/user-management' })
|
||||
},
|
||||
goToCardManagement() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/card-management' })
|
||||
},
|
||||
goToFinancialManagement() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/financial-management' })
|
||||
},
|
||||
goToDeviceManagement() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/device-management' })
|
||||
},
|
||||
goToReports() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/data-reports' })
|
||||
},
|
||||
goToSystemConfig() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/system-config' })
|
||||
},
|
||||
goToSecurityMonitor() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/security-monitor' })
|
||||
},
|
||||
goToEmergencyControl() {
|
||||
uni.showModal({
|
||||
title: '应急控制',
|
||||
content: '即将进入应急控制模式,请确认您有相应权限',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/admin/emergency-control' })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
formatTime(time) {
|
||||
const now = new Date()
|
||||
const diff = now - time
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
|
||||
if (minutes < 1) return '刚刚'
|
||||
if (minutes < 60) return `${minutes}分钟前`
|
||||
|
||||
const hours = Math.floor(minutes / 60)
|
||||
if (hours < 24) return `${hours}小时前`
|
||||
|
||||
return time.toLocaleDateString()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.admin-dashboard {
|
||||
padding: 20rpx;
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.stats-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.stat-icon.users {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.stat-icon.transactions {
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.stat-icon.revenue {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.stat-icon.devices {
|
||||
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||||
}
|
||||
|
||||
.stat-icon image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.stat-change.positive {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.stat-change.negative {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.system-status, .alerts-section, .quick-operations,
|
||||
.charts-section, .system-info {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.last-update {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.alert-count {
|
||||
font-size: 24rpx;
|
||||
color: #dc3545;
|
||||
background: #f8d7da;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.status-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.service-status {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 12rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.service-status.healthy {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.service-status.warning {
|
||||
background: #ffc107;
|
||||
}
|
||||
|
||||
.service-status.error {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.service-metrics {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
font-size: 20rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.alerts-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.alert-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
border-left: 6rpx solid;
|
||||
}
|
||||
|
||||
.alert-item.warning {
|
||||
background: #fff3cd;
|
||||
border-left-color: #ffc107;
|
||||
}
|
||||
|
||||
.alert-item.error {
|
||||
background: #f8d7da;
|
||||
border-left-color: #dc3545;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.alert-icon image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.alert-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.alert-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.alert-time {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.alert-actions {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.operations-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.operation-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30rpx 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
border-radius: 12rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.operation-item:hover {
|
||||
border-color: #007AFF;
|
||||
background: #f8f9ff;
|
||||
}
|
||||
|
||||
.operation-item.emergency {
|
||||
border-color: #dc3545;
|
||||
background: #fff5f5;
|
||||
}
|
||||
|
||||
.operation-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.operation-text {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chart-filters {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
|
||||
.filter-item.active {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
border-color: #007AFF;
|
||||
}
|
||||
|
||||
.chart-canvas {
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
}
|
||||
|
||||
.mini-charts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.mini-chart {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mini-chart-title {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mini-chart-canvas {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid #eee;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user