|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 21 KiB |
@ -0,0 +1,26 @@ |
|||||
|
const emoTextList = ['憨笑', '媚眼', '开心', '坏笑', '可怜', '爱心', '笑哭', '拍手', '惊喜', '打气', |
||||
|
'大哭', '流泪', '饥饿', '难受', '健身', '示爱', '色色', '眨眼', '暴怒', '惊恐', |
||||
|
'思考', '头晕', '大吐', '酷笑', '翻滚', '享受', '鼻涕', '快乐', '雀跃', '微笑', |
||||
|
'贪婪', '红心', '粉心', '星星', '大火', '眼睛', '音符', "叹号", "问号", "绿叶", |
||||
|
"燃烧", "喇叭", "警告", "信封", "房子", "礼物", "点赞", "举手", "喝彩", "点头", |
||||
|
"摇头", "偷瞄", "庆祝", "疾跑", "打滚", "惊吓", "起跳" |
||||
|
]; |
||||
|
|
||||
|
|
||||
|
// 将匹配结果替换表情图片
|
||||
|
let textToImg = (emoText) => { |
||||
|
let word = emoText.replace(/\#|\;/gi, ''); |
||||
|
|
||||
|
let idx = emoTextList.indexOf(word); |
||||
|
if (idx == -1) { |
||||
|
return emoText; |
||||
|
} |
||||
|
|
||||
|
let url = new URL(`../assets/emoji/${idx}.gif`, import.meta.url).href; |
||||
|
return `<img src="${url}" style="width:20px;heigt:20px;" />` |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export const transform = (content) => { |
||||
|
return content.replace(/\#[\u4E00-\u9FA5]{1,3}\;/gi, (text) => textToImg(text)); |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
export const html2Escape = (strText) => { |
||||
|
return strText.replace(/[<>&"]/g, function(c) { |
||||
|
return { |
||||
|
'<': '<', |
||||
|
'>': '>', |
||||
|
'&': '&', |
||||
|
'"': '"' |
||||
|
} [c]; |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
export const replaceURLWithHTMLLinks = (content) => { |
||||
|
// 使用正则表达式匹配更广泛的URL格式(此正则由deepseek生成)
|
||||
|
const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]|\bwww\.[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; |
||||
|
return content.replace(urlRegex, (url) => { |
||||
|
// 如果URL不以http(s)://开头,则添加http://前缀
|
||||
|
if (!url.startsWith("http")) { |
||||
|
url = "http://" + url; |
||||
|
} |
||||
|
return `<a href="${url}" target="_blank" style="color: #5359dc;text-decoration: underline;">${url}</a>`; |
||||
|
}); |
||||
|
} |
||||
@ -1,81 +1,530 @@ |
|||||
<template> |
<template> |
||||
<div class="app-container home"> |
<div class="app-container home"> |
||||
|
<!-- 统计指标卡片 --> |
||||
|
<el-row :gutter="20" style="margin-bottom: 20px"> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="statistics-card"> |
||||
|
<div class="statistics-item"> |
||||
|
<div class="statistics-icon" style="background-color: #409EFF;"> |
||||
|
<el-icon size="24"> |
||||
|
<User /> |
||||
|
</el-icon> |
||||
|
</div> |
||||
|
<div class="statistics-content"> |
||||
|
<div class="statistics-title">总用户量</div> |
||||
|
<div class="statistics-value">{{ totalUserCount }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="statistics-card"> |
||||
|
<div class="statistics-item"> |
||||
|
<div class="statistics-icon" style="background-color: #67C23A;"> |
||||
|
<el-icon size="24"> |
||||
|
<ChatDotRound /> |
||||
|
</el-icon> |
||||
|
</div> |
||||
|
<div class="statistics-content"> |
||||
|
<div class="statistics-title">总群组数量</div> |
||||
|
<div class="statistics-value">{{ totalGroupCount }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="statistics-card"> |
||||
|
<div class="statistics-item"> |
||||
|
<div class="statistics-icon" style="background-color: #E6A23C;"> |
||||
|
<el-icon size="24"> |
||||
|
<Timer /> |
||||
|
</el-icon> |
||||
|
</div> |
||||
|
<div class="statistics-content"> |
||||
|
<div class="statistics-title">日活用户</div> |
||||
|
<div class="statistics-value">{{ dailyActiveCount }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="statistics-card"> |
||||
|
<div class="statistics-item"> |
||||
|
<div class="statistics-icon" style="background-color: #909399;"> |
||||
|
<el-icon size="24"> |
||||
|
<Clock /> |
||||
|
</el-icon> |
||||
|
</div> |
||||
|
<div class="statistics-content"> |
||||
|
<div class="statistics-title">月活用户</div> |
||||
|
<div class="statistics-value">{{ monthlyActiveCount }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 统计图表 --> |
||||
|
<div class="chart-section"> |
||||
<el-row :gutter="20"> |
<el-row :gutter="20"> |
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px"> |
<el-col :span="12"> |
||||
<h2>盒子IM 后台管理系统</h2> |
<el-card> |
||||
<h4>盒子IM是一个仿微信的聊天系统,支持私聊、群聊、离线消息、发送图片、文件、语音、emoji表情、音视频通话等功能</h4> |
<template #header> |
||||
|
<span>用户注册</span> |
||||
|
<el-select v-model="registrationSelectedDays" @change="loadRegistrationChartData" |
||||
|
style="width: 100px; float: right;" size="small"> |
||||
|
<el-option label="7天" :value="7"></el-option> |
||||
|
<el-option label="15天" :value="15"></el-option> |
||||
|
<el-option label="30天" :value="30"></el-option> |
||||
|
</el-select> |
||||
|
</template> |
||||
|
<div ref="registrationChartContainer" style="width: 100%; height: 300px;"></div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<el-card> |
||||
|
<template #header> |
||||
|
<span>消息统计</span> |
||||
|
<el-select v-model="selectedDays" @change="loadChartData" style="width: 100px; float: right;" |
||||
|
size="small"> |
||||
|
<el-option label="7天" :value="7"></el-option> |
||||
|
<el-option label="15天" :value="15"></el-option> |
||||
|
<el-option label="30天" :value="30"></el-option> |
||||
|
</el-select> |
||||
|
</template> |
||||
|
<div ref="chartContainer" style="width: 100%; height: 300px;"></div> |
||||
|
</el-card> |
||||
</el-col> |
</el-col> |
||||
</el-row> |
</el-row> |
||||
<el-divider /> |
</div> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup name="Index" lang="ts"> |
<script setup name="Index" lang="ts"> |
||||
const goTarget = (url: string) => { |
import { ref, onMounted, nextTick, computed } from 'vue'; |
||||
window.open(url, '__blank'); |
import * as echarts from 'echarts'; |
||||
|
import { getDailyMessageCount } from '@/api/im/privateMessage'; |
||||
|
import { getDailyGroupMessageCount } from '@/api/im/groupMessage'; |
||||
|
import { getDailyRegistrationCount, getTotalUserCount, getActiveUserStats } from '@/api/im/user'; |
||||
|
import { getTotalGroupCount } from '@/api/im/group'; |
||||
|
import { User, ChatDotRound, Timer, Calendar, Clock } from '@element-plus/icons-vue'; |
||||
|
|
||||
|
const chartContainer = ref<HTMLElement>(); |
||||
|
const selectedDays = ref(7); |
||||
|
let chartInstance: echarts.ECharts | null = null; |
||||
|
|
||||
|
const registrationChartContainer = ref<HTMLElement>(); |
||||
|
const registrationSelectedDays = ref(7); |
||||
|
let registrationChartInstance: echarts.ECharts | null = null; |
||||
|
|
||||
|
// 总用户数量 |
||||
|
const totalUserCount = ref(0); |
||||
|
|
||||
|
// 总群组数量 |
||||
|
const totalGroupCount = ref(0); |
||||
|
|
||||
|
// 活跃用户数据 |
||||
|
const dailyActiveCount = ref(0); |
||||
|
const weeklyActiveCount = ref(0); |
||||
|
const monthlyActiveCount = ref(0); |
||||
|
|
||||
|
// 加载总用户数量 |
||||
|
const loadTotalUserCount = async () => { |
||||
|
try { |
||||
|
const response = await getTotalUserCount(); |
||||
|
totalUserCount.value = response.data; |
||||
|
} catch (error) { |
||||
|
console.error('加载总用户数量失败:', error); |
||||
|
totalUserCount.value = 0; |
||||
|
} |
||||
}; |
}; |
||||
</script> |
|
||||
|
|
||||
<style scoped lang="scss"> |
// 加载总群组数量 |
||||
.home { |
const loadTotalGroupCount = async () => { |
||||
blockquote { |
try { |
||||
padding: 10px 20px; |
const response = await getTotalGroupCount(); |
||||
margin: 0 0 20px; |
totalGroupCount.value = response.data; |
||||
font-size: 17.5px; |
} catch (error) { |
||||
border-left: 5px solid #eee; |
console.error('加载总群组数量失败:', error); |
||||
|
totalGroupCount.value = 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 加载活跃用户统计数据 |
||||
|
const loadActiveUserStats = async () => { |
||||
|
try { |
||||
|
const response = await getActiveUserStats(); |
||||
|
dailyActiveCount.value = response.data.dailyActive; |
||||
|
weeklyActiveCount.value = response.data.weeklyActive; |
||||
|
monthlyActiveCount.value = response.data.monthlyActive; |
||||
|
} catch (error) { |
||||
|
console.error('加载活跃用户统计失败:', error); |
||||
|
dailyActiveCount.value = 0; |
||||
|
weeklyActiveCount.value = 0; |
||||
|
monthlyActiveCount.value = 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// 加载图表数据 |
||||
|
const loadChartData = async () => { |
||||
|
try { |
||||
|
// 同时加载私聊和群聊数据 |
||||
|
const [privateResponse, groupResponse] = await Promise.all([ |
||||
|
getDailyMessageCount(selectedDays.value), |
||||
|
getDailyGroupMessageCount(selectedDays.value) |
||||
|
]); |
||||
|
|
||||
|
const privateData = privateResponse.data; |
||||
|
const groupData = groupResponse.data; |
||||
|
|
||||
|
// 处理数据,确保所有日期都有数据 |
||||
|
const chartData = processChartData(privateData, groupData); |
||||
|
|
||||
|
// 更新图表 |
||||
|
if (chartInstance) { |
||||
|
chartInstance.setOption({ |
||||
|
xAxis: { |
||||
|
data: chartData.dates |
||||
|
}, |
||||
|
series: [ |
||||
|
{ |
||||
|
data: chartData.privateCounts |
||||
|
}, |
||||
|
{ |
||||
|
data: chartData.groupCounts |
||||
} |
} |
||||
hr { |
] |
||||
margin-top: 20px; |
}); |
||||
margin-bottom: 20px; |
|
||||
border: 0; |
|
||||
border-top: 1px solid #eee; |
|
||||
} |
} |
||||
.col-item { |
} catch (error) { |
||||
margin-bottom: 20px; |
console.error('加载图表数据失败:', error); |
||||
} |
} |
||||
|
}; |
||||
|
|
||||
|
// 处理图表数据,填充缺失的日期 |
||||
|
const processChartData = (privateData: any[], groupData: any[]) => { |
||||
|
const dates: string[] = []; |
||||
|
const privateCounts: number[] = []; |
||||
|
const groupCounts: number[] = []; |
||||
|
|
||||
ul { |
// 生成日期范围 |
||||
padding: 0; |
const today = new Date(); |
||||
margin: 0; |
for (let i = selectedDays.value - 1; i >= 0; i--) { |
||||
|
const date = new Date(today); |
||||
|
date.setDate(date.getDate() - i); |
||||
|
const dateStr = date.toISOString().split('T')[0]; |
||||
|
dates.push(dateStr); |
||||
|
|
||||
|
// 查找对应日期的私聊数据 |
||||
|
const privateDayData = privateData.find(item => item.date === dateStr); |
||||
|
privateCounts.push(privateDayData ? parseInt(privateDayData.count) : 0); |
||||
|
|
||||
|
// 查找对应日期的群聊数据 |
||||
|
const groupDayData = groupData.find(item => item.date === dateStr); |
||||
|
groupCounts.push(groupDayData ? parseInt(groupDayData.count) : 0); |
||||
} |
} |
||||
|
|
||||
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; |
return { dates, privateCounts, groupCounts }; |
||||
font-size: 13px; |
}; |
||||
color: #676a6c; |
|
||||
overflow-x: hidden; |
// 加载用户注册统计图表数据 |
||||
|
const loadRegistrationChartData = async () => { |
||||
|
try { |
||||
|
const response = await getDailyRegistrationCount(registrationSelectedDays.value); |
||||
|
const data = response.data; |
||||
|
|
||||
|
// 处理数据,确保所有日期都有数据 |
||||
|
const chartData = processRegistrationChartData(data); |
||||
|
|
||||
|
// 更新图表 |
||||
|
if (registrationChartInstance) { |
||||
|
registrationChartInstance.setOption({ |
||||
|
xAxis: { |
||||
|
data: chartData.dates |
||||
|
}, |
||||
|
series: [{ |
||||
|
data: chartData.counts |
||||
|
}] |
||||
|
}); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('加载用户注册统计图表数据失败:', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 处理用户注册统计图表数据,填充缺失的日期 |
||||
|
const processRegistrationChartData = (data: any[]) => { |
||||
|
const dates: string[] = []; |
||||
|
const counts: number[] = []; |
||||
|
|
||||
|
// 生成日期范围 |
||||
|
const today = new Date(); |
||||
|
for (let i = registrationSelectedDays.value - 1; i >= 0; i--) { |
||||
|
const date = new Date(today); |
||||
|
date.setDate(date.getDate() - i); |
||||
|
const dateStr = date.toISOString().split('T')[0]; |
||||
|
dates.push(dateStr); |
||||
|
|
||||
|
// 查找对应日期的数据 |
||||
|
const dayData = data.find(item => item.date === dateStr); |
||||
|
counts.push(dayData ? parseInt(dayData.count) : 0); |
||||
|
} |
||||
|
|
||||
|
return { dates, counts }; |
||||
|
}; |
||||
|
|
||||
|
// 初始化图表 |
||||
|
const initChart = async () => { |
||||
|
await nextTick(); |
||||
|
if (chartContainer.value) { |
||||
|
chartInstance = echarts.init(chartContainer.value); |
||||
|
|
||||
|
const option = { |
||||
|
title: { |
||||
|
text: '每日消息量趋势对比', |
||||
|
left: 'center' |
||||
|
}, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
formatter: (params: any) => { |
||||
|
let result = `${params[0].name}<br/>`; |
||||
|
params.forEach((param: any) => { |
||||
|
result += `${param.seriesName}: ${param.value}<br/>`; |
||||
|
}); |
||||
|
return result; |
||||
|
} |
||||
|
}, |
||||
|
legend: { |
||||
|
data: ['私聊消息', '群聊消息'], |
||||
|
top: 30 |
||||
|
}, |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
data: [], |
||||
|
axisLabel: { |
||||
|
formatter: (value: string) => { |
||||
|
return value.substring(5); // 只显示月-日 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
name: '消息量' |
||||
|
}, |
||||
|
series: [ |
||||
|
{ |
||||
|
name: '私聊消息', |
||||
|
type: 'line', |
||||
|
data: [], |
||||
|
smooth: true, |
||||
|
itemStyle: { |
||||
|
color: '#409EFF' |
||||
|
}, |
||||
|
areaStyle: { |
||||
|
color: { |
||||
|
type: 'linear', |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
x2: 0, |
||||
|
y2: 1, |
||||
|
colorStops: [{ |
||||
|
offset: 0, color: 'rgba(64, 158, 255, 0.3)' |
||||
|
}, { |
||||
|
offset: 1, color: 'rgba(64, 158, 255, 0.1)' |
||||
|
}] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
name: '群聊消息', |
||||
|
type: 'line', |
||||
|
data: [], |
||||
|
smooth: true, |
||||
|
itemStyle: { |
||||
|
color: '#67C23A' |
||||
|
}, |
||||
|
areaStyle: { |
||||
|
color: { |
||||
|
type: 'linear', |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
x2: 0, |
||||
|
y2: 1, |
||||
|
colorStops: [{ |
||||
|
offset: 0, color: 'rgba(103, 194, 58, 0.3)' |
||||
|
}, { |
||||
|
offset: 1, color: 'rgba(103, 194, 58, 0.1)' |
||||
|
}] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
grid: { |
||||
|
left: '3%', |
||||
|
right: '4%', |
||||
|
bottom: '3%', |
||||
|
top: '15%', |
||||
|
containLabel: true |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
chartInstance.setOption(option); |
||||
|
|
||||
|
// 加载初始数据 |
||||
|
await loadChartData(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
onMounted(() => { |
||||
|
loadTotalUserCount(); |
||||
|
loadTotalGroupCount(); |
||||
|
loadActiveUserStats(); |
||||
|
initChart(); |
||||
|
initRegistrationChart(); |
||||
|
}); |
||||
|
|
||||
|
// 初始化用户注册统计图表 |
||||
|
const initRegistrationChart = async () => { |
||||
|
await nextTick(); |
||||
|
if (registrationChartContainer.value) { |
||||
|
registrationChartInstance = echarts.init(registrationChartContainer.value); |
||||
|
|
||||
|
const option = { |
||||
|
title: { |
||||
|
text: '每日用户注册趋势', |
||||
|
left: 'center' |
||||
|
}, |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
formatter: (params: any) => { |
||||
|
const data = params[0]; |
||||
|
return `${data.name}<br/>注册用户: ${data.value}`; |
||||
|
} |
||||
|
}, |
||||
|
xAxis: { |
||||
|
type: 'category', |
||||
|
data: [], |
||||
|
axisLabel: { |
||||
|
formatter: (value: string) => { |
||||
|
return value.substring(5); // 只显示月-日 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
yAxis: { |
||||
|
type: 'value', |
||||
|
name: '注册用户数' |
||||
|
}, |
||||
|
series: [{ |
||||
|
name: '注册用户', |
||||
|
type: 'line', |
||||
|
data: [], |
||||
|
smooth: true, |
||||
|
itemStyle: { |
||||
|
color: '#E6A23C' |
||||
|
}, |
||||
|
areaStyle: { |
||||
|
color: { |
||||
|
type: 'linear', |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
x2: 0, |
||||
|
y2: 1, |
||||
|
colorStops: [{ |
||||
|
offset: 0, color: 'rgba(230, 162, 60, 0.3)' |
||||
|
}, { |
||||
|
offset: 1, color: 'rgba(230, 162, 60, 0.1)' |
||||
|
}] |
||||
|
} |
||||
|
} |
||||
|
}], |
||||
|
grid: { |
||||
|
left: '3%', |
||||
|
right: '4%', |
||||
|
bottom: '3%', |
||||
|
containLabel: true |
||||
|
} |
||||
|
}; |
||||
|
|
||||
ul { |
registrationChartInstance.setOption(option); |
||||
list-style-type: none; |
|
||||
|
// 加载初始数据 |
||||
|
await loadRegistrationChartData(); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.app-container { |
||||
|
padding: 20px; |
||||
} |
} |
||||
|
|
||||
h4 { |
// 统计卡片样式 |
||||
margin-top: 0px; |
.statistics-card { |
||||
|
border-radius: 8px; |
||||
|
border: 1px solid #f0f2f5; |
||||
|
|
||||
|
:deep(.el-card__body) { |
||||
|
padding: 16px; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
h2 { |
.statistics-item { |
||||
margin-top: 10px; |
display: flex; |
||||
font-size: 26px; |
align-items: center; |
||||
font-weight: 100; |
|
||||
|
.statistics-icon { |
||||
|
width: 48px; |
||||
|
height: 48px; |
||||
|
border-radius: 8px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
margin-right: 12px; |
||||
|
color: white; |
||||
} |
} |
||||
|
|
||||
p { |
.statistics-content { |
||||
margin-top: 10px; |
flex: 1; |
||||
|
|
||||
b { |
.statistics-title { |
||||
font-weight: 700; |
font-size: 13px; |
||||
|
color: #8c8c8c; |
||||
|
margin-bottom: 4px; |
||||
|
} |
||||
|
|
||||
|
.statistics-value { |
||||
|
font-size: 24px; |
||||
|
font-weight: 600; |
||||
|
color: #262626; |
||||
|
line-height: 1; |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
.update-log { |
// 图表区域 |
||||
ol { |
.chart-section { |
||||
display: block; |
margin-top: 24px; |
||||
list-style-type: decimal; |
|
||||
margin-block-start: 1em; |
.el-card { |
||||
margin-block-end: 1em; |
border-radius: 8px; |
||||
margin-inline-start: 0; |
border: 1px solid #f0f2f5; |
||||
margin-inline-end: 0; |
|
||||
padding-inline-start: 40px; |
:deep(.el-card__body) { |
||||
|
padding: 16px; |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
// 卡片头部样式优化 |
||||
|
:deep(.el-card__header) { |
||||
|
border-bottom: 1px solid #f0f2f5; |
||||
|
padding: 12px 16px; |
||||
|
|
||||
|
span { |
||||
|
font-size: 14px; |
||||
|
font-weight: 500; |
||||
|
color: #262626; |
||||
|
} |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||