车辆监控
This commit is contained in:
parent
1bf47c141c
commit
e1a82fb670
576
src/components/car/VehicleCards.vue
Normal file
576
src/components/car/VehicleCards.vue
Normal file
@ -0,0 +1,576 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vehicle-cards">
|
||||||
|
<!-- 卡片网格布局 -->
|
||||||
|
<div class="cards-container">
|
||||||
|
<div class="cards-grid">
|
||||||
|
<div
|
||||||
|
v-for="vehicle in filteredVehicleData"
|
||||||
|
:key="vehicle.id"
|
||||||
|
class="vehicle-card"
|
||||||
|
:class="{
|
||||||
|
'task-status': vehicle.routeStatus === '任务中',
|
||||||
|
'idle-status': vehicle.routeStatus === '闲置中',
|
||||||
|
'waiting-status': vehicle.routeStatus === '等待中'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="card-content">
|
||||||
|
<!-- 第一行:左侧图标+车辆编号和类型,右侧状态 -->
|
||||||
|
<div class="card-row card-header">
|
||||||
|
<div class="left-content">
|
||||||
|
<img src="@/assets/images/carIcon.png" class="car-icon" alt="车辆图标" />
|
||||||
|
<div class="vehicle-info">
|
||||||
|
<span class="vehicle-id">{{ vehicle.carId }}</span>
|
||||||
|
<span class="vehicle-type">{{ vehicle.type }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-content">
|
||||||
|
<span class="status-tag"
|
||||||
|
:class="{
|
||||||
|
'task': vehicle.routeStatus === '任务中',
|
||||||
|
'idle': vehicle.routeStatus === '闲置中',
|
||||||
|
'waiting': vehicle.routeStatus === '等待中'
|
||||||
|
}">
|
||||||
|
{{ vehicle.routeStatus }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第二行:电池信息、电压电流 -->
|
||||||
|
<div class="card-row battery-info">
|
||||||
|
<div class="info-item battery-status">
|
||||||
|
<div class="battery-container">
|
||||||
|
<div class="battery-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="14" viewBox="0 0 24 14">
|
||||||
|
<rect x="1" y="2" width="20" height="10" rx="1" ry="1" fill="transparent" stroke="#00ff84" stroke-width="1.5"/>
|
||||||
|
<rect x="21" y="4" width="2" height="6" rx="1" ry="1" fill="#00ff84"/>
|
||||||
|
<rect x="2" y="3" :width="getBatteryWidth(vehicle.soc)" height="8" rx="1" ry="1" fill="#00ff84"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span class="lightning-icon">⚡</span>
|
||||||
|
<span class="percentage">{{ vehicle.soc }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="divider">|</span>
|
||||||
|
<div class="info-item voltage">
|
||||||
|
<i class="el-icon voltage-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M14 9.5V6h2V2H8v4h2v3.5L2 20h20L14 9.5zm-6 8.5l5-5l5 5H8z"/></svg>
|
||||||
|
</i>
|
||||||
|
<span>{{ vehicle.voltage }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="divider">|</span>
|
||||||
|
<div class="info-item current">
|
||||||
|
<i class="el-icon current-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M16 7l-4-4l-4 4l1.41 1.41L11 6.83V20h2V6.83l1.59 1.58L16 7z"/></svg>
|
||||||
|
</i>
|
||||||
|
<span>{{ vehicle.current }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第三行:位置信息 -->
|
||||||
|
<div class="card-row location-info">
|
||||||
|
<i class="el-icon location-icon">
|
||||||
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-78e17ca8=""><path fill="currentColor" d="M512 928c23.936 0 117.504-68.352 192.064-153.152C803.456 661.888 864 535.808 864 416c0-189.632-155.84-320-352-320S160 226.368 160 416c0 120.32 60.544 246.4 159.936 359.232C394.432 859.84 488 928 512 928zm0-435.2a64 64 0 1 0 0-128 64 64 0 0 0 0 128zm0 140.8a204.8 204.8 0 1 1 0-409.6 204.8 204.8 0 0 1 0 409.6z"></path></svg>
|
||||||
|
</i>
|
||||||
|
<span class="location-value">{{ vehicle.location }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第四行:负责人和电话 -->
|
||||||
|
<div class="card-row contact-info">
|
||||||
|
<div class="left-content">
|
||||||
|
<i class="el-icon person-icon">
|
||||||
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-78e17ca8=""><path fill="currentColor" d="M628.736 528.896A416 416 0 0 1 928 928H96a415.872 415.872 0 0 1 299.264-399.104L512 704l116.736-175.104zM720 304a208 208 0 1 1-416 0 208 208 0 0 1 416 0z"></path></svg>
|
||||||
|
</i>
|
||||||
|
<span class="contact-name">{{ vehicle.batteryLevel }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right-content">
|
||||||
|
<i class="el-icon phone-icon">
|
||||||
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-78e17ca8=""><path fill="currentColor" d="M768 896a128 128 0 0 0 128-128V256a128 128 0 0 0-128-128H256a128 128 0 0 0-128 128v512a128 128 0 0 0 128 128h512zm0 64H256a192 192 0 0 1-192-192V256a192 192 0 0 1 192-192h512a192 192 0 0 1 192 192v512a192 192 0 0 1-192 192z"></path><path fill="currentColor" d="M542.336 711.552a32 32 0 0 1-33.92-29.44 431.04 431.04 0 0 0-166.272-173.76 32 32 0 0 1-2.048-50.592l42.688-38.816a32 32 0 0 1 43.008 3.328 353.6 353.6 0 0 1 151.936 167.52 32 32 0 0 1-28.8 45.76l-.512-13.28c.448 4.48.704 8.96.512 13.28zm215.808-261.12a32 32 0 0 1-46.08-12.608 803.2 803.2 0 0 0-315.392-280.064 32.128 32.128 0 0 1-9.216-43.392l38.016-51.2a32.128 32.128 0 0 1 44.352-8.448c123.136 88.576 242.688 196.544 339.456 347.648a32 32 0 0 1-11.264 47.424l-39.872 1.344.064-.704z"></path></svg>
|
||||||
|
</i>
|
||||||
|
<span class="contact-phone">{{ vehicle.contactPhone }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第五行:品牌和公司 -->
|
||||||
|
<div class="card-row company-info">
|
||||||
|
<div class="left-content">
|
||||||
|
<i class="el-icon brand-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 4C7.59 4 4 7.59 4 12s3.59 8 8 8s8-3.59 8-8s-3.59-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6s6 2.69 6 6s-2.69 6-6 6zm-1-10h2v4h-2zm0 5h2v2h-2z"/></svg>
|
||||||
|
</i>
|
||||||
|
<span class="brand-name">{{ vehicle.brand }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="right-content">
|
||||||
|
<i class="el-icon company-icon">
|
||||||
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-78e17ca8=""><path fill="currentColor" d="M160 224a32 32 0 0 0-32 32v544a32 32 0 0 0 32 32h704a32 32 0 0 0 32-32V256a32 32 0 0 0-32-32H160zm0-64h704a96 96 0 0 1 96 96v544a96 96 0 0 1-96 96H160a96 96 0 0 1-96-96V256a96 96 0 0 1 96-96z"></path><path fill="currentColor" d="M464 224a32 32 0 0 0-32 32v128a32 32 0 0 0 32 32h96a32 32 0 0 0 32-32V256a32 32 0 0 0-32-32h-96zM96 384h128q32 0 32 32t-32 32H96q-32 0-32-32t32-32zm142.4 234.24L326.4 800h64.32l96-170.24-41.92-64.96L384 704l-88.96-149.76-57.6 64z"></path></svg>
|
||||||
|
</i>
|
||||||
|
<span class="company-name">{{ vehicle.organization }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 第六行:最新更新时间 -->
|
||||||
|
<div class="card-row update-time">
|
||||||
|
<span class="update-label">最新更新时间:</span>
|
||||||
|
<span class="update-value">{{ vehicle.lastTime }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部统计和分页 -->
|
||||||
|
<div class="bottom-stats">
|
||||||
|
<div class="status-counters">
|
||||||
|
<div class="status-counter online">
|
||||||
|
<div class="counter-label">在线设备:</div>
|
||||||
|
<div class="counter-value">{{ onlineCount }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-counter offline">
|
||||||
|
<div class="counter-label">离线设备:</div>
|
||||||
|
<span class="counter-value">{{ offlineCount }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-counter fault">
|
||||||
|
<span class="counter-label">故障设备:</span>
|
||||||
|
<span class="counter-value">{{ faultCount }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
:total="total"
|
||||||
|
v-model:page="currentPage"
|
||||||
|
v-model:limit="pageSize"
|
||||||
|
@pagination="handlePagination"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, defineProps, defineEmits, computed } from 'vue';
|
||||||
|
import Pagination from '@/components/Pagination/index.vue';
|
||||||
|
import car1 from '@/assets/images/car1.png';
|
||||||
|
import car2 from '@/assets/images/car2.png';
|
||||||
|
import car3 from '@/assets/images/car3.png';
|
||||||
|
import car4 from '@/assets/images/car4.png';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
vehicleData: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['view', 'control']);
|
||||||
|
|
||||||
|
// 分页相关 - 调整为每页显示8条数据
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(8);
|
||||||
|
const total = computed(() => props.vehicleData.length);
|
||||||
|
|
||||||
|
// 筛选后的数据
|
||||||
|
const filteredVehicleData = computed(() => {
|
||||||
|
const start = (currentPage.value - 1) * pageSize.value;
|
||||||
|
const end = start + pageSize.value;
|
||||||
|
return props.vehicleData.slice(start, end);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 状态统计
|
||||||
|
const onlineCount = computed(() =>
|
||||||
|
props.vehicleData.filter(v => v.status === '在线').length
|
||||||
|
);
|
||||||
|
const offlineCount = computed(() =>
|
||||||
|
props.vehicleData.filter(v => v.status === '离线').length
|
||||||
|
);
|
||||||
|
const faultCount = computed(() =>
|
||||||
|
props.vehicleData.filter(v => v.status === '故障').length
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取车辆图片
|
||||||
|
function getCarImage(vehicle) {
|
||||||
|
switch (vehicle.status) {
|
||||||
|
case '在线':
|
||||||
|
return car1;
|
||||||
|
case '离线':
|
||||||
|
return car2;
|
||||||
|
case '故障':
|
||||||
|
return car3;
|
||||||
|
default:
|
||||||
|
return car4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类型
|
||||||
|
function getStatusType(status) {
|
||||||
|
switch (status) {
|
||||||
|
case '在线':
|
||||||
|
return 'success';
|
||||||
|
case '离线':
|
||||||
|
return 'info';
|
||||||
|
case '故障':
|
||||||
|
return 'danger';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取行驶状态对应的类型
|
||||||
|
function getRouteStatusType(status) {
|
||||||
|
switch (status) {
|
||||||
|
case '任务中':
|
||||||
|
return 'success';
|
||||||
|
case '待命中':
|
||||||
|
case '闲置中':
|
||||||
|
return '';
|
||||||
|
case '充电中':
|
||||||
|
return 'warning';
|
||||||
|
case '等待中':
|
||||||
|
return 'info';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取电池百分比
|
||||||
|
function getBatteryPercentage(soc) {
|
||||||
|
return parseInt(soc) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取电池宽度
|
||||||
|
function getBatteryWidth(soc) {
|
||||||
|
const percentage = getBatteryPercentage(soc);
|
||||||
|
// 最大宽度为18(因为总宽20,留出左右边距)
|
||||||
|
return Math.max(1, Math.min(18, (percentage / 100) * 18));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取电池颜色
|
||||||
|
function getBatteryColor(soc) {
|
||||||
|
const percentage = getBatteryPercentage(soc);
|
||||||
|
if (percentage > 70) return '#67C23A';
|
||||||
|
if (percentage > 30) return '#E6A23C';
|
||||||
|
return '#F56C6C';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理查看详情
|
||||||
|
function handleView(row) {
|
||||||
|
emit('view', row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理车辆控制
|
||||||
|
function handleControl(row) {
|
||||||
|
emit('control', row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理分页变化
|
||||||
|
function handlePagination({ page, limit }) {
|
||||||
|
currentPage.value = page;
|
||||||
|
pageSize.value = limit;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.vehicle-cards {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(100% - 60px); /* 减去底部统计区域高度 */
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-grid {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card {
|
||||||
|
background-color: #343744;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* 顶部状态颜色 */
|
||||||
|
&.task-status {
|
||||||
|
border-top: 4px solid #5690e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.idle-status {
|
||||||
|
border-top: 4px solid #c3c3c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&.waiting-status {
|
||||||
|
border-top: 4px solid #ff312f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.card-header {
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.left-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.car-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.vehicle-id {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-type {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #96a0b5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag {
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.task {
|
||||||
|
background-color: #5690e7;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.idle {
|
||||||
|
background-color: #c3c3c3;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.waiting {
|
||||||
|
background-color: #ff312f;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.battery-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.battery-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgba(0, 255, 132, 0.2); /* #00ff84 with 20% opacity */
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
|
||||||
|
.battery-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightning-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0 4px;
|
||||||
|
color: #00ff84;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percentage {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00ff84;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i.el-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #67C23A;
|
||||||
|
|
||||||
|
&.voltage-icon,
|
||||||
|
&.current-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
color: #5e6778;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.location-info {
|
||||||
|
color: #96a0b5;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
i.location-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
margin-right: 4px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.contact-info,
|
||||||
|
&.company-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.left-content,
|
||||||
|
.right-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
color: #96a0b5;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #409EFF;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.update-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #5e6778;
|
||||||
|
|
||||||
|
.update-label {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-stats {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 15px 5px;
|
||||||
|
border-top: 1px solid #4C4F5F;
|
||||||
|
|
||||||
|
.status-counters {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.status-counter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.counter-label {
|
||||||
|
color: #96A0B5;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.online .counter-value {
|
||||||
|
color: #67C23A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.offline .counter-value {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fault .counter-value {
|
||||||
|
color: #F56C6C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义EL组件样式 */
|
||||||
|
:deep(.el-button.is-text) {
|
||||||
|
color: #347AE2;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-pagination) {
|
||||||
|
margin-top: 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义滚动条 */
|
||||||
|
.cards-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-container::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #4C4F5F;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-container::-webkit-scrollbar-track {
|
||||||
|
background-color: #292c38;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 1600px) {
|
||||||
|
.vehicle-card {
|
||||||
|
width: calc(33.33% - 12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.vehicle-card {
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.vehicle-card {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-stats {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.status-counters {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,76 +1,98 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="search-filter">
|
<div class="search-filter">
|
||||||
<!-- 车牌号输入 -->
|
<div class="search-area">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="请输入车牌号查询"
|
placeholder="请输入车牌号查询"
|
||||||
clearable
|
clearable
|
||||||
class="search-input"
|
prefix-icon="Search"
|
||||||
@input="handleSearch"
|
class="search-input"
|
||||||
prefix-icon="Search"
|
@input="handleSearchInput"
|
||||||
/>
|
@keyup.enter="handleSearch"
|
||||||
<!-- 车辆类型下拉 -->
|
/>
|
||||||
<el-select
|
|
||||||
v-model="vehicleType"
|
<el-select v-model="vehicleType" placeholder="车辆类型" clearable class="search-select" @change="handleSearch">
|
||||||
placeholder="车辆类型"
|
<el-option label="全部" value="" />
|
||||||
clearable
|
<el-option label="驱动车" value="驱动车" />
|
||||||
class="search-select"
|
<el-option label="巡检车" value="巡检车" />
|
||||||
@change="handleFilter"
|
</el-select>
|
||||||
>
|
|
||||||
<el-option label="全部" value="" />
|
<el-select v-model="vehicleStatus" placeholder="车辆状态" clearable class="search-select" @change="handleSearch">
|
||||||
<el-option label="驱动车" value="驱动车" />
|
<el-option label="全部" value="" />
|
||||||
<el-option label="巡检车" value="巡检车" />
|
<el-option label="在线" value="在线" />
|
||||||
<!-- 可根据实际类型补充 -->
|
<el-option label="离线" value="离线" />
|
||||||
</el-select>
|
<el-option label="故障" value="故障" />
|
||||||
<!-- 车辆状态下拉 -->
|
</el-select>
|
||||||
<el-select
|
|
||||||
v-model="vehicleStatus"
|
<div class="search-buttons">
|
||||||
placeholder="车辆状态"
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
clearable
|
<el-button class="reset-btn" @click="handleReset">重置</el-button>
|
||||||
class="search-select"
|
<el-button class="export-btn" @click="handleExport">导出</el-button>
|
||||||
@change="handleFilter"
|
</div>
|
||||||
>
|
|
||||||
<el-option label="全部" value="" />
|
<div class="action-buttons">
|
||||||
<el-option label="在线" value="在线" />
|
<!-- 视图切换按钮 -->
|
||||||
<el-option label="离线" value="离线" />
|
<div class="view-switcher">
|
||||||
<el-option label="故障" value="故障" />
|
<div
|
||||||
</el-select>
|
class="view-btn list-view"
|
||||||
|
:class="{'active': viewMode === 'list'}"
|
||||||
<div class="button-group">
|
@click="switchView('list')"
|
||||||
<!-- 搜索按钮 -->
|
title="列表视图"
|
||||||
<el-button type="primary" class="search-btn" @click="handleFilter">搜索</el-button>
|
>
|
||||||
<!-- 重置按钮 -->
|
<svg-icon icon-class="table" />
|
||||||
<el-button class="reset-btn" @click="resetFilter">重置</el-button>
|
</div>
|
||||||
</div>
|
<div
|
||||||
|
class="view-btn card-view"
|
||||||
<div class="action-group">
|
:class="{'active': viewMode === 'card'}"
|
||||||
<!-- 导出按钮 -->
|
@click="switchView('card')"
|
||||||
<el-button class="export-btn" @click="exportData">导出</el-button>
|
title="卡片视图"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="dashboard" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineEmits } from 'vue';
|
import { ref, defineProps, defineEmits, watchEffect } from 'vue';
|
||||||
import { Download, Search } from '@element-plus/icons-vue';
|
import { Search, Download } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
const emit = defineEmits(['search', 'filter', 'reset', 'export']);
|
const props = defineProps({
|
||||||
|
filters: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
viewMode: {
|
||||||
|
type: String,
|
||||||
|
default: 'list'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['filter', 'reset', 'export', 'switch-view']);
|
||||||
|
|
||||||
|
// 搜索相关
|
||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
const vehicleType = ref('');
|
const vehicleType = ref('');
|
||||||
const vehicleStatus = ref('');
|
const vehicleStatus = ref('');
|
||||||
|
|
||||||
// 搜索处理
|
// 监听filters变化并同步到本地状态
|
||||||
function handleSearch() {
|
watchEffect(() => {
|
||||||
emit('search', {
|
if (props.filters) {
|
||||||
searchText: searchText.value,
|
searchText.value = props.filters.searchText || '';
|
||||||
vehicleType: vehicleType.value,
|
vehicleType.value = props.filters.vehicleType || '';
|
||||||
vehicleStatus: vehicleStatus.value
|
vehicleStatus.value = props.filters.vehicleStatus || '';
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理搜索输入
|
||||||
|
function handleSearchInput() {
|
||||||
|
// 可以添加防抖逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤处理
|
// 处理搜索
|
||||||
function handleFilter() {
|
function handleSearch() {
|
||||||
emit('filter', {
|
emit('filter', {
|
||||||
searchText: searchText.value,
|
searchText: searchText.value,
|
||||||
vehicleType: vehicleType.value,
|
vehicleType: vehicleType.value,
|
||||||
@ -78,107 +100,105 @@ function handleFilter() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置
|
// 处理重置
|
||||||
function resetFilter() {
|
function handleReset() {
|
||||||
searchText.value = '';
|
searchText.value = '';
|
||||||
vehicleType.value = '';
|
vehicleType.value = '';
|
||||||
vehicleStatus.value = '';
|
vehicleStatus.value = '';
|
||||||
emit('reset');
|
emit('reset');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出
|
// 处理导出
|
||||||
function exportData() {
|
function handleExport() {
|
||||||
emit('export');
|
emit('export');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视图切换
|
||||||
|
function switchView(type) {
|
||||||
|
emit('switch-view', type);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.search-filter {
|
.search-filter {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 0;
|
|
||||||
background: transparent;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-area {
|
||||||
width: 270px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-select {
|
|
||||||
width: 140px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
|
||||||
}
|
.search-input {
|
||||||
|
width: 250px;
|
||||||
.action-group {
|
margin-right: 10px;
|
||||||
margin-left: auto;
|
}
|
||||||
}
|
|
||||||
|
.search-select {
|
||||||
@media (max-width: 1200px) {
|
width: 150px;
|
||||||
.search-filter {
|
margin-right: 10px;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
|
|
||||||
|
.search-buttons {
|
||||||
|
margin-right: 20px;
|
||||||
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.action-buttons {
|
||||||
width: 100%;
|
margin-left: auto;
|
||||||
max-width: 270px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-group {
|
|
||||||
margin-left: 0;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
gap: 10px;
|
||||||
margin-top: 10px;
|
|
||||||
}
|
.view-switcher {
|
||||||
}
|
display: flex;
|
||||||
|
background-color: #343744;
|
||||||
@media (max-width: 768px) {
|
border-radius: 8px;
|
||||||
.search-filter {
|
overflow: hidden;
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
.view-btn {
|
||||||
}
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
.search-input, .search-select {
|
align-items: center;
|
||||||
width: 100%;
|
width: 36px;
|
||||||
}
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
.button-group, .action-group {
|
transition: all 0.3s;
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
&:hover {
|
||||||
|
background-color: #2B3B5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #96A0B5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 输入框 下拉框 搜索栏
|
||||||
:deep(.el-input__wrapper:hover) {
|
:deep(.el-input__wrapper:hover) {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-select) {
|
:deep(.el-select) {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
width: 140px !important;
|
width: 150px !important;
|
||||||
// 修改el-select的输入框的border
|
// 修改el-select的输入框的border
|
||||||
--el-border-color-hover: #343744 !important;
|
--el-border-color-hover: #343744 !important;
|
||||||
--el-border-color: #343744 !important;
|
--el-border-color: #343744 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-input__wrapper) {
|
|
||||||
background: #343744 !important;
|
|
||||||
border: none !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
height: 36px !important;
|
|
||||||
font-size: 15px;
|
|
||||||
color: #96A0B5 !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-select__wrapper.is-focused) {
|
:deep(.el-select__wrapper.is-focused) {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
@ -188,10 +208,17 @@ function exportData() {
|
|||||||
color: #96A0B5 !important;
|
color: #96A0B5 !important;
|
||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
height: 36px !important;
|
height: 36px !important;
|
||||||
width: 140px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: #343744 !important;
|
||||||
|
color: #96A0B5 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
height: 36px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-select__placeholder) {
|
:deep(.el-select__placeholder) {
|
||||||
@ -217,4 +244,50 @@ function exportData() {
|
|||||||
background-color: #337ecc;
|
background-color: #337ecc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 媒体查询,处理响应式布局
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.search-area {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-select {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-buttons {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.search-area {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.search-input,
|
||||||
|
.search-select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-buttons,
|
||||||
|
.action-buttons {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vehicle-table">
|
<div class="vehicle-table">
|
||||||
<!-- 搜索区域 -->
|
<!-- 搜索区域 -->
|
||||||
<div class="search-area">
|
<div class="search-area" v-if="!hideSearch">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="请输入车牌号查询"
|
placeholder="请输入车牌号查询"
|
||||||
@ -32,83 +32,103 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<!-- 两个Tab切换的图标1.png 2.png -->
|
<!-- 视图切换按钮 -->
|
||||||
<!-- 默认1.png 显示当前表格点击2.png 显示卡片样式 -->
|
<div class="view-switcher">
|
||||||
|
<div
|
||||||
|
class="view-btn list-view"
|
||||||
|
:class="{'active': !isCardView}"
|
||||||
|
@click="switchView('list')"
|
||||||
|
title="列表视图"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="table" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="view-btn card-view"
|
||||||
|
:class="{'active': isCardView}"
|
||||||
|
@click="switchView('card')"
|
||||||
|
title="卡片视图"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="dashboard" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格区域 -->
|
||||||
<el-table
|
<div class="table-container" :class="{ 'full-height': hideSearch }">
|
||||||
:data="filteredVehicleData"
|
<!-- 表格 -->
|
||||||
style="width: 100%"
|
<el-table
|
||||||
@selection-change="handleSelectionChange"
|
:data="filteredVehicleData"
|
||||||
:header-cell-style="{ backgroundColor: '#343744', color: '#fff' }"
|
style="width: 100%"
|
||||||
class="custom-table"
|
@selection-change="handleSelectionChange"
|
||||||
:bg-color="'#292C38'"
|
:header-cell-style="{ backgroundColor: '#343744', color: '#fff' }"
|
||||||
>
|
class="custom-table"
|
||||||
<el-table-column type="selection" width="55" />
|
:bg-color="'#292C38'"
|
||||||
|
>
|
||||||
<el-table-column label="序号" width="70">
|
<el-table-column type="selection" width="55" />
|
||||||
<template #default="scope">
|
|
||||||
{{ scope.$index + 1 }}
|
<el-table-column label="序号" width="70">
|
||||||
</template>
|
<template #default="scope">
|
||||||
</el-table-column>
|
{{ scope.$index + 1 }}
|
||||||
|
</template>
|
||||||
<el-table-column label="操作" width="120">
|
</el-table-column>
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
<el-table-column label="操作" width="120">
|
||||||
link
|
<template #default="scope">
|
||||||
text
|
<el-button
|
||||||
type="primary"
|
link
|
||||||
|
text
|
||||||
@click="handleView(scope.row)"
|
type="primary"
|
||||||
>
|
@click="handleView(scope.row)"
|
||||||
查看
|
>
|
||||||
</el-button>
|
查看
|
||||||
</template>
|
</el-button>
|
||||||
</el-table-column>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="carId" label="车辆号" />
|
|
||||||
<el-table-column prop="type" label="类型" />
|
<el-table-column prop="carId" label="车辆号" />
|
||||||
<el-table-column prop="brand" label="品牌" />
|
<el-table-column prop="type" label="类型" />
|
||||||
<el-table-column prop="organization" label="所属单位" width="120"/>
|
<el-table-column prop="brand" label="品牌" />
|
||||||
<el-table-column prop="number" label="当前故障数" width="100" />
|
<el-table-column prop="organization" label="所属单位" width="120"/>
|
||||||
|
<el-table-column prop="number" label="当前故障数" width="100" />
|
||||||
<el-table-column prop="routeStatus" label="作业状态">
|
|
||||||
<template #default="scope">
|
<el-table-column prop="routeStatus" label="作业状态">
|
||||||
<el-tag :type="getRouteStatusType(scope.row.routeStatus)">
|
<template #default="scope">
|
||||||
{{ scope.row.routeStatus }}
|
<el-tag :type="getRouteStatusType(scope.row.routeStatus)">
|
||||||
</el-tag>
|
{{ scope.row.routeStatus }}
|
||||||
</template>
|
</el-tag>
|
||||||
</el-table-column>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="车辆状态">
|
|
||||||
<template #default="scope">
|
<el-table-column prop="status" label="车辆状态">
|
||||||
<el-tag :type="scope.row.status === '在线' ? 'success' : 'danger'">
|
<template #default="scope">
|
||||||
{{ scope.row.status }}
|
<el-tag :type="scope.row.status === '在线' ? 'success' : 'danger'">
|
||||||
</el-tag>
|
{{ scope.row.status }}
|
||||||
</template>
|
</el-tag>
|
||||||
</el-table-column>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="chargeStatus" label="充电状态"/>
|
|
||||||
<el-table-column prop="voltage" label="总电压"/>
|
<el-table-column prop="chargeStatus" label="充电状态"/>
|
||||||
<el-table-column prop="current" label="总电流" />
|
<el-table-column prop="voltage" label="总电压"/>
|
||||||
<el-table-column prop="soc" label="SOC" />
|
<el-table-column prop="current" label="总电流" />
|
||||||
<el-table-column prop="lastTime" label="最新时间" width="170" />
|
<el-table-column prop="soc" label="SOC" />
|
||||||
<el-table-column prop="location" label="最新位置" />
|
<el-table-column prop="lastTime" label="最新时间" width="170" />
|
||||||
<el-table-column prop="batteryLevel" label="负责人" />
|
<el-table-column prop="location" label="最新位置" />
|
||||||
<el-table-column prop="contactPhone" label="负责人电话" width="130" />
|
<el-table-column prop="batteryLevel" label="负责人" />
|
||||||
</el-table>
|
<el-table-column prop="contactPhone" label="负责人电话" width="130" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-container">
|
||||||
v-show="total > 0"
|
<pagination
|
||||||
:total="total"
|
v-show="total > 0"
|
||||||
v-model:page="currentPage"
|
:total="total"
|
||||||
v-model:limit="pageSize"
|
v-model:page="currentPage"
|
||||||
@pagination="handlePagination"
|
v-model:limit="pageSize"
|
||||||
/>
|
@pagination="handlePagination"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -125,10 +145,14 @@ const props = defineProps({
|
|||||||
filters: {
|
filters: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
hideSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['view', 'control', 'selection-change', 'search', 'filter', 'reset', 'export']);
|
const emit = defineEmits(['view', 'control', 'selection-change', 'search', 'filter', 'reset', 'export', 'switch-view']);
|
||||||
|
|
||||||
// 搜索相关
|
// 搜索相关
|
||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
@ -143,6 +167,9 @@ const total = computed(() => props.vehicleData.length);
|
|||||||
// 选中的行
|
// 选中的行
|
||||||
const selectedRows = ref([]);
|
const selectedRows = ref([]);
|
||||||
|
|
||||||
|
// 视图切换状态
|
||||||
|
const isCardView = ref(false);
|
||||||
|
|
||||||
// 监听filters变化并同步到本地状态
|
// 监听filters变化并同步到本地状态
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (props.filters) {
|
if (props.filters) {
|
||||||
@ -233,6 +260,12 @@ function handleControl(row) {
|
|||||||
emit('control', row);
|
emit('control', row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视图切换
|
||||||
|
function switchView(viewType) {
|
||||||
|
isCardView.value = viewType === 'card';
|
||||||
|
emit('switch-view', viewType);
|
||||||
|
}
|
||||||
|
|
||||||
// 处理选择变化
|
// 处理选择变化
|
||||||
function handleSelectionChange(selection) {
|
function handleSelectionChange(selection) {
|
||||||
selectedRows.value = selection;
|
selectedRows.value = selection;
|
||||||
@ -249,8 +282,10 @@ function handlePagination({ page, limit }) {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.vehicle-table {
|
.vehicle-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #292c38;
|
background-color: transparent;
|
||||||
border-radius: 4px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-area {
|
.search-area {
|
||||||
@ -278,9 +313,58 @@ function handlePagination({ page, limit }) {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
|
.view-switcher {
|
||||||
|
display: flex;
|
||||||
|
background-color: #343744;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.view-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #2B3B5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #96A0B5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&.full-height {
|
||||||
|
height: calc(100% - 50px); /* 留出分页高度 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 输入框 下拉框 搜索栏
|
// 输入框 下拉框 搜索栏
|
||||||
:deep(.el-input__wrapper:hover) {
|
:deep(.el-input__wrapper:hover) {
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
@ -342,7 +426,7 @@ function handlePagination({ page, limit }) {
|
|||||||
|
|
||||||
/* 自定义表格样式 */
|
/* 自定义表格样式 */
|
||||||
.custom-table {
|
.custom-table {
|
||||||
background-color: #292c38 !important;
|
background-color: transparent !important;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -363,11 +447,11 @@ function handlePagination({ page, limit }) {
|
|||||||
|
|
||||||
/* 确保整个表格区域背景色 */
|
/* 确保整个表格区域背景色 */
|
||||||
:deep(.el-table) {
|
:deep(.el-table) {
|
||||||
background-color: #292c38 !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table__body) {
|
:deep(.el-table__body) {
|
||||||
background-color: #292c38 !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-table__inner-wrapper::before) {
|
:deep(.el-table__inner-wrapper::before) {
|
||||||
@ -418,7 +502,7 @@ function handlePagination({ page, limit }) {
|
|||||||
|
|
||||||
td {
|
td {
|
||||||
height: 68px!important;
|
height: 68px!important;
|
||||||
background-color: #292c38;
|
background-color: transparent;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border-bottom: 1px solid #4C4F5F;
|
border-bottom: 1px solid #4C4F5F;
|
||||||
}
|
}
|
||||||
@ -465,8 +549,8 @@ function handlePagination({ page, limit }) {
|
|||||||
|
|
||||||
/* 全局表格样式覆盖,防止闪白 */
|
/* 全局表格样式覆盖,防止闪白 */
|
||||||
:deep(.el-table) {
|
:deep(.el-table) {
|
||||||
--el-table-bg-color: #292c38 !important;
|
--el-table-bg-color: transparent !important;
|
||||||
--el-table-tr-bg-color: #292c38 !important;
|
--el-table-tr-bg-color: transparent !important;
|
||||||
--el-table-border-color: #4C4F5F !important;
|
--el-table-border-color: #4C4F5F !important;
|
||||||
--el-table-header-bg-color: #343744 !important;
|
--el-table-header-bg-color: #343744 !important;
|
||||||
--el-table-row-hover-bg-color: #2B3B5A !important;
|
--el-table-row-hover-bg-color: #2B3B5A !important;
|
||||||
@ -476,6 +560,20 @@ function handlePagination({ page, limit }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 自定义滚动条 */
|
||||||
|
.table-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #4C4F5F;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container::-webkit-scrollbar-track {
|
||||||
|
background-color: #292c38;
|
||||||
|
}
|
||||||
|
|
||||||
// 媒体查询,处理响应式布局
|
// 媒体查询,处理响应式布局
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.search-area {
|
.search-area {
|
||||||
|
|||||||
@ -12,18 +12,83 @@
|
|||||||
class="stats-section"
|
class="stats-section"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 表格区域 -->
|
<!-- 内容容器 - 包含搜索区域和内容区域 -->
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<VehicleTable
|
<!-- 搜索和过滤区域 -->
|
||||||
:vehicleData="filteredVehicleData"
|
<div class="search-area">
|
||||||
:filters="filters"
|
<el-input
|
||||||
@view="viewVehicleDetail"
|
v-model="searchText"
|
||||||
@control="controlVehicle"
|
placeholder="请输入车牌号查询"
|
||||||
@selection-change="handleSelectionChange"
|
clearable
|
||||||
@filter="handleFilter"
|
prefix-icon="Search"
|
||||||
@reset="resetFilter"
|
class="search-input"
|
||||||
@export="exportVehicleData"
|
@keyup.enter="handleSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<el-select v-model="vehicleType" placeholder="车辆类型" clearable class="search-select" @change="handleSearch">
|
||||||
|
<el-option label="全部" value="" />
|
||||||
|
<el-option label="驱动车" value="驱动车" />
|
||||||
|
<el-option label="巡检车" value="巡检车" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<el-select v-model="vehicleStatus" placeholder="车辆状态" clearable class="search-select" @change="handleSearch">
|
||||||
|
<el-option label="全部" value="" />
|
||||||
|
<el-option label="在线" value="在线" />
|
||||||
|
<el-option label="离线" value="离线" />
|
||||||
|
<el-option label="故障" value="故障" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<div class="search-buttons">
|
||||||
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||||
|
<el-button class="reset-btn" @click="resetFilter">重置</el-button>
|
||||||
|
<el-button class="export-btn" @click="exportVehicleData">导出</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
<!-- 视图切换按钮 -->
|
||||||
|
<div class="view-switcher">
|
||||||
|
<div
|
||||||
|
class="view-btn list-view"
|
||||||
|
:class="{'active': viewMode === 'list'}"
|
||||||
|
@click="handleViewSwitch('list')"
|
||||||
|
title="列表视图"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="table" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="view-btn card-view"
|
||||||
|
:class="{'active': viewMode === 'card'}"
|
||||||
|
@click="handleViewSwitch('card')"
|
||||||
|
title="卡片视图"
|
||||||
|
>
|
||||||
|
<svg-icon icon-class="dashboard" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<div class="content-area">
|
||||||
|
<!-- 列表视图 -->
|
||||||
|
<VehicleTable
|
||||||
|
v-if="viewMode === 'list'"
|
||||||
|
:vehicleData="filteredVehicleData"
|
||||||
|
:filters="filters"
|
||||||
|
@view="viewVehicleDetail"
|
||||||
|
@control="controlVehicle"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
:hide-search="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 卡片视图 -->
|
||||||
|
<VehicleCards
|
||||||
|
v-else
|
||||||
|
:vehicleData="filteredVehicleData"
|
||||||
|
:filters="filters"
|
||||||
|
@view="viewVehicleDetail"
|
||||||
|
@control="controlVehicle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -39,11 +104,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineEmits } from 'vue';
|
import { ref, defineEmits, computed } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { Close } from '@element-plus/icons-vue';
|
import { Close, Search } from '@element-plus/icons-vue';
|
||||||
import VehicleStats from '../../../components/car/VehicleStats.vue';
|
import VehicleStats from '../../../components/car/VehicleStats.vue';
|
||||||
import VehicleTable from '../../../components/car/VehicleTable.vue';
|
import VehicleTable from '../../../components/car/VehicleTable.vue';
|
||||||
|
import VehicleCards from '../../../components/car/VehicleCards.vue';
|
||||||
import VehicleDetail from './detail.vue';
|
import VehicleDetail from './detail.vue';
|
||||||
import { useVehicle } from '../../../composables/useVehicle';
|
import { useVehicle } from '../../../composables/useVehicle';
|
||||||
|
|
||||||
@ -60,11 +126,16 @@ const {
|
|||||||
exportData
|
exportData
|
||||||
} = useVehicle();
|
} = useVehicle();
|
||||||
|
|
||||||
|
// 搜索相关
|
||||||
|
const searchText = ref('');
|
||||||
|
const vehicleType = ref('');
|
||||||
|
const vehicleStatus = ref('');
|
||||||
|
|
||||||
// 统计数据选中索引
|
// 统计数据选中索引
|
||||||
const activeStatIndex = ref(0);
|
const activeStatIndex = ref(0);
|
||||||
|
|
||||||
// 地图相关
|
// 视图模式 - 列表或卡片
|
||||||
const showMap = ref(false);
|
const viewMode = ref('list');
|
||||||
|
|
||||||
// 表格选中行
|
// 表格选中行
|
||||||
const selectedRows = ref([]);
|
const selectedRows = ref([]);
|
||||||
@ -73,11 +144,6 @@ const selectedRows = ref([]);
|
|||||||
const showDetail = ref(false);
|
const showDetail = ref(false);
|
||||||
const currentVehicle = ref(null);
|
const currentVehicle = ref(null);
|
||||||
|
|
||||||
// 切换地图显示
|
|
||||||
function toggleMap() {
|
|
||||||
showMap.value = !showMap.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查看车辆详情 - 点击查看按钮时触发
|
// 查看车辆详情 - 点击查看按钮时触发
|
||||||
function viewVehicleDetail(vehicle) {
|
function viewVehicleDetail(vehicle) {
|
||||||
currentVehicle.value = vehicle;
|
currentVehicle.value = vehicle;
|
||||||
@ -89,6 +155,11 @@ function closeDetail() {
|
|||||||
showDetail.value = false;
|
showDetail.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理视图切换
|
||||||
|
function handleViewSwitch(type) {
|
||||||
|
viewMode.value = type;
|
||||||
|
}
|
||||||
|
|
||||||
// 远程控制车辆
|
// 远程控制车辆
|
||||||
function controlVehicle(vehicle) {
|
function controlVehicle(vehicle) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
@ -125,21 +196,20 @@ function handleSelectionChange(rows) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理搜索
|
// 处理搜索
|
||||||
function handleSearch(keyword) {
|
function handleSearch() {
|
||||||
// 实现搜索逻辑
|
// 更新过滤条件
|
||||||
ElMessage({
|
updateFilters({
|
||||||
message: `搜索关键词: ${keyword}`,
|
searchText: searchText.value,
|
||||||
type: 'success'
|
vehicleType: vehicleType.value,
|
||||||
|
vehicleStatus: vehicleStatus.value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理过滤
|
|
||||||
function handleFilter(filterValues) {
|
|
||||||
updateFilters(filterValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置过滤器
|
// 重置过滤器
|
||||||
function resetFilter() {
|
function resetFilter() {
|
||||||
|
searchText.value = '';
|
||||||
|
vehicleType.value = '';
|
||||||
|
vehicleStatus.value = '';
|
||||||
resetFilters();
|
resetFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +238,17 @@ function exportVehicleData() {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
|
min-height: calc(100vh - 250px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-area {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
height: calc(100% - 70px); /* 减去搜索区域的高度 */
|
||||||
|
min-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-view {
|
.detail-view {
|
||||||
@ -177,6 +257,109 @@ function exportVehicleData() {
|
|||||||
animation: fadeIn 0.3s ease;
|
animation: fadeIn 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 搜索区域样式 */
|
||||||
|
.search-area {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: 1px solid #343744;
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-select {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.view-switcher {
|
||||||
|
display: flex;
|
||||||
|
background-color: #343744;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.view-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #2B3B5A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #409eff;
|
||||||
|
|
||||||
|
:deep(svg) {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(svg) {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #96A0B5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义输入框样式 */
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: #343744 !important;
|
||||||
|
color: #96A0B5 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
height: 36px !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select__wrapper) {
|
||||||
|
background: #343744 !important;
|
||||||
|
color: #96A0B5 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
height: 36px !important;
|
||||||
|
font-size: 15px;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button) {
|
||||||
|
background: #343744;
|
||||||
|
border-color: transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 36px;
|
||||||
|
|
||||||
|
&.el-button--primary {
|
||||||
|
background-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background: #2B3B5A;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.el-button--primary:hover, &.el-button--primary:focus {
|
||||||
|
background-color: #337ecc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 淡入淡出过渡效果 */
|
/* 淡入淡出过渡效果 */
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
@ -202,5 +385,22 @@ function exportVehicleData() {
|
|||||||
.detail-view {
|
.detail-view {
|
||||||
min-height: calc(100vh - 80px);
|
min-height: calc(100vh - 80px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-area {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.search-input,
|
||||||
|
.search-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user