smart-car-web/src/views/device/data-recognition/index.vue

407 lines
13 KiB
Vue
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.

<template>
<!-- 设备管理-数据识别 -->
<el-card class="data-recognition-container">
<!-- 顶部过滤器 -->
<el-card v-show="showSearch" class="search-card">
<el-form :inline="true" ref="queryFormRef" :model="queryParams" label-width="auto">
<el-form-item>
<el-date-picker v-model="timeRange" type="datetimerange" range-separator=" ~ "
start-placeholder="开始时间" end-placeholder="结束时间" value-format="yyyy-MM-dd HH:mm"
format="yyyy-MM-dd HH:mm" style="width: 400px" @change="handleTimeRangeChange" />
</el-form-item>
<el-form-item>
<el-button class="query-btn" @click="handleQuery">查询</el-button>
<el-button class="reset-btn" @click="handleReset">重置</el-button>
<el-button class="export-btn" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 车辆数据统计 -->
<el-card class="stats-card">
<h3 class="stats-title">车辆数据</h3>
<div class="stats-grid">
<div class="stat-card" v-for="(stat, index) in statistics" :key="index">
<div class="stat-value">{{ stat.value }}</div>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
</el-card>
<!-- 数据列表 -->
<div class="table-container">
<TableModel :formLabel="[]" :showOperation="false" :showRightTools="false" ref="dataRecognitionTableRef"
:columnsList="columnsList" :request-api="dataRecognitionListAPI" :handleColWidth="250"
:showSearch="false">
<template slot="tableTitle">
<h3>数据列表</h3>
</template>
<template slot="carType" slot-scope="{ data }">
<el-tag v-if="data.carType === '1'" type="success">油车</el-tag>
<el-tag v-else-if="data.carType === '2'" type="warning">新能源</el-tag>
<el-tag v-else type="info">其他</el-tag>
</template>
<template slot="recognitionPhoto" slot-scope="{ data }">
<el-image :src="data.recognitionPhoto || test_image" alt="识别照片" class="recognition-photo"
fit="cover" :preview-src-list="previewImageList">
<div slot="error" class="image-error">
<i class="el-icon-picture"></i>
</div>
</el-image>
</template>
</TableModel>
</div>
</el-card>
</template>
<script>
import TableModel from '@/components/TableModel2'
import { columnsList } from './config'
import { dataRecognitionListAPI, dataRecognitionDetailAPI, exportDataRecognitionAPI } from '@/api/device/data-recognition'
import test_image from '@/assets/images/test_image.png'
export default {
name: 'DataRecognition',
components: {
TableModel,
},
data() {
return {
showSearch: true,
columnsList,
dataRecognitionListAPI,
test_image,
timeRange: null,
statistics: [
{ label: '车辆总数(辆)', value: '0' },
{ label: '油车数量(辆)', value: '0' },
{ label: '新能源数量(辆)', value: '0' },
{ label: '总车流量(PCU/h)', value: '0' },
{ label: '油车车流量(PCU/h)', value: '0' },
{ label: '新能源车流量(PCU/h)', value: '0' },
],
queryParams: {
pageNum: 1,
pageSize: 10,
startTime: '',
endTime: '',
},
}
},
computed: {
previewImageList() {
// 从表格数据中获取所有图片URL
if (this.$refs.dataRecognitionTableRef && this.$refs.dataRecognitionTableRef.tableList) {
return this.$refs.dataRecognitionTableRef.tableList
.map(item => item.recognitionPhoto || this.test_image)
.filter(url => url)
}
return []
},
},
created() {
// 初始化默认时间为当天的 0点0分 到 23点59分
this.initDefaultTimeRange()
// 获取统计数据
this.getStatistics()
},
methods: {
/** 初始化默认时间范围 */
initDefaultTimeRange() {
const now = new Date()
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0)
const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59)
const formatTime = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
}
const defaultTimeRange = [formatTime(todayStart), formatTime(todayEnd)]
this.timeRange = defaultTimeRange
this.queryParams.startTime = defaultTimeRange[0]
this.queryParams.endTime = defaultTimeRange[1]
},
/** 获取统计数据 */
async getStatistics() {
try {
const params = {
startTime: this.queryParams.startTime,
endTime: this.queryParams.endTime,
}
const res = await dataRecognitionDetailAPI(params)
if (res.code === 200 && res.data) {
// 将返回的数据映射到 statistics 数组
const data = res.data
this.statistics = [
{
label: '车辆总数(辆)',
value: data.totalCarNum || '0'
},
{
label: '油车数量(辆)',
value: data.petrolVehiclesNum || '0'
},
{
label: '新能源数量(辆)',
value: data.newEnergyNum || '0'
},
{
label: '总车流量(PCU/h)',
value: data.totalPCU || '0'
},
{
label: '油车车流量(PCU/h)',
value: data.petrolVehiclesPCU || '0'
},
{
label: '新能源车流量(PCU/h)',
value: data.newEnergyPCU || '0'
},
]
}
} catch (error) {
console.error('获取统计数据失败:', error)
// 如果接口调用失败,保持默认值
}
},
/** 查询操作 */
handleQuery() {
// 更新统计数据
this.getStatistics()
if (this.$refs.dataRecognitionTableRef) {
// 合并查询参数
Object.assign(this.$refs.dataRecognitionTableRef.queryParams, this.queryParams)
this.$refs.dataRecognitionTableRef.queryParams.pageNum = 1
this.$refs.dataRecognitionTableRef.getTableList()
}
},
/** 重置操作 */
handleReset() {
// 重置时恢复默认时间范围(当天的 0点0分 到 23点59分
this.initDefaultTimeRange()
// 重置后重新获取统计数据
this.getStatistics()
if (this.$refs.dataRecognitionTableRef) {
Object.assign(this.$refs.dataRecognitionTableRef.queryParams, this.queryParams)
this.$refs.dataRecognitionTableRef.queryParams.pageNum = 1
this.$refs.dataRecognitionTableRef.getTableList()
}
},
/** 导出操作 */
async handleExport() {
try {
const params = { ...this.queryParams }
const res = await exportDataRecognitionAPI(params)
if (res.code === 200) {
// 处理文件下载
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `数据识别数据_${new Date().getTime()}.xlsx`
link.click()
window.URL.revokeObjectURL(url)
this.$message.success('导出成功')
} else {
this.$message.error(res.msg || '导出失败')
}
} catch (error) {
console.error('导出失败:', error)
this.$message.error('导出失败')
}
},
/** 时间范围变化 */
handleTimeRangeChange(value) {
if (value && value.length === 2) {
this.queryParams.startTime = value[0]
this.queryParams.endTime = value[1]
} else {
this.queryParams.startTime = ''
this.queryParams.endTime = ''
}
},
},
}
</script>
<style scoped lang="scss">
.data-recognition-container {
height: calc(100vh - 84px);
overflow: hidden;
background: linear-gradient(180deg, #f1f6ff 20%, #e5efff 100%);
}
.search-card {
margin-bottom: 10px;
::v-deep .el-form-item {
margin-bottom: 10px;
}
.query-btn {
width: 98px;
height: 36px;
background: #1f72ea;
box-shadow: 0px 4px 8px 0px rgba(51, 135, 255, 0.5);
border-radius: 4px;
color: #fff;
border: none;
font-size: 14px;
transition: all 0.3s;
&:hover {
background: #4a8bff;
box-shadow: 0px 6px 12px 0px rgba(51, 135, 255, 0.6);
}
}
.reset-btn {
width: 98px;
height: 36px;
background: #ffffff;
box-shadow: 0px 4px 8px 0px rgba(76, 76, 76, 0.2);
border-radius: 4px;
color: #333;
border: none;
font-size: 14px;
transition: all 0.3s;
&:hover {
background: #f5f5f5;
box-shadow: 0px 6px 12px 0px rgba(76, 76, 76, 0.3);
color: #40a9ff;
}
}
.export-btn {
width: 98px;
height: 36px;
background: #1f72ea;
box-shadow: 0px 4px 8px 0px rgba(51, 135, 255, 0.5);
border-radius: 4px;
color: #fff;
border: none;
font-size: 14px;
transition: all 0.3s;
&:hover {
background: #4a8bff;
box-shadow: 0px 6px 12px 0px rgba(51, 135, 255, 0.6);
}
}
}
.stats-card {
margin-bottom: 10px;
.stats-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0 0 16px 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 16px;
.stat-card {
background: linear-gradient(135deg, #1f72ea 0%, #4a8bff 100%);
border-radius: 8px;
padding: 24px;
color: #fff;
text-align: center;
box-shadow: 0px 4px 12px 0px rgba(31, 114, 234, 0.3);
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0px 6px 16px 0px rgba(31, 114, 234, 0.4);
}
.stat-value {
font-size: 32px;
font-weight: 700;
margin-bottom: 8px;
line-height: 1.2;
}
.stat-label {
font-size: 14px;
opacity: 0.9;
line-height: 1.4;
}
}
}
}
.table-container {
height: calc(100vh - 420px);
overflow: hidden;
}
::v-deep .table-card {
height: calc(100vh - 420px) !important;
display: flex;
flex-direction: column;
overflow: hidden;
.el-card__body {
min-height: 0;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 20px;
}
.table-container {
flex: 1;
min-height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.pagination-wrapper {
flex-shrink: 0;
margin-top: 16px;
padding-top: 16px;
}
}
.recognition-photo {
width: 80px;
height: 60px;
border-radius: 4px;
cursor: pointer;
}
.image-error {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 60px;
background: #f5f5f5;
border-radius: 4px;
color: #999;
i {
font-size: 24px;
}
}
</style>