| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |