Преглед изворни кода

进出记录管理,前后端. 新增一些查询条件

xu пре 3 месеци
родитељ
комит
6453612954

+ 11 - 13
server/api/v1/vehicle/vehicle_record.go

@@ -3,6 +3,7 @@ package vehicle
 import (
 	"fmt"
 	"server/model/common/response"
+	"server/model/vehicle/request"
 	vehicleResponse "server/model/vehicle/response"
 
 	"github.com/gin-gonic/gin"
@@ -58,23 +59,20 @@ func ListVehicleRecordsByPlateNumber(c *gin.Context) {
 
 // ListVehicleRecords 获取所有车辆记录列表
 func ListVehicleRecords(c *gin.Context) {
-	page := 1
-	pageSize := 10
-	if p := c.Query("page"); p != "" {
-		fmt.Sscanf(p, "%d", &page)
-	}
-	if ps := c.Query("page_size"); ps != "" {
-		fmt.Sscanf(ps, "%d", &pageSize)
-	}
+	var info request.VehicleRecordRequest
+
+	err := c.ShouldBindJSON(&info)
 
-	total, list, err := vehicleRecordService.ListVehicleRecords(page, pageSize)
+	list, total, err := vehicleRecordService.ListVehicleRecords(info)
 	if err != nil {
 		response.FailWithMessage(err.Error(), c)
 		return
 	}
 
-	response.OkWithData(vehicleResponse.VehicleRecordList{
-		Total: total,
-		List:  list,
-	}, c)
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.Page,
+		PageSize: info.PageSize,
+	}, "获取成功", c)
 }

+ 11 - 1
server/dao/shortlist.go

@@ -18,7 +18,7 @@ func (Shortlist) TableName() string {
 }
 
 func QueryShortlistList(limit, offset int, listType, plateNumber, rfidTag string) (list []Shortlist, total int64, err error) {
-	db := global.GVA_DB.Debug().Model(&Shortlist{}).Preload("Vehicle") // 预加载车辆信息
+	db := global.GVA_DB.Model(&Shortlist{}).Preload("Vehicle") // 预加载车辆信息
 
 	// 1. 按名单类型筛选:black / white
 	if listType != "" {
@@ -52,6 +52,16 @@ func QueryAllShortlists() (list []Shortlist, err error) {
 	return list, err
 }
 
+func QueryShortlistByRFIDTag(rfidTag string) (shortlist Shortlist, err error) {
+	err = global.GVA_DB.Model(&Shortlist{}).
+		Preload("Vehicle").
+		Joins("LEFT JOIN vehicle ON shortlist.vehicle_id = vehicle.id").
+		Where("vehicle.rfid_tag = ?", rfidTag).
+		First(&shortlist).Error
+
+	return
+}
+
 func CreateShortlist(shortlist Shortlist) error {
 	return global.GVA_DB.Create(&shortlist).Error
 }

+ 7 - 0
server/model/vehicle/request/vehicle.go

@@ -64,3 +64,10 @@ type VehicleExitResponse struct {
 		PaymentStatus string  `json:"payment_status"`
 	} `json:"data"`
 }
+
+type VehicleRecordRequest struct {
+	PlateNumber string `form:"plate_number" json:"plate_number"`
+	RFIDTag     string `form:"rfid_tag" json:"rfid_tag"`
+	Page        int    `form:"page,default=1" json:"page"`
+	PageSize    int    `form:"page_size,default=10" json:"page_size"`
+}

+ 1 - 1
server/router/vehicle/vehicle.go

@@ -33,6 +33,6 @@ func SetupVehicleRouter(router *gin.RouterGroup) {
 		// 车辆记录
 		vehicleGroup.GET("/record/get", vehicle.GetVehicleRecordByID)
 		vehicleGroup.GET("/record/list-by-plate", vehicle.ListVehicleRecordsByPlateNumber)
-		vehicleGroup.GET("/record/list", vehicle.ListVehicleRecords)
+		vehicleGroup.POST("/record/list", vehicle.ListVehicleRecords)
 	}
 }

+ 4 - 0
server/service/parking/booth.go

@@ -109,6 +109,10 @@ func (s *boothService) ListBooths(req request.BoothQuery) (int64, []dao.Booth, e
 		query = query.Where("booth_name LIKE ?", "%"+req.BoothName+"%")
 	}
 
+	if req.BoothName != "" {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
 	// 计算总数
 	query.Count(&total)
 

+ 4 - 0
server/service/parking/channel.go

@@ -111,6 +111,10 @@ func (s *channelService) ListChannels(req request.ChannelQuery) (int64, []dao.Ch
 		query = query.Where("direction = ?", req.Direction)
 	}
 
+	if req.ParkingLotID != 0 {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
 	// 计算总数
 	query.Count(&total)
 

+ 36 - 18
server/service/uhf/reader.go

@@ -224,14 +224,15 @@ func parseReportData(deviceCode string, buf []byte) (*ReportData, error) {
 	}, nil
 }
 
+// 防抖与状态
 var (
-	epcStatus      = make(map[string]bool)
 	epcLastTime    = make(map[string]int64)
-	epcMutex       sync.RWMutex
+	epcStatus      = make(map[string]bool)
+	epcMutex       sync.Mutex
 	debounceSecond = int64(3)
 )
 
-// ===================== 核心:自动开闸 + 自动关闸 + 临时车权限判断 =====================
+// handleReportData 处理UHF读取到的标签 + 完整权限判断
 func handleReportData(report *ReportData) {
 	if len(report.Epcs) == 0 {
 		return
@@ -253,7 +254,7 @@ func handleReportData(report *ReportData) {
 		return
 	}
 
-	// ===================== 查询设备 + 通道(包含是否允许临时车)=====================
+	// ===================== 查询设备 + 通道 =====================
 	var device dao.UHFReader
 	err := global.GVA_DB.Preload("Channel").First(&device, "device_code = ?", report.DeviceCode).Error
 	if err != nil {
@@ -261,32 +262,51 @@ func handleReportData(report *ReportData) {
 	}
 	channel := device.Channel
 	direction := channel.Direction
-	allowTemporary := channel.AllowTemporary // ✅ 是否允许临时车
+	allowTemporary := channel.AllowTemporary
 
 	vehicleService := service.ServiceGroupApp.VehicleServiceGroup.VehicleService
+	shortlistService := service.ServiceGroupApp.VehicleServiceGroup.ShortlistService // 黑白名单服务
 
-	// ===================== 根据EPC查询车辆信息 =====================
+	// ===================== 1. 根据EPC查询车辆 =====================
 	vehicle, err := vehicleService.GetVehicleByPlateNumber("", epc)
 	isTempVehicle := false
 	if err != nil || vehicle.ID == 0 {
-		// 查不到 = 临时车
 		isTempVehicle = true
 	} else {
-		// 根据车辆类型判断
 		isTempVehicle = (vehicle.VehicleType == "临时车")
 	}
 
-	// ===================== 禁止不允许的临时车 =====================
-	if isTempVehicle && !allowTemporary {
-		fmt.Printf("🚫 禁止通行 | 临时车不允许此通道 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+	// ===================== 2. 查询黑白名单 =====================
+	isBlack, isWhite := shortlistService.CheckVehicleShortlist(epc)
+
+	// ===================== 3. 黑名单优先判断:无论什么车,黑名单一律禁止 =====================
+	if isBlack {
+		fmt.Printf("🚫 禁止通行 | 车辆在黑名单中 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
 		epcLastTime[epc] = now
 		return
 	}
 
-	// ===================== 入口 =====================
+	// ===================== 4. 临时车规则 =====================
+	if isTempVehicle {
+		// 通道不允许临时车 + 不在白名单 → 禁止
+		if !allowTemporary && !isWhite {
+			fmt.Printf("🚫 禁止通行 | 临时车不允许此通道且不在白名单 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+			epcLastTime[epc] = now
+			return
+		}
+
+		// 临时车 + 不在白名单 → 禁止
+		if !isWhite {
+			fmt.Printf("🚫 禁止通行 | 临时车不在白名单 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
+			epcLastTime[epc] = now
+			return
+		}
+	}
+
+	// ===================== 5. 入口 =====================
 	if direction == "in" {
 		if !epcStatus[epc] {
-			fmt.Printf("🟢 入口开闸 | 设备:%s EPC:%s 临时车:%v\n", report.DeviceCode, epc, isTempVehicle)
+			fmt.Printf("🟢 入口开闸 | 设备:%s EPC:%s 临时车:%v 白名单:%v\n", report.DeviceCode, epc, isTempVehicle, isWhite)
 
 			// 开闸
 			handler.reader.CloseRelay1(2)
@@ -297,16 +317,14 @@ func handleReportData(report *ReportData) {
 			vehicleService.VehicleEntry(vehicleEntry)
 
 			epcStatus[epc] = true
-		} else {
-			fmt.Printf("✅ 已在场 | 设备:%s EPC:%s\n", report.DeviceCode, epc)
 		}
 	}
 
-	// ===================== 出口 =====================
+	// ===================== 6. 出口 =====================
 	if direction == "out" {
-		fmt.Printf("🔴 出口开闸(放行)| 设备:%s EPC:%s 临时车:%v\n", report.DeviceCode, epc, isTempVehicle)
+		fmt.Printf("🔴 出口开闸 | 设备:%s EPC:%s 临时车:%v 白名单:%v\n", report.DeviceCode, epc, isTempVehicle, isWhite)
 
-		// 出口开闸放行
+		// 开闸
 		handler.reader.CloseRelay1(2)
 
 		// 记录出场

+ 4 - 0
server/service/uhf/uhf_service.go

@@ -142,6 +142,10 @@ func (s *uhfService) ListReaders(req request.UHFReaderQuery) ([]response.UHFRead
 		query = query.Where("connect_type = ?", req.ConnectType)
 	}
 
+	if req.ParkingLotID != 0 {
+		query = query.Where("parking_lot_id = ?", req.ParkingLotID)
+	}
+
 	err := query.Count(&total).Error
 	if err != nil {
 		return nil, 0, err

+ 23 - 0
server/service/vehicle/shortlist.go

@@ -2,6 +2,7 @@ package vehicle
 
 import (
 	"server/dao"
+	"server/global"
 	"server/model/vehicle/request"
 )
 
@@ -22,6 +23,28 @@ func (shortlistService *ShortlistService) QueryAllShortlists() (shortlists []dao
 	return dao.QueryAllShortlists()
 }
 
+func (shortlistService *ShortlistService) QueryShortlistByRFIDTag(rfidTag string) (shortlist dao.Shortlist, err error) {
+	return dao.QueryShortlistByRFIDTag(rfidTag)
+}
+
+func (s *ShortlistService) CheckVehicleShortlist(rfid string) (isBlack bool, isWhite bool) {
+	var vehicle dao.Vehicle
+	err := global.GVA_DB.Where("rfid_tag = ?", rfid).First(&vehicle).Error
+	if err != nil {
+		return false, false
+	}
+
+	var shortlist dao.Shortlist
+	err = global.GVA_DB.Where("vehicle_id = ?", vehicle.ID).First(&shortlist).Error
+	if err != nil {
+		return false, false
+	}
+
+	isBlack = shortlist.ListType == "黑名单"
+	isWhite = shortlist.ListType == "白名单"
+	return
+}
+
 func (shortlistService *ShortlistService) CreateShortlist(shortlist dao.Shortlist) error {
 	return dao.CreateShortlist(shortlist)
 }

+ 21 - 12
server/service/vehicle/vehicle_record.go

@@ -3,6 +3,7 @@ package vehicle
 import (
 	"server/dao"
 	"server/global"
+	"server/model/vehicle/request"
 )
 
 type VehicleRecordService struct{}
@@ -38,21 +39,29 @@ func (s *VehicleRecordService) ListVehicleRecordsByPlateNumber(plateNumber strin
 }
 
 // ListVehicleRecords 获取所有车辆记录列表
-func (s *VehicleRecordService) ListVehicleRecords(page, pageSize int) (int64, []dao.VehicleRecord, error) {
-	var records []dao.VehicleRecord
-	var total int64
+func (s *VehicleRecordService) ListVehicleRecords(info request.VehicleRecordRequest) (list []dao.VehicleRecord, total int64, err error) {
+	db := global.GVA_DB.Debug().Model(&dao.VehicleRecord{})
 
-	query := global.GVA_DB.Model(&dao.VehicleRecord{})
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
 
-	// 计算总数
-	query.Count(&total)
+	// 车牌号模糊查询
+	if info.PlateNumber != "" {
+		db = db.Where("plate_number LIKE ?", "%"+info.PlateNumber+"%")
+	}
 
-	// 分页查询
-	offset := (page - 1) * pageSize
-	result := query.Order("entry_time DESC").Offset(offset).Limit(pageSize).Find(&records)
-	if result.Error != nil {
-		return 0, nil, result.Error
+	// RFID标签精确查询
+	if info.RFIDTag != "" {
+		db = db.Where("rfid_tag LIKE ?", "%"+info.RFIDTag+"%")
 	}
 
-	return total, records, nil
+	// 查询总数
+	err = db.Count(&total).Error
+	if err != nil {
+		return nil, 0, err
+	}
+
+	// 分页查询 + 排序
+	err = db.Order("entry_time DESC").Limit(limit).Offset(offset).Find(&list).Error
+	return list, total, err
 }

+ 8 - 0
web/src/api/vehicle.js

@@ -110,3 +110,11 @@ export const obtainChargeList = (id) => {
     method: 'get'
   })
 }
+
+export const queryVehicleRecordList = (data) => {
+  return service({
+    url: '/vehicle/record/list',
+    method: 'post',
+    data
+  })
+}

+ 293 - 191
web/src/view/parking/carInfo.vue

@@ -1,269 +1,371 @@
 <template>
   <div>
+    <!-- 搜索表单 -->
     <el-form inline>
       <el-form-item label="车牌号">
         <el-input
-            placeholder="请输入车牌号"
-            v-model="infoSearchData.plate_number"
-            clearable
-            @clear="infoSearchData.plate_number = ''"/>
+          v-model="queryForm.plate_number"
+          placeholder="请输入车牌号"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="车辆类型">
+        <el-select
+          v-model="queryForm.vehicle_type"
+          placeholder="全部"
+          clearable
+        >
+          <el-option
+            label="月租车"
+            value="月租车"
+          />
+          <el-option
+            label="临时车"
+            value="临时车"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item label="车主">
-        <el-select v-model="carOwnerName" @change="changeSearchCarOwner" clearable @clear="clearCarOwner">
+        <el-select
+          v-model="queryForm.owner_id"
+          clearable
+          placeholder="请选择车主"
+          style="width: 180px"
+        >
           <el-option
-              v-for="item in carOwnerList"
-              :key="item.ID"
-              :label="item.owner_name"
-              :value="item.owner_name"
+            v-for="item in ownerList"
+            :key="item.ID"
+            :label="item.owner_name"
+            :value="item.ID"
           />
         </el-select>
       </el-form-item>
+
       <el-form-item>
-        <el-button icon="Search" type="primary" @click="getCarInfoList">搜索</el-button>
+        <el-button
+          icon="Search"
+          type="primary"
+          @click="getVehicleList"
+        >搜索</el-button>
+        <el-button
+          icon="Refresh"
+          @click="resetQuery"
+        >重置</el-button>
       </el-form-item>
       <el-form-item>
-        <el-button icon="plus" type="primary" @click="openCarInfo('新增车辆信息', '')">新增</el-button>
+        <el-button
+          icon="Plus"
+          type="primary"
+          @click="openDialog('新增车辆', null)"
+        >新增</el-button>
       </el-form-item>
     </el-form>
-    <el-table :data="carInfoList" height="605">
-      <el-table-column label="车主id" align="center" prop="owner_id">
-      </el-table-column>
-      <el-table-column label="车牌号" align="center" prop="plate_number">
-      </el-table-column>
-      <el-table-column label="车辆类型" align="center" prop="vehicle_type">
-      </el-table-column>
-      <el-table-column label="车辆品牌" align="center" prop="vehicle_brand">
+
+    <!-- 车辆列表 -->
+    <el-table
+      :data="vehicleList"
+      height="605"
+      border
+    >
+      <el-table-column
+        label="ID"
+        align="center"
+        prop="ID"
+        width="80"
+      />
+      <el-table-column
+        label="车牌号"
+        align="center"
+        prop="plate_number"
+      />
+      <el-table-column
+        label="电子标签"
+        align="center"
+        prop="rfid_tag"
+      />
+      <el-table-column
+        label="车辆类型"
+        align="center"
+        prop="vehicle_type"
+      >
+        <template #default="scope">
+          <el-tag :type="scope.row.vehicle_type === '月租车' ? 'primary' : 'success'">
+            {{ scope.row.vehicle_type }}
+          </el-tag>
+        </template>
       </el-table-column>
-      <el-table-column label="车辆颜色" align="center" prop="vehicle_color">
+      <el-table-column
+        label="品牌"
+        align="center"
+        prop="vehicle_brand"
+      />
+      <el-table-column
+        label="颜色"
+        align="center"
+        prop="vehicle_color"
+      />
+      <el-table-column
+        label="车主"
+        align="center"
+      >
+        <template #default="scope">
+          {{ scope.row.owner?.owner_name || "临时车辆" }}
+        </template>
       </el-table-column>
-      <el-table-column label="操作" align="center">
+
+      <el-table-column
+        label="操作"
+        align="center"
+        width="180"
+      >
         <template #default="scope">
-          <el-button icon="edit" type="primary" text @click="openCarInfo('编辑车辆信息', scope.row)">编辑</el-button>
-          <el-button icon="delete" type="primary" text @click="deleteCarInfo(scope.row)">删除</el-button>
+          <el-button
+            icon="Edit"
+            type="primary"
+            text
+            @click="openDialog('编辑车辆', scope.row)"
+          >编辑</el-button>
+          <el-button
+            icon="Delete"
+            type="danger"
+            text
+            @click="deleteVehicle(scope.row.ID)"
+          >删除</el-button>
         </template>
       </el-table-column>
     </el-table>
-    <div style="width: 100%;display: flex;justify-content: end">
+
+    <!-- 分页 -->
+    <div class="pagination-box">
       <el-pagination
-          :current-page="infoSearchData.page"
-          :page-size="infoSearchData.page_size"
-          :page-sizes="[10, 30, 50, 100]"
-          :total="carInfoTotal"
-          layout="total, sizes, prev, pager, next, jumper"
-          @current-change="handleCurrentChange"
-          @size-change="handleSizeChange"
-          style="padding-right: 10px"
+        :current-page="queryForm.page"
+        :page-size="queryForm.pageSize"
+        :page-sizes="[10, 30, 50, 100]"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @current-change="handlePageChange"
+        @size-change="handleSizeChange"
       />
     </div>
-    <el-dialog v-model="carInfoShow" :title="carInfoTitle" width="500">
-      <el-form label-width="90">
+
+    <!-- 新增/编辑弹窗 -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogTitle"
+      width="520px"
+    >
+      <el-form
+        label-width="100px"
+        :model="formData"
+      >
         <el-form-item label="车牌号:">
-          <el-input v-model="carInfoData.plate_number"/>
+          <el-input
+            v-model="formData.plate_number"
+            placeholder="请输入车牌号"
+          />
+        </el-form-item>
+        <el-form-item label="电子标签:">
+          <el-input
+            v-model="formData.rfid_tag"
+            placeholder="RFID标签(UHF用)"
+          />
         </el-form-item>
         <el-form-item label="车辆类型:">
-          <el-select v-model="carInfoData.vehicle_type">
-            <el-option label="月租车" value="月租车"/>
-            <el-option label="临时车" value="临时车"/>
+          <el-select
+            v-model="formData.vehicle_type"
+            placeholder="请选择"
+          >
+            <el-option
+              label="月租车"
+              value="月租车"
+            />
+            <el-option
+              label="临时车"
+              value="临时车"
+            />
           </el-select>
         </el-form-item>
         <el-form-item label="车辆品牌:">
-          <el-input v-model="carInfoData.vehicle_brand"/>
+          <el-input
+            v-model="formData.vehicle_brand"
+            placeholder="如:保时捷"
+          />
         </el-form-item>
         <el-form-item label="车辆颜色">
-          <el-input v-model="carInfoData.vehicle_color"/>
+          <el-input
+            v-model="formData.vehicle_color"
+            placeholder="如:黑色"
+          />
         </el-form-item>
-        <el-form-item label="车主:">
-          <el-select v-model="ownerName" value-key="ID" @change="changeCarOwner">
+        <el-form-item label="绑定车主:">
+          <el-select
+            v-model="formData.owner_id"
+            placeholder="临时车可不选"
+            clearable
+            style="width: 100%"
+          >
             <el-option
-                v-for="item in carOwnerList"
-                :key="item.ID"
-                :label="item.owner_name"
-                :value="item.owner_name"/>
+              v-for="item in ownerList"
+              :key="item.ID"
+              :label="item.owner_name"
+              :value="item.ID"
+            />
           </el-select>
         </el-form-item>
-        <el-form-item label="电子标签:">
-          <el-input v-model="carInfoData.rfid_tag"/>
-        </el-form-item>
       </el-form>
+
       <template #footer>
-        <el-button @click="carInfoShow = false">取消</el-button>
-        <el-button @click="carInfoOperation()" type="primary">确认</el-button>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm"
+        >确认保存</el-button>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from "vue"
-import { addCarInfo, editCarInfo, obtainCarInfoList, removeCarInfo, carInfoPhone } from '@/api/carInfo'
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+import {
+  obtainVehicleList,
+  addVehicle,
+  editVehicle,
+  removeVehicleList
+} from '@/api/vehicle'
+// 车主接口
 import { obtainCarOwnerList } from '@/api/carOwner'
-import { ElMessage, ElMessageBox } from "element-plus";
-const carOwnerName = ref('')
-const carOwnerList = reactive([])
-const carInfoTitle = ref('')
-const carInfoShow = ref(false)
-const carInfoData = reactive({
-  id:0,
+
+// 搜索参数
+const queryForm = reactive({
   plate_number: '',
   vehicle_type: '',
-  vehicle_brand: '',
-  vehicle_color: '',
-  owner_id: 0,
-  rfid_tag: '17'
+  owner_id: null,
+  page: 1,
+  pageSize: 10
 })
 
-const infoSearchData = reactive({
+// 车辆列表
+const vehicleList = ref([])
+const total = ref(0)
+// 车主列表
+const ownerList = ref([])
+
+// 弹窗
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formData = reactive({
+  ID: 0,
   plate_number: '',
+  rfid_tag: '',
   vehicle_type: '',
-  owner_id: 0,
-  page: 1,
-  page_size: 10
+  vehicle_brand: '',
+  vehicle_color: '',
+  owner_id: null // 支持为空(临时车)
 })
 
-const ownerName = ref('')
-const carInfoList = reactive([])
-
-const carInfoTotal = ref(0)
-const openCarInfo = (head,data) => {
-  carInfoTitle.value = head
-  carInfoShow.value = true
-  if (head === '编辑车辆信息') {
-    carInfoData.id = data.ID
-    carInfoData.owner_id = data.owner_id
-    carInfoData.plate_number = data.plate_number
-    carInfoData.vehicle_type = data.vehicle_type
-    carInfoData.vehicle_brand = data.vehicle_brand
-    carInfoData.vehicle_color = data.vehicle_color
-    carInfoData.rfid_tag = data.rfid_tag
-    carOwnerList.forEach(item => {
-      if (item.ID === data.owner_id) {
-        ownerName.value = item.owner_name
-      }
-    })
-  } else {
-    carInfoData.plate_number = ''
-    carInfoData.vehicle_type = ''
-    carInfoData.vehicle_brand = ''
-    carInfoData.vehicle_color = ''
-    carInfoData.owner_id = 0
-    carInfoData.rfid_tag = ''
-    ownerName.value = ''
-  }
-}
-const changeSearchCarOwner = (val) => {
-  carOwnerList.forEach(item => {
-    if (item.owner_name === val) {
-      infoSearchData.owner_id = item.ID
+// ============== 车辆列表 ==============
+const getVehicleList = async() => {
+  try {
+    const res = await obtainVehicleList(queryForm)
+    if (res.code === 0) {
+      vehicleList.value = res.data.list
+      total.value = res.data.total
     }
-  })
-  console.log(infoSearchData.owner_id)
+  } catch (err) {
+    ElMessage.error('获取列表失败')
+  }
 }
 
-const clearCarOwner = () => {
-  infoSearchData.owner_id = 0
-  ownerName.value = ''
+// 重置搜索
+const resetQuery = () => {
+  queryForm.plate_number = ''
+  queryForm.vehicle_type = ''
+  queryForm.owner_id = null
+  queryForm.page = 1
+  getVehicleList()
 }
 
-const changeCarOwner = (val) => {
-  carOwnerList.forEach(item => {
-    if (item.owner_name === val) {
-      ownerName.value = item.owner_name
-      carInfoData.owner_id = item.ID
-    }
-  })
-}
-
-const carInfoOperation = () => {
-  if (carInfoTitle.value === '新增车辆信息') {
-    addCarInfo(carInfoData).then(res => {
-      if (res.code === 0) {
-        ElMessage.success('添加成功')
-        carInfoShow.value = false
-        getCarInfoList()
-      }
-    })
-  } else {
-    editCarInfo(carInfoData).then(res => {
-      console.log(res)
-      if (res.code === 0) {
-        ElMessage.success('更新成功')
-        carInfoShow.value = false
-        getCarInfoList()
-      }
+// ============== 弹窗操作 ==============
+const openDialog = (title, row) => {
+  dialogTitle.value = title
+  dialogVisible.value = true
+  // 重置表单
+  if (!row) {
+    Object.assign(formData, {
+      ID: 0, plate_number: '', rfid_tag: '',
+      vehicle_type: '', vehicle_brand: '',
+      vehicle_color: '', owner_id: null
     })
+    return
   }
+  // 编辑回显
+  Object.assign(formData, row)
 }
 
-const deleteCarInfo = (data) => {
-  ElMessageBox.confirm(
-      '确定要删除该车辆信息吗?',
-      '提示',
-      {
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-  )
-      .then(() => {
-        removeCarInfo(data.ID).then(res => {
-          if (res.code === 0) {
-            ElMessage({
-              type: 'success',
-              message: '删除成功',
-            })
-            getCarInfoList()
-          } else {
-            ElMessage({
-              type: 'error',
-              message: '删除失败',
-            })
-          }
-        })
-      })
-      .catch(() => {
-        ElMessage({
-          type: 'info',
-          message: '取消删除',
-        })
-      })
-}
-
-const getCarInfoList = () => {
-  obtainCarInfoList(infoSearchData).then(res => {
-    if (res.code === 0) {
-      carInfoList.length = 0
-      carInfoList.push(...res.data.list)
-      carInfoTotal.value = res.data.total
+// 提交保存
+const submitForm = async() => {
+  if (!formData.plate_number) {
+    ElMessage.warning('请输入车牌号')
+    return
+  }
+  try {
+    if (formData.ID > 0) {
+      await editVehicle(formData)
+      ElMessage.success('编辑成功')
+    } else {
+      await addVehicle(formData)
+      ElMessage.success('新增成功')
     }
-  })
+    dialogVisible.value = false
+    getVehicleList()
+  } catch (err) {
+    ElMessage.error('保存失败')
+  }
 }
 
-const getCarOwnerList = () => {
-  obtainCarOwnerList().then(res => {
-    if (res.code === 0) {
-      carOwnerList.length = 0
-      carOwnerList.push(...res.data.list)
-    }
-  })
+// ============== 删除 ==============
+const deleteVehicle = async(id) => {
+  await ElMessageBox.confirm('确定删除该车辆?', '提示', { type: 'warning' })
+  try {
+    await removeVehicleList({ id })
+    ElMessage.success('删除成功')
+    getVehicleList()
+  } catch (err) {
+    ElMessage.error('删除失败')
+  }
 }
 
-const handleCurrentChange = (data) => {
-  infoSearchData.page = data
-  getCarInfoList()
+// ============== 分页 ==============
+const handlePageChange = (page) => {
+  queryForm.page = page
+  getVehicleList()
+}
+const handleSizeChange = (size) => {
+  queryForm.pageSize = size
+  queryForm.page = 1
+  getVehicleList()
 }
 
-const handleSizeChange = (data) => {
-  infoSearchData.page_size = data
-  infoSearchData.page = 1
-  getCarInfoList()
+// ============== 初始化 ==============
+const getOwnerList = async() => {
+  const res = await obtainCarOwnerList()
+  if (res.code === 0) ownerList.value = res.data.list
 }
 
 onMounted(() => {
-  getCarInfoList()
-  getCarOwnerList()
+  getVehicleList()
+  getOwnerList()
 })
 </script>
 
 <style scoped lang="scss">
-
+.pagination-box {
+  width: 100%;
+  display: flex;
+  justify-content: end;
+  padding: 10px;
+}
 </style>

+ 190 - 120
web/src/view/report/enter.vue

@@ -1,153 +1,223 @@
 <template>
   <div>
+    <!-- 搜索表单 -->
     <el-form inline>
-      <el-form-item label="车牌:">
-        <el-input v-model="licensePlate"/>
+      <el-form-item label="车牌号码">
+        <el-input
+          v-model="queryForm.plate_number"
+          placeholder="请输入车牌"
+          clearable
+        />
       </el-form-item>
-<!--      <el-form-item label="ID:">-->
-<!--        <el-input v-model="enterId" />-->
-<!--      </el-form-item>-->
+
+      <el-form-item label="RFID标签">
+        <el-input
+          v-model="queryForm.rfid_tag"
+          placeholder="请输入RFID"
+          clearable
+        />
+      </el-form-item>
+
       <el-form-item>
-        <el-button type="primary" @click="conditionEnterList">搜索</el-button>
+        <el-button
+          type="primary"
+          @click="getRecordList"
+        >搜索</el-button>
+        <el-button @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
-    <el-row>
-      <el-col :span="24">
-        <el-table :data="enterList" height="605">
-          <el-table-column label="车牌">
-
-          </el-table-column>
-          <el-table-column label="车牌识别号码">
-
-          </el-table-column>
-          <el-table-column label="入场时间">
-
-          </el-table-column>
-          <el-table-column label="入场岗亭">
-
-          </el-table-column>
-          <el-table-column label="入场通道">
-
-          </el-table-column>
-          <el-table-column label="出场时间">
-
-          </el-table-column>
-          <el-table-column label="出场岗亭">
-
-          </el-table-column>
-          <el-table-column label="出场通道">
-
-          </el-table-column>
-          <el-table-column label="操作">
-            <template #default="scope">
-              <el-button type="primary" text icon="View" @click="openEnter(scope.row)">查看</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-        <div style="width: 100%;display: flex;justify-content: end">
-          <el-pagination
-              :current-page="enterCondition.page"
-              :page-size="enterCondition.page_size"
-              :page-sizes="[10, 30, 50, 100]"
-              :total="enterTotal"
-              layout="total, sizes, prev, pager, next, jumper"
-              @current-change="handleCurrentChange"
-              @size-change="handleSizeChange"
-              style="padding-right: 10px"
-          />
-        </div>
-      </el-col>
-    </el-row>
-    <el-dialog title="进出记录" v-model="enterShow">
-      <el-form>
-        <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-form inline label-position="top">
-        <el-form-item label="入场识别照片">
-          <el-image></el-image>
-        </el-form-item>
-        <el-form-item label="入场抓拍相机照片">
-          <el-image></el-image>
+
+    <!-- 表格 -->
+    <el-table
+      :data="recordList"
+      height="605"
+      border
+    >
+      <el-table-column
+        label="车牌"
+        prop="plate_number"
+        align="center"
+      />
+      <el-table-column
+        label="RFID标签"
+        prop="rfid_tag"
+        align="center"
+      />
+
+      <el-table-column
+        label="入场时间"
+        align="center"
+      >
+        <template #default="scope">
+          {{ formatTime(scope.row.entry_time) }}
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="出场时间"
+        align="center"
+      >
+        <template #default="scope">
+          {{ formatTime(scope.row.exit_time) || '未出场' }}
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="停留时长"
+        align="center"
+      >
+        <template #default="scope">
+          {{ scope.row.stay_time || 0 }} 分钟
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="费用"
+        align="center"
+      >
+        <template #default="scope">
+          ¥ {{ scope.row.fee || 0 }}
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="支付状态"
+        align="center"
+      >
+        <template #default="scope">
+          <el-tag :type="scope.row.payment_status === 'paid' ? 'success' : 'warning'">
+            {{ scope.row.payment_status === 'paid' ? '已支付' : '未支付' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="操作"
+        align="center"
+        width="100"
+      >
+        <template #default="scope">
+          <el-button
+            type="primary"
+            text
+            icon="View"
+            @click="openDetail(scope.row)"
+          >
+            查看
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <div style="display: flex; justify-content: end; margin-top:10px">
+      <el-pagination
+        v-model:current-page="queryForm.page"
+        v-model:page-size="queryForm.page_size"
+        :page-sizes="[10, 30, 50, 100]"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+      />
+    </div>
+
+    <!-- 查看详情弹窗 -->
+    <el-dialog
+      v-model="detailShow"
+      title="进出记录详情"
+      width="700px"
+    >
+      <el-form label-width="100px">
+        <el-form-item label="车牌">{{ detail.plate_number }}</el-form-item>
+        <el-form-item label="RFID">{{ detail.rfid_tag }}</el-form-item>
+        <el-form-item label="入场时间">{{ formatTime(detail.entry_time) }}</el-form-item>
+        <el-form-item label="出场时间">{{ formatTime(detail.exit_time) || '未出场' }}</el-form-item>
+        <el-form-item label="停留时长">{{ detail.stay_time || 0 }} 分钟</el-form-item>
+        <el-form-item label="费用">¥ {{ detail.fee || 0 }}</el-form-item>
+        <el-form-item label="支付状态">
+          {{ detail.payment_status === 'paid' ? '已支付' : '未支付' }}
         </el-form-item>
       </el-form>
-      <el-form inline label-position="top">
-        <el-form-item label="出场识别照片">
-          <el-image></el-image>
+
+      <!-- 图片展示 -->
+      <div style="display: flex; gap:10px; margin-top:10px">
+        <el-form-item label="入场图片">
+          <el-image
+            style="width: 200px; height:120px; object-fit:cover"
+            :src="detail.entry_image"
+            fit="cover"
+            alt="入场图片"
+          />
         </el-form-item>
-        <el-form-item label="出场抓拍相机照片">
-          <el-image></el-image>
+
+        <el-form-item label="出场图片">
+          <el-image
+            style="width: 200px; height:120px; object-fit:cover"
+            :src="detail.exit_image"
+            fit="cover"
+            alt="出场图片"
+          />
         </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="enterShow = false">取消</el-button>
-          <el-button type="primary" @click="enterShow = false">
-            确定
-          </el-button>
-        </div>
-      </template>
+      </div>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { reactive, ref, onMounted } from "vue"
-import { obtainEnterList, obtainEnterNumber } from "@/api/enter"
-const enterList = reactive([])
-const enterShow = ref(false)
-const enterCondition = reactive({
-  page:1,
+import { ref, reactive, onMounted } from 'vue'
+import { queryVehicleRecordList } from '@/api/vehicle' // 你的真实接口
+import { ElMessage } from 'element-plus'
+
+// 查询条件
+const queryForm = reactive({
+  plate_number: '',
+  rfid_tag: '',
+  page: 1,
   page_size: 10
 })
 
-const licensePlate = ref('')
-// const enterId = ref(0)
-const enterTotal = ref(0)
-const openEnter = (data) => {
-  console.log(data)
-  enterShow.value = true
+const recordList = ref([])
+const total = ref(0)
+const detailShow = ref(false)
+const detail = ref({})
+
+// 时间格式化
+const formatTime = (time) => {
+  if (!time) return ''
+  return time.replace('T', ' ').split('+')[0]
 }
-const getEnterList = async () => {
-  await obtainEnterList(enterCondition).then(res => {
+
+// 获取记录列表
+const getRecordList = async() => {
+  try {
+    const res = await queryVehicleRecordList(queryForm)
     if (res.code === 0) {
-      enterList.length = 0
-      enterTotal.value = res.data.total
-      enterList.push(...res.data.list)
+      recordList.value = res.data.list
+      total.value = res.data.total
     }
-  })
+  } catch (e) {
+    ElMessage.error('获取记录失败')
+  }
 }
 
-const handleCurrentChange = (data) => {
-  enterCondition.page = data
-  getEnterList()
+// 重置
+const resetQuery = () => {
+  queryForm.plate_number = ''
+  queryForm.rfid_tag = ''
+  queryForm.page = 1
+  getRecordList()
 }
 
-const handleSizeChange = (data) => {
-  enterCondition.page_size = data
-  enterCondition.page = 1
-  getEnterList()
-}
-
-// 条件检索
-const conditionEnterList = () => {
-  obtainEnterNumber(licensePlate.value).then(res => {
-    if (res.code === 0) {
-      enterList.length = 0
-      enterList.push(...res.data.list)
-      enterTotal.value = res.data.total
-    }
-  })
+// 打开详情
+const openDetail = (row) => {
+  detail.value = row
+  detailShow.value = true
 }
 
+// 初始化
 onMounted(() => {
-  getEnterList()
+  getRecordList()
 })
 </script>
 
-<style scoped lang="scss">
-
+<style scoped>
 </style>