فهرست منبع

黑白名单管理,前后端

xu 3 ماه پیش
والد
کامیت
3446ec1e28

+ 1 - 1
server/api/v1/vehicle/shortlist.go

@@ -22,7 +22,7 @@ func (sa *ShortlistApi) QueryAllShortlists(c *gin.Context) {
 
 func (sa *ShortlistApi) QueryShortlistList(c *gin.Context) {
 	var info request.ShortlistRequest
-	err := c.ShouldBindQuery(&info)
+	err := c.ShouldBindJSON(&info)
 	if err != nil {
 		response.FailWithMessage(err.Error(), c)
 		return

+ 9 - 0
server/api/v1/vehicle/vehicle.go

@@ -77,6 +77,15 @@ func GetVehicleByPlateNumber(c *gin.Context) {
 	response.OkWithData(vehicleInfo, c)
 }
 
+func QueryAllVehicles(c *gin.Context) {
+	vehicles, err := vehicleService.QueryAllVehicles()
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithData(vehicles, c)
+}
+
 // ListVehicles 获取车辆列表
 func ListVehicles(c *gin.Context) {
 	var req request.VehicleQuery

+ 2 - 2
server/router/vehicle/shortlist.go

@@ -19,7 +19,7 @@ func (s *ShortlistRouter) InitShortlistRouter(Router *gin.RouterGroup) {
 		shortlistRouter.PUT("updateShortlist", shortlistRouterShortlist.UpdateShortlist)    // 更新shortlist
 	}
 	{
-		shortlistRouterWithoutRecord.GET("queryAllShortlists", shortlistRouterShortlist.QueryAllShortlists) // 获取所有shortlist
-		shortlistRouterWithoutRecord.GET("queryShortlistList", shortlistRouterShortlist.QueryShortlistList) // 获取shortlist列表
+		shortlistRouterWithoutRecord.GET("queryAllShortlists", shortlistRouterShortlist.QueryAllShortlists)  // 获取所有shortlist
+		shortlistRouterWithoutRecord.POST("queryShortlistList", shortlistRouterShortlist.QueryShortlistList) // 获取shortlist列表
 	}
 }

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

@@ -15,6 +15,7 @@ func SetupVehicleRouter(router *gin.RouterGroup) {
 		vehicleGroup.PUT("/update", vehicle.UpdateVehicle)
 		vehicleGroup.GET("/get", vehicle.GetVehicleByID)
 		vehicleGroup.GET("/get-by-plate", vehicle.GetVehicleByPlateNumber)
+		vehicleGroup.GET("/all", vehicle.QueryAllVehicles)
 		vehicleGroup.GET("/list", vehicle.ListVehicles)
 		vehicleGroup.DELETE("/delete", vehicle.DeleteVehicle)
 

+ 10 - 1
server/service/vehicle/vehicle.go

@@ -90,6 +90,15 @@ func (s *VehicleService) GetVehicleByPlateNumber(plateNumber, RFIDTag string) (d
 	return vehicle, nil
 }
 
+func (s *VehicleService) QueryAllVehicles() ([]dao.Vehicle, error) {
+	var vehicles []dao.Vehicle
+	err := global.GVA_DB.Find(&vehicles).Error
+	if err != nil {
+		return vehicles, err
+	}
+	return vehicles, nil
+}
+
 // ListVehicles 获取车辆列表
 func (s *VehicleService) ListVehicles(req request.VehicleQuery) (int64, []dao.Vehicle, error) {
 	var vehicles []dao.Vehicle
@@ -114,7 +123,7 @@ func (s *VehicleService) ListVehicles(req request.VehicleQuery) (int64, []dao.Ve
 
 	// 分页查询
 	offset := (req.Page - 1) * req.PageSize
-	result := query.Offset(offset).Limit(req.PageSize).Find(&vehicles)
+	result := query.Offset(offset).Limit(req.PageSize).Preload("Owner").Find(&vehicles)
 	if result.Error != nil {
 		return 0, nil, result.Error
 	}

+ 44 - 0
web/src/api/shortlist.js

@@ -0,0 +1,44 @@
+import service from '@/utils/request'
+
+// 新增黑白名单
+export const addShortlist = (data) => {
+  return service({
+    url: '/shortlist/createShortlist',
+    method: 'post',
+    data
+  })
+}
+
+// 修改黑白名单
+export const updateShortlist = (data) => {
+  return service({
+    url: '/shortlist/updateShortlist',
+    method: 'put',
+    data
+  })
+}
+
+// 查询所有黑白名单
+export const queryAllShortlists = () => {
+  return service({
+    url: '/shortlist/queryAllShortlists',
+    method: 'get'
+  })
+}
+
+// 查询黑白名单
+export const queryShortlistList = (data) => {
+  return service({
+    url: '/shortlist/queryShortlistList',
+    method: 'post',
+    data
+  })
+}
+
+// 删除黑白名单
+export const deleteShortlist = (id) => {
+  return service({
+    url: '/shortlist/deleteShortlist?id=' + id,
+    method: 'delete'
+  })
+}

+ 65 - 58
web/src/api/vehicle.js

@@ -2,104 +2,111 @@ import service from '@/utils/request'
 
 // 新增车主
 export const addVehicle = (data) => {
-    return service({
-        url: '/owner/create',
-        method: 'post',
-        data
-    })
+  return service({
+    url: '/owner/create',
+    method: 'post',
+    data
+  })
 }
 
 // 更新车辆信息
 export const editVehicle = (data) => {
-    return service({
-        url: '/vehicle/update',
-        method: 'put',
-        data
-    })
+  return service({
+    url: '/vehicle/update',
+    method: 'put',
+    data
+  })
+}
+
+export const queryAllVehicles = () => {
+  return service({
+    url: '/vehicle/all',
+    method: 'get'
+  })
 }
 
 // 根据ID获取车辆信息
 export const obtainVehicleId = (id) => {
-    return service({
-        url: '/vehicle/get?id=' + id,
-        method: 'get'
-    })
+  return service({
+    url: '/vehicle/get?id=' + id,
+    method: 'get'
+  })
 }
 
 // 根据车牌号获取车辆信息
 export const obtainVehicleLicense = (number) => {
-    return service({
-        url: '/vehicle/get-by-plate?plate_number=' + number,
-        method: 'get'
-    })
+  return service({
+    url: '/vehicle/get-by-plate?plate_number=' + number,
+    method: 'get'
+  })
 }
 
 // 获取车辆列表
-export const obtainVehicleList  = (params) => {
-    return service({
-        url: '/vehicle/list',
-        method: 'get',
-        params
-    })
+export const obtainVehicleList = (params) => {
+  return service({
+    url: '/vehicle/list',
+    method: 'get',
+    params
+  })
 }
 
 // 删除车辆
-export const removeVehicleList  = (params) => {
-    return service({
-        url: '/vehicle/delete',
-        method: 'delete',
-        params
-    })
+export const removeVehicleList = (params) => {
+  return service({
+    url: '/vehicle/delete',
+    method: 'delete',
+    params
+  })
 }
 
 // 车辆入场
 export const vehicleEntry = (data) => {
-    return service({
-        url: '/vehicle/entry',
-        method: 'post',
-        data
-    })
+  return service({
+    url: '/vehicle/entry',
+    method: 'post',
+    data
+  })
 }
 
 // 车辆出场
 export const vehicleAppear = (data) => {
-    return service({
-        url: '/vehicle/exit',
-        method: 'post',
-        data
-    })
+  return service({
+    url: '/vehicle/exit',
+    method: 'post',
+    data
+  })
 }
 
 // 创建收费配置
 export const createCharge = (data) => {
-    return service({
-        url: '/vehicle/fee-config/create',
-        method: 'post',
-        data
-    })
+  return service({
+    url: '/vehicle/fee-config/create',
+    method: 'post',
+    data
+  })
 }
 
 // 更新收费配置
 export const editCharge = (data) => {
-    return service({
-        url: '/vehicle/fee-config/update',
-        method: 'put',
-        data
-    })
+  return service({
+    url: '/vehicle/fee-config/update',
+    method: 'put',
+    data
+  })
 }
 
 // 根据ID获取收费配置
 export const obtainChargeId = (id) => {
-    return service({
-        url: '/vehicle/fee-config/get?id=' + id,
-        method: 'get'
-    })
+  return service({
+    url: '/vehicle/fee-config/get?id=' + id,
+    method: 'get'
+  })
 }
 
 // 获取收费配置列表
 export const obtainChargeList = (id) => {
-    return service({
-        url: '/vehicle/fee-config/list',
-        method: 'get'
-    })
+  return service({
+    url: '/vehicle/fee-config/list',
+    method: 'get'
+  })
 }

+ 163 - 72
web/src/view/parking/carOwner.vue

@@ -2,22 +2,46 @@
   <div>
     <el-form inline>
       <el-form-item>
-        <el-input placeholder="请输入手机号" v-model="obtainData.owner_phone" />
+        <el-input
+          v-model="obtainData.owner_phone"
+          placeholder="请输入手机号"
+        />
       </el-form-item>
       <el-form-item>
-        <el-input placeholder="请输入车主名称" v-model="obtainData.owner_name"></el-input>
+        <el-input
+          v-model="obtainData.owner_name"
+          placeholder="请输入车主名称"
+        />
       </el-form-item>
       <el-form-item>
-        <el-select placeholder="是否是vip" v-model="obtainData.is_vip" @change="changeVip">
-          <el-option label="未成为vip" :value="false"></el-option>
-          <el-option label="已成为vip" :value="true"></el-option>
+        <el-select
+          v-model="obtainData.is_vip"
+          placeholder="是否是vip"
+          clearable
+          @change="changeVip"
+        >
+          <el-option
+            label="未成为vip"
+            :value="false"
+          />
+          <el-option
+            label="已成为vip"
+            :value="true"
+          />
         </el-select>
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" icon="Search" @click="getCarOwnerList">搜索</el-button>
+        <el-button
+          type="primary"
+          icon="Search"
+          @click="getCarOwnerList"
+        >搜索</el-button>
       </el-form-item>
       <el-form-item>
-        <el-button icon="Plus" @click="openCarOwner('新增车主','')">新增</el-button>
+        <el-button
+          icon="Plus"
+          @click="openCarOwner('新增车主','')"
+        >新增</el-button>
       </el-form-item>
       <el-form-item>
         <el-button icon="Delete">删除</el-button>
@@ -38,69 +62,136 @@
         <el-button icon="ChatLineRound">其他</el-button>
       </el-form-item>
     </el-form>
-    <el-table :data="carOwnerList" height="605">
-      <el-table-column label="名字" align="center" width="180" prop="owner_name"></el-table-column>
-      <el-table-column label="姓氏" align="center" width="180" prop="owner_surname"></el-table-column>
-      <el-table-column label="手机" align="center" prop="owner_phone"></el-table-column>
-      <el-table-column label="邮箱" align="center"></el-table-column>
-      <el-table-column label="vip" align="center">
+    <el-table
+      :data="carOwnerList"
+      height="605"
+    >
+      <el-table-column
+        label="名字"
+        align="center"
+        width="180"
+        prop="owner_name"
+      />
+      <el-table-column
+        label="姓氏"
+        align="center"
+        width="180"
+        prop="owner_surname"
+      />
+      <el-table-column
+        label="手机"
+        align="center"
+        prop="owner_phone"
+      />
+      <el-table-column
+        label="邮箱"
+        align="center"
+      />
+      <el-table-column
+        label="vip"
+        align="center"
+      >
         <template #default="scope">
-          <el-button :type="scope.row.is_vip ? 'success' : 'danger'" plain>
-            {{scope.row.is_vip ? '已成为vip' : '未成为vip'}}
+          <el-button
+            :type="scope.row.is_vip ? 'success' : 'danger'"
+            plain
+          >
+            {{ scope.row.is_vip ? '已成为vip' : '未成为vip' }}
           </el-button>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center">
+      <el-table-column
+        label="操作"
+        align="center"
+      >
         <template #default="scope">
-<!--          <el-tooltip content="Bottom center" placement="bottom" effect="light">-->
-<!--            <el-button text type="primary" icon="MessageBox">备注</el-button>-->
-<!--          </el-tooltip>-->
-          <el-button text type="primary" icon="MessageBox">备注</el-button>
-          <el-button @click="openCarOwner('编辑车主', scope.row)" icon="edit" text type="primary">编辑</el-button>
-          <el-button @click="deleteCarOwner(scope.row)" icon="delete" text type="primary">删除</el-button>
+          <!--          <el-tooltip content="Bottom center" placement="bottom" effect="light">-->
+          <!--            <el-button text type="primary" icon="MessageBox">备注</el-button>-->
+          <!--          </el-tooltip>-->
+          <el-button
+            text
+            type="primary"
+            icon="MessageBox"
+          >备注</el-button>
+          <el-button
+            icon="edit"
+            text
+            type="primary"
+            @click="openCarOwner('编辑车主', scope.row)"
+          >编辑</el-button>
+          <el-button
+            icon="delete"
+            text
+            type="primary"
+            @click="deleteCarOwner(scope.row)"
+          >删除</el-button>
         </template>
       </el-table-column>
     </el-table>
     <div style="width: 100%;display: flex;justify-content: end">
       <el-pagination
-          :current-page="obtainData.page"
-          :page-size="obtainData.pageSize"
-          :page-sizes="[10, 30, 50, 100]"
-          :total="carOwnerTotal"
-          layout="total, sizes, prev, pager, next, jumper"
-          @current-change="handleCurrentChange"
-          @size-change="handleSizeChange"
-          style="padding-right: 10px"
+        :current-page="obtainData.page"
+        :page-size="obtainData.pageSize"
+        :page-sizes="[10, 30, 50, 100]"
+        :total="carOwnerTotal"
+        layout="total, sizes, prev, pager, next, jumper"
+        style="padding-right: 10px"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
       />
     </div>
-    <el-dialog v-model="carOwnerShow" :title="carOwnerTitle" width="650">
-      <el-form label-width="100" size="large">
+    <el-dialog
+      v-model="carOwnerShow"
+      :title="carOwnerTitle"
+      width="650"
+    >
+      <el-form
+        label-width="100"
+        size="large"
+      >
         <el-form-item label="名称:">
-          <el-input v-model="carOwnerData.owner_name"/>
+          <el-input v-model="carOwnerData.owner_name" />
         </el-form-item>
         <el-form-item label="姓氏:">
-          <el-input v-model="carOwnerData.owner_surname"/>
+          <el-input v-model="carOwnerData.owner_surname" />
         </el-form-item>
         <el-form-item label="手机:">
-          <el-input v-model="carOwnerData.owner_phone"/>
+          <el-input v-model="carOwnerData.owner_phone" />
         </el-form-item>
         <el-form-item label="vip:">
-          <el-radio-group v-model="carOwnerData.is_vip" size="large" fill="#409eff">
-            <el-radio-button label="否" :value="false" />
-            <el-radio-button label="是" :value="true" />
+          <el-radio-group
+            v-model="carOwnerData.is_vip"
+            size="large"
+            fill="#409eff"
+          >
+            <el-radio-button
+              label="否"
+              :value="false"
+            />
+            <el-radio-button
+              label="是"
+              :value="true"
+            />
           </el-radio-group>
         </el-form-item>
         <el-form-item label="vip过期时间:">
           <el-date-picker
-              v-model="carOwnerData.vip_expire_time"
-              type="date"
-              placeholder="当前日期"
+            v-model="carOwnerData.vip_expire_time"
+            type="date"
+            placeholder="当前日期"
           />
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="carOwnerShow = false" size="large">取消</el-button>
-        <el-button @click="carOwnerOperation" type="primary" size="large">确认</el-button>
+        <el-button
+          size="large"
+          @click="carOwnerShow = false"
+        >取消</el-button>
+        <el-button
+          type="primary"
+          size="large"
+          @click="carOwnerOperation"
+        >确认</el-button>
       </template>
     </el-dialog>
   </div>
@@ -110,7 +201,7 @@
 import { obtainCarOwnerList, addCarOwner, editCarOwner, removeCarOwner } from '@/api/carOwner'
 import { onMounted, reactive, ref } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import {deleteGuardBooth} from "@/api/guardBooth";
+import { deleteGuardBooth } from '@/api/guardBooth'
 const carOwnerList = reactive([1])
 const carOwnerShow = ref(false)
 const carOwnerTitle = ref('')
@@ -158,36 +249,36 @@ const getCarOwnerList = () => {
 
 const deleteCarOwner = (data) => {
   ElMessageBox.confirm(
-      '确定要删除该车主信息吗?',
-      '提示',
-      {
-        confirmButtonText: '确认',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
+    '确定要删除该车主信息吗?',
+    '提示',
+    {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
   )
-      .then(() => {
-       removeCarOwner(data.ID).then(res => {
-          if (res.code === 0) {
-            ElMessage({
-              type: 'success',
-              message: '删除成功',
-            })
-            getCarOwnerList()
-          } else {
-            ElMessage({
-              type: 'error',
-              message: '删除失败',
-            })
-          }
-        })
+    .then(() => {
+      removeCarOwner(data.ID).then(res => {
+        if (res.code === 0) {
+          ElMessage({
+            type: 'success',
+            message: '删除成功',
+          })
+          getCarOwnerList()
+        } else {
+          ElMessage({
+            type: 'error',
+            message: '删除失败',
+          })
+        }
       })
-      .catch(() => {
-        ElMessage({
-          type: 'info',
-          message: '取消删除',
-        })
+    })
+    .catch(() => {
+      ElMessage({
+        type: 'info',
+        message: '取消删除',
       })
+    })
 }
 
 const openCarOwner = (head, data) => {
@@ -209,7 +300,7 @@ const openCarOwner = (head, data) => {
   }
 }
 
-const carOwnerOperation = async () => {
+const carOwnerOperation = async() => {
   if (carOwnerTitle.value === '新增车主') {
     await addCarOwner(carOwnerData).then(res => {
       if (res.code === 0) {

+ 287 - 49
web/src/view/parking/roster.vue

@@ -1,78 +1,316 @@
 <template>
-  <div>
-    <el-form inline>
-      <el-form-item label="车牌" style="width: 200px">
-        <el-input/>
+  <div class="shortlist-page">
+    <!-- 查询条件 -->
+    <el-form
+      inline
+      :model="queryForm"
+    >
+      <el-form-item label="车牌号码">
+        <el-input
+          v-model="queryForm.plate_number"
+          placeholder="车牌"
+          clearable
+        />
       </el-form-item>
-      <el-form-item label="车主">
-        <el-input/>
-      </el-form-item>
-      <el-form-item label="到期日期">
-        <el-select></el-select>
-      </el-form-item>
-      <el-form-item label="车辆类型" style="width: 180px">
-        <el-select></el-select>
-      </el-form-item>
-      <el-form-item label="黑白名单">
-        <el-select></el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button>批量导入</el-button>
+
+      <el-form-item label="RFID标签">
+        <el-input
+          v-model="queryForm.rfid_tag"
+          placeholder="RFID"
+          clearable
+        />
       </el-form-item>
-      <el-form-item>
-        <el-button icon="Plus" @click="addRollShow = true">新增</el-button>
+
+      <el-form-item label="名单类型">
+        <el-select
+          v-model="queryForm.list_type"
+          placeholder="全部"
+          clearable
+        >
+          <el-option
+            label="白名单"
+            value="白名单"
+          />
+          <el-option
+            label="黑名单"
+            value="黑名单"
+          />
+        </el-select>
       </el-form-item>
+
       <el-form-item>
-        <el-button type="primary">搜索</el-button>
+        <el-button
+          type="primary"
+          @click="getList"
+        >搜索</el-button>
+        <el-button @click="resetQuery">重置</el-button>
+        <el-button
+          type="success"
+          icon="Plus"
+          @click="add"
+        >新增</el-button>
       </el-form-item>
     </el-form>
-    <el-dialog v-model="rollOperationShow">
-      <el-form>
-        <el-form-item label="黑白名单:">
-          <el-radio-group v-model="rollData.black_white" size="large" fill="#409eff">
-            <el-radio-button label="否" :value="false" />
-            <el-radio-button label="是" :value="true" />
+
+    <!-- 表格 -->
+    <el-table
+      :data="list"
+      border
+      stripe
+    >
+      <el-table-column
+        prop="ID"
+        label="ID"
+        align="center"
+        width="70"
+      />
+      <el-table-column
+        prop="list_type"
+        label="名单类型"
+        align="center"
+        width="120"
+      >
+        <template #default="scope">
+          <el-tag :type="scope.row.list_type === '白名单' ? 'success' : 'danger'">
+            {{ scope.row.list_type }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="车牌"
+        align="center"
+        prop="vehicle.plate_number"
+      />
+      <el-table-column
+        label="RFID"
+        align="center"
+        prop="vehicle.rfid_tag"
+      />
+      <el-table-column
+        label="车辆类型"
+        align="center"
+        prop="vehicle.vehicle_type"
+      />
+      <el-table-column
+        label="到期时间"
+        align="center"
+        width="200"
+      >
+        <template #default="scope">
+          {{ formatTime(scope.row.expiration_time) }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        align="center"
+        width="180"
+      >
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="edit(scope.row)"
+          >编辑</el-button>
+          <el-button
+            link
+            type="danger"
+            @click="del(scope.row.ID)"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <el-pagination
+      v-model:current-page="page"
+      v-model:page-size="pageSize"
+      :total="total"
+      layout="total, prev, pager, next, jumper"
+      style="margin-top:20px; text-align:right"
+      @current-change="getList"
+    />
+
+    <!-- 新增/编辑弹窗 → 车辆下拉选择 -->
+    <el-dialog
+      v-model="dialogVisible"
+      title="黑白名单管理"
+      width="550px"
+    >
+      <el-form
+        :model="form"
+        label-width="120px"
+      >
+        <el-form-item
+          label="名单类型"
+          required
+        >
+          <el-radio-group v-model="form.list_type">
+            <el-radio value="白名单">白名单</el-radio>
+            <el-radio value="黑名单">黑名单</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="车主名称:">
-          <el-select></el-select>
+
+        <el-form-item
+          label="选择车辆"
+          required
+        >
+          <el-select
+            v-model="form.vehicle_id"
+            placeholder="请选择车辆"
+            style="width:100%"
+          >
+            <el-option
+              v-for="v in vehicleList"
+              :key="v.ID"
+              :label="`${v.plate_number} | ${v.rfid_tag || '无标签'}`"
+              :value="v.ID"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="到期时间">
+          <el-date-picker
+            v-model="form.expiration_time"
+            type="datetime"
+            placeholder="不选则永久有效"
+            style="width:100%"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
         </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>
+
       <template #footer>
-        <el-button @click="rollOperationShow = false">取消</el-button>
-        <el-button @click="rollOperation" type="primary">确认</el-button>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button
+          type="primary"
+          @click="submit"
+        >确认保存</el-button>
       </template>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive } from 'vue'
-const activeName = ref('first')
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { queryShortlistList, addShortlist, updateShortlist, deleteShortlist } from '@/api/shortlist'
+import { queryAllVehicles } from '@/api/vehicle' // 你现有的车辆接口
 
-const handleClick = () => {
+// 数据列表
+const list = ref([])
+const total = ref(0)
+const page = ref(1)
+const pageSize = ref(10)
 
-}
-// 黑白名单操作
-const rollOperationShow = ref(false)
-
-const rollData = reactive({
-  black_white: false,
-  carOwner: 0,
-  carPlate: '',
-  recognize: '',
-  expire: ''
+// 查询表单
+const queryForm = reactive({
+  plate_number: '',
+  rfid_tag: '',
+  list_type: ''
+})
+
+// 车辆下拉列表
+const vehicleList = ref([])
+
+// 弹窗
+const dialogVisible = ref(false)
+const form = reactive({
+  ID: 0,
+  list_type: '白名单',
+  vehicle_id: '',
+  expiration_time: ''
 })
 
-const rollOperation = () => {
+// 时间格式化
+const formatTime = (time) => {
+  if (!time) return '永久有效'
+  return time.replace('T', ' ').split('+')[0]
+}
+
+// 获取黑白名单列表
+const getList = async() => {
+  const res = await queryShortlistList({
+    page: page.value,
+    pageSize: pageSize.value,
+    ...queryForm
+  })
+  list.value = res.data.list
+  total.value = res.data.total
+}
+
+// 获取所有车辆(下拉)
+const getVehicleAll = async() => {
+  const res = await queryAllVehicles()
+  vehicleList.value = res.data
+}
+
+// 重置
+const resetQuery = () => {
+  queryForm.plate_number = ''
+  queryForm.rfid_tag = ''
+  queryForm.list_type = ''
+  page.value = 1
+  getList()
+}
+
+// 新增
+const add = () => {
+  form.ID = 0
+  form.list_type = '白名单'
+  form.vehicle_id = ''
+  form.expiration_time = ''
+  dialogVisible.value = true
+}
 
+// 编辑
+const edit = (row) => {
+  form.ID = row.ID
+  form.list_type = row.list_type
+  form.vehicle_id = row.vehicle_id
+  form.expiration_time = row.expiration_time ? formatTime(row.expiration_time) : ''
+  dialogVisible.value = true
 }
 
+// 提交
+const submit = async() => {
+  if (!form.vehicle_id) {
+    ElMessage.warning('请选择车辆')
+    return
+  }
+  try {
+    if (form.ID > 0) {
+      await updateShortlist(form)
+      ElMessage.success('修改成功')
+    } else {
+      await addShortlist(form)
+      ElMessage.success('新增成功')
+    }
+    dialogVisible.value = false
+    getList()
+  } catch (e) {
+    ElMessage.error('保存失败')
+  }
+}
+
+// 删除
+const del = async(id) => {
+  await ElMessageBox.confirm('确定删除?')
+  await deleteShortlist(id)
+  ElMessage.success('删除成功')
+  getList()
+}
+
+// 初始化
+onMounted(() => {
+  getList()
+  getVehicleAll()
+})
 </script>
 
 <style scoped lang="scss">
-
+.shortlist-page {
+  padding: 20px;
+  background: #fff;
+  min-height: 100%;
+}
 </style>