parent
52f8ea1470
commit
0a2fac6cdb
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"*.vue": "vue",
|
||||||
|
"*.js": "javascript",
|
||||||
|
"*.json": "json",
|
||||||
|
"*.css": "css",
|
||||||
|
"*.scss": "scss",
|
||||||
|
"*.less": "less"
|
||||||
|
},
|
||||||
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
"editor.semanticHighlighting.enabled": true,
|
||||||
|
"editor.bracketPairColorization.enabled": true,
|
||||||
|
"editor.guides.bracketPairs": true
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"endOfLine": "auto"
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const mockOverviewData = {
|
||||||
|
month: {
|
||||||
|
total: 42,
|
||||||
|
onlineRate: 90.9,
|
||||||
|
communicationCount: 50,
|
||||||
|
avgCommunicationTime: 2.0
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
total: 45,
|
||||||
|
onlineRate: 92.5,
|
||||||
|
communicationCount: 580,
|
||||||
|
avgCommunicationTime: 2.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockTrendData = {
|
||||||
|
month: {
|
||||||
|
legends: ['大连', '小连'],
|
||||||
|
xAxis: ['1日', '2日', '3日', '4日', '5日', '6日', '7日', '8日', '9日', '10日', '11日', '12日', '13日', '14日', '15日', '16日', '17日', '18日', '19日', '20日', '21日', '22日', '23日', '24日', '25日', '26日', '27日', '28日', '29日', '30日', '31日'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '大连',
|
||||||
|
data: [60, 58, 65, 70, 75, 85, 80, 70, 60, 90, 85, 80, 95, 85, 65, 70, 75, 85, 80, 70, 60, 90, 85, 80, 95, 85, 65, 70, 75, 85, 80]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '小连',
|
||||||
|
data: [65, 70, 80, 75, 60, 55, 90, 85, 95, 85, 75, 60, 55, 65, 80, 75, 60, 55, 90, 85, 95, 85, 75, 60, 55, 65, 80, 75, 60, 55, 90]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
legends: ['大连', '小连'],
|
||||||
|
xAxis: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '大连',
|
||||||
|
data: [85, 88, 90, 92, 88, 85, 90, 92, 95, 90, 88, 85]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '小连',
|
||||||
|
data: [80, 85, 88, 90, 85, 80, 88, 90, 92, 88, 85, 80]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备统计概览数据
|
||||||
|
export function getDeviceOverview(params) {
|
||||||
|
return {
|
||||||
|
data: mockOverviewData[params.type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备在线率趋势数据
|
||||||
|
export function getDeviceOnlineTrend(params) {
|
||||||
|
return {
|
||||||
|
data: mockTrendData[params.type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取行驶概览数据
|
||||||
|
export function getDrivingOverview(params) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve({
|
||||||
|
data: {
|
||||||
|
totalMileage: 12345.67,
|
||||||
|
avgMileage: 1234.56,
|
||||||
|
maxMileage: 2345.67,
|
||||||
|
minMileage: 123.45
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取行驶趋势数据
|
||||||
|
export function getDrivingTrend(params) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const isMonth = params.type === 'month'
|
||||||
|
const xAxis = isMonth
|
||||||
|
? Array.from({length: 31}, (_, i) => `${i + 1}日`)
|
||||||
|
: Array.from({length: 12}, (_, i) => `${i + 1}月`)
|
||||||
|
|
||||||
|
const series = Array.from({length: xAxis.length}, () =>
|
||||||
|
Math.floor(Math.random() * 2000) + 500
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
data: {
|
||||||
|
xAxis,
|
||||||
|
series
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取行驶排名数据
|
||||||
|
export function getDrivingRanking(params) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const data = Array.from({length: params.limit}, (_, i) => ({
|
||||||
|
deviceName: `设备${i + 1}`,
|
||||||
|
mileage: Math.floor(Math.random() * 2000) + 500
|
||||||
|
})).sort((a, b) => b.mileage - a.mileage)
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 获取警告列表
|
||||||
|
export function getWarningList(params) {
|
||||||
|
// 模拟数据
|
||||||
|
const mockData = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
device_no: 'PT001',
|
||||||
|
device_name: '泵车A',
|
||||||
|
warning_type: '低电量',
|
||||||
|
warning_time: '2024-01-20 14:30:50',
|
||||||
|
location: '上海市浦东新区世纪大道',
|
||||||
|
status: '未处理'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
device_no: 'PT002',
|
||||||
|
device_name: '泵车B',
|
||||||
|
warning_type: '低电量预警',
|
||||||
|
warning_time: '2024-01-20 14:30:50',
|
||||||
|
location: '上海市浦东新区世纪大道',
|
||||||
|
status: '已处理'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
device_no: 'PT003',
|
||||||
|
device_name: '泵车C',
|
||||||
|
warning_type: '低电量超低预警',
|
||||||
|
warning_time: '2024-01-20 14:30:50',
|
||||||
|
location: '上海市浦东新区世纪大道',
|
||||||
|
status: '处理中'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
total: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟 API 调用
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(mockData);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索警告
|
||||||
|
export function searchWarnings(params) {
|
||||||
|
// 模拟数据
|
||||||
|
const mockData = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
device_no: 'PT001',
|
||||||
|
device_name: '泵车A',
|
||||||
|
warning_type: '低电量',
|
||||||
|
warning_time: '2024-01-20 14:30:50',
|
||||||
|
location: '上海市浦东新区世纪大道',
|
||||||
|
status: '未处理'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
total: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟 API 调用
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(mockData);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,276 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="statistics-page">
|
||||||
|
<div class="filter-header">
|
||||||
|
<el-form :inline="true" class="filter-form">
|
||||||
|
<el-form-item label="统计类型">
|
||||||
|
<el-radio-group v-model="filterForm.type" @change="handleTypeChange">
|
||||||
|
<el-radio label="month">按月统计</el-radio>
|
||||||
|
<el-radio label="year">按年统计</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="filterForm.type === 'month' ? '选择月份' : '选择年份'">
|
||||||
|
<el-date-picker
|
||||||
|
v-if="filterForm.type === 'month'"
|
||||||
|
v-model="filterForm.date"
|
||||||
|
type="month"
|
||||||
|
placeholder="选择年月"
|
||||||
|
value-format="yyyy-MM">
|
||||||
|
</el-date-picker>
|
||||||
|
<el-date-picker
|
||||||
|
v-else
|
||||||
|
v-model="filterForm.date"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择年份"
|
||||||
|
value-format="yyyy">
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">统计分析</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="statistics-cards">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card purple">
|
||||||
|
<div class="card-title">设备总数</div>
|
||||||
|
<div class="card-value">{{ overviewData.total }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card blue">
|
||||||
|
<div class="card-title">平均在线率</div>
|
||||||
|
<div class="card-value">{{ overviewData.onlineRate }}<span class="unit">%</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card orange">
|
||||||
|
<div class="card-title">通联次数</div>
|
||||||
|
<div class="card-value">{{ overviewData.communicationCount }}</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card light-blue">
|
||||||
|
<div class="card-title">平均通联时间</div>
|
||||||
|
<div class="card-value">{{ overviewData.avgCommunicationTime }}<span class="unit">h</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="trend-chart">
|
||||||
|
<el-card>
|
||||||
|
<div slot="header" class="chart-header">
|
||||||
|
<span>设备在线率趋势</span>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container" ref="trendChart"></div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import { getDeviceOverview, getDeviceOnlineTrend } from '@/api/car/statistics'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const now = new Date()
|
||||||
|
const defaultDate = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0')
|
||||||
|
return {
|
||||||
|
filterForm: {
|
||||||
|
type: 'month',
|
||||||
|
date: defaultDate
|
||||||
|
},
|
||||||
|
chart: null,
|
||||||
|
overviewData: {
|
||||||
|
total: 0,
|
||||||
|
onlineRate: 0,
|
||||||
|
communicationCount: 0,
|
||||||
|
avgCommunicationTime: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initChart()
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDefaultDate() {
|
||||||
|
const now = new Date()
|
||||||
|
if (this.filterForm?.type === 'month') {
|
||||||
|
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
||||||
|
} else {
|
||||||
|
return `${now.getFullYear()}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadData() {
|
||||||
|
try {
|
||||||
|
// 获取概览数据
|
||||||
|
const overviewRes = getDeviceOverview({
|
||||||
|
type: this.filterForm.type,
|
||||||
|
date: this.filterForm.date
|
||||||
|
})
|
||||||
|
this.overviewData = overviewRes.data
|
||||||
|
|
||||||
|
// 获取趋势数据
|
||||||
|
const trendRes = getDeviceOnlineTrend({
|
||||||
|
type: this.filterForm.type,
|
||||||
|
date: this.filterForm.date
|
||||||
|
})
|
||||||
|
this.updateChart(trendRes.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error)
|
||||||
|
this.$message.error('加载数据失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
initChart() {
|
||||||
|
this.chart = echarts.init(this.$refs.trendChart)
|
||||||
|
},
|
||||||
|
updateChart(data) {
|
||||||
|
if (!this.chart) {
|
||||||
|
this.chart = echarts.init(this.$refs.trendChart)
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: data.legends || []
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: data.xAxis || [],
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0,
|
||||||
|
rotate: 30
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}%'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: data.series.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: item.data,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||||
|
offset: 0,
|
||||||
|
color: item.name === '大连' ? 'rgba(58,77,233,0.8)' : 'rgba(73,227,172,0.8)'
|
||||||
|
}, {
|
||||||
|
offset: 1,
|
||||||
|
color: item.name === '大连' ? 'rgba(58,77,233,0.1)' : 'rgba(73,227,172,0.1)'
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chart.setOption(option)
|
||||||
|
},
|
||||||
|
handleTypeChange() {
|
||||||
|
this.filterForm.date = this.getDefaultDate()
|
||||||
|
this.loadData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.dispose()
|
||||||
|
this.chart = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.statistics-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-cards {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.purple {
|
||||||
|
background: linear-gradient(135deg, #a355f7 0%, #9061f9 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.blue {
|
||||||
|
background: linear-gradient(135deg, #3a4de9 0%, #3f8cfe 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.orange {
|
||||||
|
background: linear-gradient(135deg, #ff9f43 0%, #ff7f50 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.light-blue {
|
||||||
|
background: linear-gradient(135deg, #17c2d7 0%, #3bbaf5 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-chart {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
padding: 10px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,308 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="statistics-page">
|
||||||
|
<div class="filter-header">
|
||||||
|
<el-form :inline="true" class="filter-form">
|
||||||
|
<el-form-item label="统计类型">
|
||||||
|
<el-radio-group v-model="filterForm.type" @change="handleTypeChange">
|
||||||
|
<el-radio label="month">按月统计</el-radio>
|
||||||
|
<el-radio label="year">按年统计</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="filterForm.type === 'month' ? '选择月份' : '选择年份'">
|
||||||
|
<el-date-picker
|
||||||
|
v-if="filterForm.type === 'month'"
|
||||||
|
v-model="filterForm.date"
|
||||||
|
type="month"
|
||||||
|
placeholder="选择月份"
|
||||||
|
value-format="yyyy-MM">
|
||||||
|
</el-date-picker>
|
||||||
|
<el-date-picker
|
||||||
|
v-else
|
||||||
|
v-model="filterForm.date"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择年份"
|
||||||
|
value-format="yyyy">
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">统计分析</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="statistics-cards">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card purple">
|
||||||
|
<div class="card-title">总里程</div>
|
||||||
|
<div class="card-value">{{ overviewData.totalMileage }}<span class="unit">km</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card blue">
|
||||||
|
<div class="card-title">平均里程</div>
|
||||||
|
<div class="card-value">{{ overviewData.avgMileage }}<span class="unit">km</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card orange">
|
||||||
|
<div class="card-title">最高里程</div>
|
||||||
|
<div class="card-value">{{ overviewData.maxMileage }}<span class="unit">km</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-card class="stat-card light-blue">
|
||||||
|
<div class="card-title">最低里程</div>
|
||||||
|
<div class="card-value">{{ overviewData.minMileage }}<span class="unit">km</span></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="16">
|
||||||
|
<el-card class="trend-chart">
|
||||||
|
<div slot="header" class="chart-header">
|
||||||
|
<span>{{ filterForm.type === 'month' ? '日里程趋势' : '月里程趋势' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container" ref="trendChart"></div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card class="ranking-list">
|
||||||
|
<div slot="header" class="chart-header">
|
||||||
|
<span>车辆里程排名</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="rankingData" style="width: 100%" height="400">
|
||||||
|
<el-table-column type="index" label="排名" width="60"></el-table-column>
|
||||||
|
<el-table-column prop="deviceName" label="设备名称"></el-table-column>
|
||||||
|
<el-table-column prop="mileage" label="里程(km)" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.mileage.toFixed(2) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import { getDrivingOverview, getDrivingTrend, getDrivingRanking } from '@/api/car/statistics'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const now = new Date()
|
||||||
|
const defaultDate = now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0')
|
||||||
|
return {
|
||||||
|
filterForm: {
|
||||||
|
type: 'month',
|
||||||
|
date: defaultDate
|
||||||
|
},
|
||||||
|
chart: null,
|
||||||
|
overviewData: {
|
||||||
|
totalMileage: 0,
|
||||||
|
avgMileage: 0,
|
||||||
|
maxMileage: 0,
|
||||||
|
minMileage: 0
|
||||||
|
},
|
||||||
|
rankingData: [],
|
||||||
|
chartData: {
|
||||||
|
xAxis: [],
|
||||||
|
series: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDefaultDate() {
|
||||||
|
const now = new Date()
|
||||||
|
if (this.filterForm?.type === 'month') {
|
||||||
|
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
||||||
|
} else {
|
||||||
|
return `${now.getFullYear()}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadData() {
|
||||||
|
try {
|
||||||
|
// 获取概览数据
|
||||||
|
const overviewRes = await getDrivingOverview({
|
||||||
|
type: this.filterForm.type,
|
||||||
|
date: this.filterForm.date
|
||||||
|
})
|
||||||
|
this.overviewData = overviewRes.data
|
||||||
|
|
||||||
|
// 获取趋势数据
|
||||||
|
const trendRes = await getDrivingTrend({
|
||||||
|
type: this.filterForm.type,
|
||||||
|
date: this.filterForm.date
|
||||||
|
})
|
||||||
|
this.chartData = trendRes.data
|
||||||
|
this.updateChart()
|
||||||
|
|
||||||
|
// 获取排名数据
|
||||||
|
const rankingRes = await getDrivingRanking({
|
||||||
|
type: this.filterForm.type,
|
||||||
|
date: this.filterForm.date,
|
||||||
|
limit: 10
|
||||||
|
})
|
||||||
|
this.rankingData = rankingRes.data
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error)
|
||||||
|
this.$message.error('加载数据失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
updateChart() {
|
||||||
|
if (!this.chart) {
|
||||||
|
this.chart = echarts.init(this.$refs.trendChart)
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: '{b}<br/>{a}: {c} km'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['里程']
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: this.chartData.xAxis,
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0,
|
||||||
|
rotate: 30
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value} km'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: '里程',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
data: this.chartData.series,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.3,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(58,77,233,0.8)'
|
||||||
|
}, {
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(58,77,233,0.1)'
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chart.setOption(option)
|
||||||
|
},
|
||||||
|
handleTypeChange() {
|
||||||
|
this.filterForm.date = this.getDefaultDate()
|
||||||
|
this.loadData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.dispose()
|
||||||
|
this.chart = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.statistics-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics-cards {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.purple {
|
||||||
|
background: linear-gradient(135deg, #a355f7 0%, #9061f9 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.blue {
|
||||||
|
background: linear-gradient(135deg, #3a4de9 0%, #3f8cfe 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.orange {
|
||||||
|
background: linear-gradient(135deg, #ff9f43 0%, #ff7f50 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.light-blue {
|
||||||
|
background: linear-gradient(135deg, #17c2d7 0%, #3bbaf5 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-chart, .ranking-list {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
padding: 10px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,24 +1,207 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div style="padding: 0px 20px">
|
<div class="statistics-page">
|
||||||
<el-image
|
<div class="filter-header">
|
||||||
style="width: 100%"
|
<el-form :inline="true" class="filter-form">
|
||||||
:src="require('/src/assets/chart.png')"
|
<el-form-item label="统计类型">
|
||||||
fit="fill"></el-image>
|
<el-radio-group v-model="filterForm.statType" @change="handleStatTypeChange">
|
||||||
<!-- <div ref="lxHeader">
|
<el-radio label="month">按月统计</el-radio>
|
||||||
<lx-header icon="md-apps" text="数据统计" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
|
<el-radio label="year">按年统计</el-radio>
|
||||||
<div slot="content"></div>
|
</el-radio-group>
|
||||||
<slot>
|
</el-form-item>
|
||||||
|
<el-form-item label="选择时间">
|
||||||
</slot>
|
<el-date-picker
|
||||||
</lx-header>
|
v-if="filterForm.statType === 'month'"
|
||||||
</div> -->
|
v-model="filterForm.date"
|
||||||
|
type="month"
|
||||||
|
placeholder="选择年月"
|
||||||
|
value-format="yyyy-MM"
|
||||||
|
@change="handleDateChange">
|
||||||
|
</el-date-picker>
|
||||||
|
<el-date-picker
|
||||||
|
v-else
|
||||||
|
v-model="filterForm.date"
|
||||||
|
type="year"
|
||||||
|
placeholder="选择年份"
|
||||||
|
value-format="yyyy"
|
||||||
|
@change="handleDateChange">
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="resetForm">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-container">
|
||||||
|
<div class="chart-item">
|
||||||
|
<div class="chart-title">设备使用时长统计</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<div ref="usageChart" style="width: 100%; height: 400px;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getDeviceStatistics } from '@/api/car/statistics'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filterForm: {
|
||||||
|
statType: 'month', // 默认按月统计
|
||||||
|
date: this.getDefaultDate()
|
||||||
|
},
|
||||||
|
usageChart: null,
|
||||||
|
chartData: {
|
||||||
|
xAxis: [],
|
||||||
|
series: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDefaultDate() {
|
||||||
|
const now = new Date()
|
||||||
|
if (this.filterForm?.statType === 'month') {
|
||||||
|
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
||||||
|
} else {
|
||||||
|
return `${now.getFullYear()}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleStatTypeChange() {
|
||||||
|
// 重置日期为当前时间
|
||||||
|
this.filterForm.date = this.getDefaultDate()
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
handleDateChange() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
async loadData() {
|
||||||
|
try {
|
||||||
|
const response = await getDeviceStatistics({
|
||||||
|
statType: this.filterForm.statType,
|
||||||
|
date: this.filterForm.date
|
||||||
|
})
|
||||||
|
this.chartData = response.data
|
||||||
|
this.updateChart()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error)
|
||||||
|
this.$message.error('加载数据失败')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateChart() {
|
||||||
|
if (!this.usageChart) {
|
||||||
|
this.usageChart = echarts.init(this.$refs.usageChart)
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['使用时长']
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: this.chartData.xAxis,
|
||||||
|
axisLabel: {
|
||||||
|
interval: 0,
|
||||||
|
rotate: 30
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '小时'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '使用时长',
|
||||||
|
type: 'bar',
|
||||||
|
data: this.chartData.series,
|
||||||
|
itemStyle: {
|
||||||
|
color: '#409EFF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.usageChart.setOption(option)
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.filterForm = {
|
||||||
|
statType: 'month',
|
||||||
|
date: this.getDefaultDate()
|
||||||
|
}
|
||||||
|
this.loadData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadData()
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (this.usageChart) {
|
||||||
|
this.usageChart.resize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.usageChart) {
|
||||||
|
this.usageChart.dispose()
|
||||||
|
}
|
||||||
|
window.removeEventListener('resize', () => {
|
||||||
|
if (this.usageChart) {
|
||||||
|
this.usageChart.resize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
</style>
|
.statistics-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-content {
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,24 +1,167 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div style="padding: 0px 20px">
|
<div class="warning-page">
|
||||||
<el-image
|
<div class="filter-header">
|
||||||
style="width: 100%"
|
<el-form :inline="true" class="filter-form">
|
||||||
:src="require('/src/assets/warning.png')"
|
<el-form-item label="设备">
|
||||||
fit="fill"></el-image>
|
<el-input v-model="filterForm.keyword" placeholder="请输入设备号或设备名称"></el-input>
|
||||||
<!-- <div ref="lxHeader">
|
</el-form-item>
|
||||||
<lx-header icon="md-apps" text="系统预警" style="margin-bottom: 10px; border: 0px; margin-top: 15px">
|
<el-form-item>
|
||||||
<div slot="content"></div>
|
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||||
<slot>
|
<el-button @click="resetForm">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</slot>
|
<el-table :data="tableData" style="width: 100%" border>
|
||||||
</lx-header>
|
<el-table-column prop="device_no" label="泵车编号" width="120"></el-table-column>
|
||||||
</div> -->
|
<el-table-column prop="device_name" label="泵车名称" width="120"></el-table-column>
|
||||||
|
<el-table-column label="预警类型" width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="getWarningTypeStyle(scope.row.warning_type)">
|
||||||
|
{{ scope.row.warning_type }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="warning_time" label="预警时间" width="180"></el-table-column>
|
||||||
|
<el-table-column prop="location" label="位置信息"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="getStatusStyle(scope.row.status)">
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-sizes="[10, 20, 30, 50]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="total">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getWarningList, searchWarnings } from '@/api/car/warning'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
filterForm: {
|
||||||
|
keyword: ''
|
||||||
|
},
|
||||||
|
tableData: [],
|
||||||
|
loading: false,
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const response = await getWarningList({
|
||||||
|
page: this.currentPage,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
});
|
||||||
|
this.tableData = response.data;
|
||||||
|
this.total = response.total;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error);
|
||||||
|
this.$message.error('加载数据失败');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleSearch() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
if (this.filterForm.keyword) {
|
||||||
|
const response = await searchWarnings({
|
||||||
|
keyword: this.filterForm.keyword,
|
||||||
|
page: this.currentPage,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
});
|
||||||
|
this.tableData = response.data;
|
||||||
|
this.total = response.total;
|
||||||
|
} else {
|
||||||
|
await this.loadData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索失败:', error);
|
||||||
|
this.$message.error('搜索失败');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetForm() {
|
||||||
|
this.filterForm = {
|
||||||
|
keyword: ''
|
||||||
|
};
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val;
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.currentPage = val;
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
getWarningTypeStyle(type) {
|
||||||
|
switch (type) {
|
||||||
|
case '低电量':
|
||||||
|
return 'warning' // 橙色
|
||||||
|
case '低电量预警':
|
||||||
|
return 'primary' // 蓝色
|
||||||
|
case '低电量超低预警':
|
||||||
|
return 'danger' // 紫红色
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStatusStyle(status) {
|
||||||
|
switch (status) {
|
||||||
|
case '未处理':
|
||||||
|
return 'danger'
|
||||||
|
case '处理中':
|
||||||
|
return 'warning'
|
||||||
|
case '已处理':
|
||||||
|
return 'success'
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.warning-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in new issue