Просмотр исходного кода

feat: 在场车辆统计页面——车牌卡片网格+点击详情+空闲车位/在场数统计

lq 1 месяц назад
Родитель
Сommit
6c56584629
2 измененных файлов с 81 добавлено и 39 удалено
  1. 9 0
      frontend/src/api/vehicle.js
  2. 72 39
      frontend/src/view/statistics/vehicle.vue

+ 9 - 0
frontend/src/api/vehicle.js

@@ -187,3 +187,12 @@ export const exitConfirm = (data) => {
     data
   })
 }
+
+// 在场车辆列表
+export const getInParkVehicles = (params) => {
+  return service({
+    url: '/vehicle/in-park',
+    method: 'get',
+    params
+  })
+}

+ 72 - 39
frontend/src/view/statistics/vehicle.vue

@@ -1,40 +1,51 @@
 <template>
   <div>
-    <el-form inline style="border-bottom:1px solid #e4e7ed">
-      <el-form-item label="区域:">
-        <el-select></el-select>
+    <el-form inline>
+      <el-form-item label="停车场">
+        <el-select v-model="lotID" clearable placeholder="全部" style="width:180px" @change="fetchData">
+          <el-option v-for="lot in lotList" :key="lot.ID" :label="lot.lot_name" :value="lot.ID" />
+        </el-select>
       </el-form-item>
-      <el-form-item>
-        <el-button icon="Refresh">刷新</el-button>
+      <el-form-item :label="$t.value.LicensePlateNo">
+        <el-input v-model="plateNumber" :placeholder="$t.value.carPlateInput" clearable style="width:160px" @keyup.enter="fetchData" />
       </el-form-item>
       <el-form-item>
-        <el-text type="success">空闲车位:</el-text>
+        <el-button type="primary" icon="Search" @click="fetchData">{{ $t.value.Search }}</el-button>
+        <el-button icon="Refresh" @click="fetchData">{{ $t.value.reload }}</el-button>
       </el-form-item>
       <el-form-item>
-        <el-text type="warning">在场车辆:</el-text>
+        <el-text type="success">空闲车位:{{ available }}</el-text>
       </el-form-item>
       <el-form-item>
-        <el-input/>
+        <el-text type="warning">在场车辆:{{ list.length }} 台</el-text>
       </el-form-item>
     </el-form>
-    <el-row style="margin-top: 10px">
+
+    <el-row :gutter="12">
       <el-col :span="17">
-        <div class="carBox">
-          <span>001</span>
+        <div class="car-grid">
+          <div v-for="(v, i) in list" :key="v.record_id" class="car-item" :class="{ active: selected === i }" @click="select(i)">
+            <span class="car-plate">{{ v.plate_number }}</span>
+            <span class="car-time">{{ formatStay(v.stay_minutes) }}</span>
+          </div>
+          <el-empty v-if="list.length === 0" description="暂无在场车辆" style="width:100%" />
         </div>
       </el-col>
-      <el-col :span="6" style="margin-left: 10px">
-        <div class="infoBox">
-          <el-form label-width="100px" label-position="left">
-            <el-form-item label="车辆:"></el-form-item>
-            <el-form-item label="车辆类型:"></el-form-item>
-            <el-form-item label="到期日期:"></el-form-item>
-            <el-form-item label="车主名称:"></el-form-item>
-            <el-form-item label="手机:"></el-form-item>
-            <el-form-item label="入场时间:"></el-form-item>
-            <el-form-item label="预计收费:"></el-form-item>
-            <el-form-item label="车位:"></el-form-item>
-          </el-form>
+      <el-col :span="7">
+        <div class="info-box" v-if="selected !== null">
+          <h4>车辆详情</h4>
+          <el-descriptions :column="1" border size="small">
+            <el-descriptions-item label="车牌">{{ cur.plate_number }}</el-descriptions-item>
+            <el-descriptions-item label="车辆类型">{{ cur.vehicle_type || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="车主">{{ cur.owner_name || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="手机">{{ cur.owner_phone || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="入场时间">{{ cur.entry_time }}</el-descriptions-item>
+            <el-descriptions-item label="停留时长">{{ formatStay(cur.stay_minutes) }}</el-descriptions-item>
+            <el-descriptions-item label="停车场">{{ cur.parking_lot || '-' }}</el-descriptions-item>
+          </el-descriptions>
+        </div>
+        <div class="info-box empty" v-else>
+          <el-empty description="点击车辆查看详情" :image-size="80" />
         </div>
       </el-col>
     </el-row>
@@ -42,23 +53,45 @@
 </template>
 
 <script setup>
+import { ref, computed, onMounted } from 'vue'
+import { getInParkVehicles } from '@/api/vehicle'
+import { obtainPullOverList } from '@/api/pullOver'
 
-</script>
+const lotID = ref(null), plateNumber = ref('')
+const list = ref([]), lotList = ref([]), selected = ref(null)
+const cur = computed(() => selected.value !== null ? list.value[selected.value] : {})
 
-<style scoped lang="scss">
-.infoBox {
-  width: 400px;
-  height: 700px;
-  background-color: #f7f9fa;
-  padding: 20px;
-  border: 1px solid #99a9bf;
-}
-.carBox {
-  width: 70px;
-  height: 100px;
-  border: 1px solid #000000;
-  display:flex;
-  align-items: center;
-  justify-content: center;
+const available = computed(() => {
+  const total = lotList.value.reduce((s, l) => s + (l.capacity || 0), 0)
+  return total - list.value.length
+})
+
+function formatStay(m) { if (!m && m !== 0) return '-'; const h = Math.floor(m / 60); return h > 0 ? `${h}h${m % 60}m` : `${m}m` }
+
+function select(i) { selected.value = selected.value === i ? null : i }
+
+async function fetchData() {
+  try {
+    const [vehicles, lots] = await Promise.all([
+      getInParkVehicles({ parking_lot_id: lotID.value || '', plate_number: plateNumber.value }),
+      obtainPullOverList({ page: 1, pageSize: 100 })
+    ])
+    if (vehicles.code === 0) list.value = vehicles.data || []
+    if (lots.code === 0) lotList.value = lots.data?.list || []
+  } catch (e) { console.error(e) }
 }
+
+onMounted(() => fetchData())
+</script>
+
+<style scoped>
+.car-grid { display: flex; flex-wrap: wrap; gap: 8px; min-height: 200px; align-content: flex-start; }
+.car-item { width: 110px; height: 70px; border: 1px solid #dcdfe6; border-radius: 6px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; transition: all .2s; background: #fff; }
+.car-item:hover { border-color: #409eff; box-shadow: 0 2px 8px rgba(64,158,255,.15); }
+.car-item.active { border-color: #409eff; background: #ecf5ff; }
+.car-plate { font-weight: 700; font-size: 15px; }
+.car-time { font-size: 11px; color: #909399; margin-top: 4px; }
+.info-box { border: 1px solid #e4e7ed; border-radius: 6px; padding: 16px; background: #fafafa; min-height: 400px; }
+.info-box.empty { display: flex; align-items: center; justify-content: center; }
+.info-box h4 { margin: 0 0 12px; font-size: 15px; }
 </style>