<template>
|
<div class="app-container">
|
<el-form :model="filters" :inline="true">
|
<el-form-item label="车牌号码">
|
<el-input
|
v-model="filters.plateNumber"
|
style="width: 240px"
|
placeholder="请输入车牌号码"
|
clearable
|
:prefix-icon="Search"
|
@change="getTableData"
|
/>
|
</el-form-item>
|
<el-form-item label="车辆类型">
|
<el-select
|
v-model="filters.vehicleType"
|
style="width: 240px"
|
placeholder="请选择车辆类型"
|
clearable
|
@change="getTableData"
|
>
|
<el-option label="小型车" value="0"></el-option>
|
<el-option label="中型车" value="1"></el-option>
|
<el-option label="大型车" value="2"></el-option>
|
<el-option label="特种车" value="3"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="进出方向">
|
<el-select
|
v-model="filters.direction"
|
style="width: 240px"
|
placeholder="请选择进出方向"
|
clearable
|
@change="getTableData"
|
>
|
<el-option label="进" value="0"></el-option>
|
<el-option label="出" value="1"></el-option>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="进出时间">
|
<el-date-picker
|
v-model="filters.timeRange"
|
type="daterange"
|
start-placeholder="开始时间"
|
end-placeholder="结束时间"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm"
|
clearable
|
@change="changeTimeRange"
|
/>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="getTableData">搜索</el-button>
|
<el-button @click="resetFilters">重置</el-button>
|
</el-form-item>
|
</el-form>
|
|
<div class="vehicle-stats">
|
<div class="stat-card">
|
<div class="stat-title">今日进车数</div>
|
<div class="stat-value in">{{ todayInCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">今日出车数</div>
|
<div class="stat-value out">{{ todayOutCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">当前在场车辆</div>
|
<div class="stat-value present">{{ presentCount }}</div>
|
</div>
|
<div class="stat-card">
|
<div class="stat-title">今日总流量</div>
|
<div class="stat-value total">{{ todayTotalCount }}</div>
|
</div>
|
</div>
|
|
<div class="table_list">
|
<PIMTable
|
rowKey="id"
|
:column="columns"
|
:tableData="dataList"
|
:page="{
|
current: pagination.currentPage,
|
size: pagination.pageSize,
|
total: pagination.total,
|
}"
|
@pagination="changePage"
|
>
|
</PIMTable>
|
</div>
|
|
<!-- 车辆详情弹窗 -->
|
<el-dialog
|
v-model="detailVisible"
|
:title="`车辆详情 - ${selectedVehicle.plateNumber}`"
|
width="700px"
|
>
|
<div v-if="selectedVehicle" class="vehicle-detail">
|
<div class="detail-section">
|
<h3>基本信息</h3>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="车牌号码">{{
|
selectedVehicle.plateNumber
|
}}</el-descriptions-item>
|
<el-descriptions-item label="车辆类型">{{
|
typeMap[selectedVehicle.vehicleType]
|
}}</el-descriptions-item>
|
<el-descriptions-item label="车辆颜色">{{
|
selectedVehicle.color
|
}}</el-descriptions-item>
|
<el-descriptions-item label="进出方向">{{
|
selectedVehicle.direction === "0" ? "进" : "出"
|
}}</el-descriptions-item>
|
<el-descriptions-item label="进出时间">{{
|
selectedVehicle.timestamp
|
}}</el-descriptions-item>
|
<el-descriptions-item label="通道名称">{{
|
selectedVehicle.channelName
|
}}</el-descriptions-item>
|
<el-descriptions-item label="识别方式">{{
|
selectedVehicle.identifyMethod
|
}}</el-descriptions-item>
|
<el-descriptions-item label="车辆状态">{{
|
statusMap[selectedVehicle.status]
|
}}</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
|
<div class="detail-section">
|
<h3>车辆图片</h3>
|
<div class="vehicle-images">
|
<div class="image-item">
|
<div class="image-title">正面图片</div>
|
<el-image
|
:src="selectedVehicle.frontImage"
|
fit="cover"
|
style="width: 100%; height: 200px"
|
:preview-src-list="[
|
selectedVehicle.frontImage,
|
selectedVehicle.sideImage,
|
]"
|
></el-image>
|
</div>
|
<div class="image-item">
|
<div class="image-title">侧面图片</div>
|
<el-image
|
:src="selectedVehicle.sideImage"
|
fit="cover"
|
style="width: 100%; height: 200px"
|
:preview-src-list="[
|
selectedVehicle.sideImage,
|
selectedVehicle.frontImage,
|
]"
|
></el-image>
|
</div>
|
</div>
|
</div>
|
</div>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="detailVisible = false">关闭</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from "vue";
|
import { Search } from "@element-plus/icons-vue";
|
import dayjs from "dayjs";
|
|
// 定义假数据
|
const mockVehicles = [
|
{
|
id: 1,
|
plateNumber: "京A12345",
|
vehicleType: "0",
|
color: "白色",
|
direction: "0",
|
timestamp: "2025-12-31 08:15:30",
|
channelName: "东门通道",
|
identifyMethod: "车牌识别",
|
status: "0",
|
frontImage: "https://picsum.photos/id/1024/800/600",
|
sideImage: "https://picsum.photos/id/1025/800/600",
|
duration: "00:00:00",
|
present: true,
|
},
|
{
|
id: 2,
|
plateNumber: "沪B67890",
|
vehicleType: "1",
|
color: "黑色",
|
direction: "1",
|
timestamp: "2025-12-31 08:10:20",
|
channelName: "西门通道",
|
identifyMethod: "车牌识别",
|
status: "1",
|
frontImage: "https://picsum.photos/id/1026/800/600",
|
sideImage: "https://picsum.photos/id/1027/800/600",
|
duration: "01:25:30",
|
present: false,
|
},
|
{
|
id: 3,
|
plateNumber: "粤C54321",
|
vehicleType: "0",
|
color: "红色",
|
direction: "0",
|
timestamp: "2025-12-31 08:05:15",
|
channelName: "南门通道",
|
identifyMethod: "IC卡识别",
|
status: "0",
|
frontImage: "https://picsum.photos/id/1028/800/600",
|
sideImage: "https://picsum.photos/id/1029/800/600",
|
duration: "00:05:45",
|
present: true,
|
},
|
{
|
id: 4,
|
plateNumber: "川D98765",
|
vehicleType: "2",
|
color: "蓝色",
|
direction: "0",
|
timestamp: "2025-12-31 08:00:00",
|
channelName: "北门通道",
|
identifyMethod: "车牌识别",
|
status: "0",
|
frontImage: "https://picsum.photos/id/1030/800/600",
|
sideImage: "https://picsum.photos/id/1031/800/600",
|
duration: "00:10:30",
|
present: true,
|
},
|
{
|
id: 5,
|
plateNumber: "苏E13579",
|
vehicleType: "0",
|
color: "银色",
|
direction: "1",
|
timestamp: "2025-12-31 07:55:45",
|
channelName: "东门通道",
|
identifyMethod: "车牌识别",
|
status: "1",
|
frontImage: "https://picsum.photos/id/1032/800/600",
|
sideImage: "https://picsum.photos/id/1033/800/600",
|
duration: "02:30:15",
|
present: false,
|
},
|
{
|
id: 6,
|
plateNumber: "浙F24680",
|
vehicleType: "3",
|
color: "黄色",
|
direction: "0",
|
timestamp: "2025-12-31 07:50:30",
|
channelName: "南门通道",
|
identifyMethod: "人工登记",
|
status: "0",
|
frontImage: "https://picsum.photos/id/1034/800/600",
|
sideImage: "https://picsum.photos/id/1035/800/600",
|
duration: "00:15:30",
|
present: true,
|
},
|
{
|
id: 7,
|
plateNumber: "鲁G97531",
|
vehicleType: "1",
|
color: "绿色",
|
direction: "1",
|
timestamp: "2025-12-31 07:45:15",
|
channelName: "西门通道",
|
identifyMethod: "车牌识别",
|
status: "1",
|
frontImage: "https://picsum.photos/id/1036/800/600",
|
sideImage: "https://picsum.photos/id/1037/800/600",
|
duration: "01:45:30",
|
present: false,
|
},
|
{
|
id: 8,
|
plateNumber: "豫H86420",
|
vehicleType: "0",
|
color: "灰色",
|
direction: "0",
|
timestamp: "2025-12-31 07:40:00",
|
channelName: "北门通道",
|
identifyMethod: "IC卡识别",
|
status: "0",
|
frontImage: "https://picsum.photos/id/1038/800/600",
|
sideImage: "https://picsum.photos/id/1039/800/600",
|
duration: "00:20:15",
|
present: true,
|
},
|
];
|
|
// 响应式数据
|
const filters = reactive({
|
plateNumber: "",
|
vehicleType: "",
|
direction: "",
|
timeRange: [],
|
});
|
|
const dataList = ref([]);
|
const pagination = reactive({
|
currentPage: 1,
|
pageSize: 10,
|
total: 0,
|
});
|
|
const detailVisible = ref(false);
|
const selectedVehicle = ref({});
|
|
// 时间范围
|
const timeRange = reactive({
|
startTime: "",
|
endTime: "",
|
});
|
|
// 状态映射
|
const statusMap = {
|
'0': '<el-tag type="success">正常</el-tag>',
|
'1': '<el-tag type="info">已离开</el-tag>',
|
'2': '<el-tag type="warning">异常</el-tag>',
|
};
|
|
// 类型映射
|
const typeMap = {
|
0: "小型车",
|
1: "中型车",
|
2: "大型车",
|
3: "特种车",
|
};
|
|
// 统计数据
|
const todayInCount = computed(() => {
|
const today = dayjs().format("YYYY-MM-DD");
|
return mockVehicles.filter((vehicle) => {
|
return vehicle.direction === "0" && vehicle.timestamp.startsWith(today);
|
}).length;
|
});
|
|
const todayOutCount = computed(() => {
|
const today = dayjs().format("YYYY-MM-DD");
|
return mockVehicles.filter((vehicle) => {
|
return vehicle.direction === "1" && vehicle.timestamp.startsWith(today);
|
}).length;
|
});
|
|
const presentCount = computed(() => {
|
return mockVehicles.filter((vehicle) => vehicle.present).length;
|
});
|
|
const todayTotalCount = computed(() => {
|
const today = dayjs().format("YYYY-MM-DD");
|
return mockVehicles.filter((vehicle) => {
|
return vehicle.timestamp.startsWith(today);
|
}).length;
|
});
|
|
// 表格列配置
|
const columns = [
|
{
|
label: "车牌号码",
|
align: "center",
|
prop: "plateNumber",
|
},
|
{
|
label: "车辆类型",
|
align: "center",
|
prop: "vehicleType",
|
formatter: (row) => {
|
return typeMap[row.vehicleType];
|
},
|
},
|
{
|
label: "车辆颜色",
|
align: "center",
|
prop: "color",
|
},
|
{
|
label: "进出方向",
|
align: "center",
|
prop: "direction",
|
formatter: (row) => {
|
return row.direction === "0" ? '<el-tag type="success">进</el-tag>' : '<el-tag type="info">出</el-tag>';
|
},
|
},
|
{
|
label: "进出时间",
|
align: "center",
|
prop: "timestamp",
|
},
|
{
|
label: "通道名称",
|
align: "center",
|
prop: "channelName",
|
},
|
{
|
label: "识别方式",
|
align: "center",
|
prop: "identifyMethod",
|
},
|
{
|
label: "车辆状态",
|
align: "center",
|
prop: "status",
|
formatter: (row) => {
|
return statusMap[row.status];
|
},
|
},
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 140,
|
operation: [
|
{
|
name: "查看详情",
|
type: "text",
|
clickFun: (row) => {
|
viewDetail(row);
|
},
|
},
|
],
|
},
|
];
|
|
// 过滤后的数据
|
const filteredData = computed(() => {
|
return mockVehicles.filter((item) => {
|
const plateMatch =
|
!filters.plateNumber || item.plateNumber.includes(filters.plateNumber);
|
const typeMatch =
|
!filters.vehicleType || item.vehicleType === filters.vehicleType;
|
const directionMatch =
|
!filters.direction || item.direction === filters.direction;
|
const timeMatch =
|
!timeRange.startTime ||
|
(item.timestamp >= timeRange.startTime &&
|
item.timestamp <= timeRange.endTime);
|
return plateMatch && typeMatch && directionMatch && timeMatch;
|
});
|
});
|
|
// 获取表格数据
|
const getTableData = () => {
|
pagination.total = filteredData.value.length;
|
const start = (pagination.currentPage - 1) * pagination.pageSize;
|
const end = start + pagination.pageSize;
|
dataList.value = filteredData.value.slice(start, end);
|
};
|
|
// 重置过滤器
|
const resetFilters = () => {
|
filters.plateNumber = "";
|
filters.vehicleType = "";
|
filters.direction = "";
|
filters.timeRange = [];
|
timeRange.startTime = "";
|
timeRange.endTime = "";
|
pagination.currentPage = 1;
|
getTableData();
|
};
|
|
// 分页变化
|
const changePage = ({ page, limit }) => {
|
pagination.currentPage = page;
|
pagination.pageSize = limit;
|
getTableData();
|
};
|
|
// 时间范围变化
|
const changeTimeRange = (value) => {
|
if (value && value.length === 2) {
|
timeRange.startTime = value[0];
|
timeRange.endTime = value[1];
|
} else {
|
timeRange.startTime = "";
|
timeRange.endTime = "";
|
}
|
getTableData();
|
};
|
|
// 查看车辆详情
|
const viewDetail = (row) => {
|
selectedVehicle.value = row;
|
detailVisible.value = true;
|
};
|
|
// 初始化数据
|
onMounted(() => {
|
getTableData();
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.table_list {
|
margin-top: unset;
|
}
|
|
.vehicle-stats {
|
display: flex;
|
gap: 20px;
|
margin-bottom: 20px;
|
|
.stat-card {
|
flex: 1;
|
background: #fff;
|
padding: 20px;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
text-align: center;
|
|
.stat-title {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 10px;
|
}
|
|
.stat-value {
|
font-size: 32px;
|
font-weight: bold;
|
|
&.in {
|
color: #67c23a;
|
}
|
|
&.out {
|
color: #409eff;
|
}
|
|
&.present {
|
color: #e6a23c;
|
}
|
|
&.total {
|
color: #909399;
|
}
|
}
|
}
|
}
|
|
.vehicle-detail {
|
.detail-section {
|
margin-bottom: 20px;
|
|
h3 {
|
font-size: 16px;
|
font-weight: bold;
|
margin-bottom: 10px;
|
color: #303133;
|
}
|
}
|
|
.vehicle-images {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 15px;
|
|
.image-item {
|
background: #f5f7fa;
|
padding: 15px;
|
border-radius: 8px;
|
|
.image-title {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 10px;
|
text-align: center;
|
}
|
}
|
}
|
}
|
</style>
|