From 314e8f134065e0ba3dc8dbb2e5e37a0b2ca2d5e8 Mon Sep 17 00:00:00 2001
From: 董国庆 <364620639@qq.com>
Date: 星期二, 09 六月 2026 10:51:22 +0800
Subject: [PATCH] demo

---
 src/views/userContent/whitelistManagement/whitelist/components/WhitelistEditModal.vue |  101 ++++++---
 src/service/api/whitelist.ts                                                          |   13 
 src/views/userContent/whitelistManagement/whitelist/index.vue                         |   30 +-
 src/service/api/testPlanManagement.ts                                                 |   24 ++
 src/views/testDemo/testPlanManagement/constants.ts                                    |   31 +++
 src/views/testDemo/testPlanManagement/api.txt                                         |   10 
 src/views/testDemo/testPlanManagement/index.vue                                       |  115 ++++++----
 src/views/testDemo/testPlanManagement/components/AssignVehicleModal.vue               |   34 ++-
 src/views/testDemo/testPlanManagement/detail.vue                                      |  200 +++++++++----------
 src/views/testDemo/testPlanManagement/components/ManageTestVehicleModal.vue           |   27 ++
 10 files changed, 358 insertions(+), 227 deletions(-)

diff --git a/src/service/api/testPlanManagement.ts b/src/service/api/testPlanManagement.ts
index 77706fa..6a7688e 100644
--- a/src/service/api/testPlanManagement.ts
+++ b/src/service/api/testPlanManagement.ts
@@ -9,6 +9,10 @@
   return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/pageList', params)
 }
 
+export function deleteTestPlan(id: number | string) {
+  return request.Delete<Service.ResponseResult<any>>(`/operation/api/v1/test-plan-demo/${id}`)
+}
+
 export function addTestPlan(data: {
   orderMode: number
   planName: string
@@ -80,7 +84,7 @@
 
 export function interruptTestPlanDispatch(planId: number | string) {
   return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/dispatch/interrupt', null, {
-    params: { planId },
+    params: { planId, testPlanId: planId },
   })
 }
 
@@ -90,14 +94,20 @@
   })
 }
 
+export function closeTestPlanDispatch(planId: number | string) {
+  return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/dispatch/closeTest', null, {
+    params: { planId },
+  })
+}
+
 export function fetchTestPlanDetailOrderPageList(data: {
   endAddress?: string
   orderNumber?: string
-  orderStatus?: number
   pageNum: number
   pageSize: number
   plateNumber?: string
   startAddress?: string
+  status?: number
   testPlanId: number | string
 }) {
   return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/detail/pageOrders', data)
@@ -106,11 +116,11 @@
 export function exportTestPlanDetailOrders(data: {
   endAddress?: string
   orderNumber?: string
-  orderStatus?: number
   pageNum?: number
   pageSize?: number
   plateNumber?: string
   startAddress?: string
+  status?: number
   testPlanId: number | string
 }) {
   const method = request.Post<Blob>('/operation/api/v1/test-plan-demo/detail/exportOrders', data, {
@@ -140,6 +150,14 @@
   return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/order/reassign', data)
 }
 
+export function assignTestPlanOrderVehicle(data: {
+  orderId: number | string
+  planId: number | string
+  vehicleIds: Array<number | string>
+}) {
+  return request.Post<Service.ResponseResult<any>>('/operation/api/v1/test-plan-demo/dispatch/assignVehicle', data)
+}
+
 export function fetchTestPlanVehiclePageList(data: {
   pageNum: number
   pageSize: number
diff --git a/src/service/api/whitelist.ts b/src/service/api/whitelist.ts
index 5e34ab8..6b51561 100644
--- a/src/service/api/whitelist.ts
+++ b/src/service/api/whitelist.ts
@@ -11,13 +11,18 @@
 /**
  * 新增白名单
  */
-export function addWhitelist(data: {
+export interface AddWhitelistParams {
   account: string
-  status: number
-  isPriorityDispatch: number
   expireTime?: string
+  isPriorityDispatch: boolean
+  isTestUser?: boolean
   maxOrderNum?: number
-}) {
+  nickName: string
+  remark?: string
+  status: boolean
+}
+
+export function addWhitelist(data: AddWhitelistParams) {
   return request.Post<Service.ResponseResult<any>>('/operation/api/v1/whitelist/add', data)
 }
 
diff --git a/src/views/testDemo/testPlanManagement/api.txt b/src/views/testDemo/testPlanManagement/api.txt
index 7f2dc48..16b83f7 100644
--- a/src/views/testDemo/testPlanManagement/api.txt
+++ b/src/views/testDemo/testPlanManagement/api.txt
@@ -402,4 +402,12 @@
 
 
 
-​/api​/v1​/test-plan-demo​/detail​/listVehicles 车辆状态不分页 post
\ No newline at end of file
+​/api​/v1​/test-plan-demo​/detail​/listVehicles 车辆状态不分页 post
+
+
+计划管理列表操作接口:
+ 
+​/api​/v1​/test-plan-demo​/{id}    删除测试计划(测试中不可删  deleted
+/api/v1/test-plan-demo/dispatch/closeTest   关闭测试(已暂停状态可操作)  post  params   planId
+/api/v1/test-plan-demo/dispatch/resume   继续测试(恢复已冻结订单)  post  params  planId
+​/api​/v1​/test-plan-demo​/dispatch​/interrupt 中断测试(冻结所有待下发订单)  post  params  testPlanId
diff --git a/src/views/testDemo/testPlanManagement/components/AssignVehicleModal.vue b/src/views/testDemo/testPlanManagement/components/AssignVehicleModal.vue
index 74b4354..e43c642 100644
--- a/src/views/testDemo/testPlanManagement/components/AssignVehicleModal.vue
+++ b/src/views/testDemo/testPlanManagement/components/AssignVehicleModal.vue
@@ -3,7 +3,7 @@
 import { computed, ref, watch } from 'vue'
 import { NButton, NCard, NDataTable, NInput, NModal, NSelect, NTag, useMessage } from 'naive-ui'
 import Pagination from '@/components/common/Pagination.vue'
-import { fetchTestPlanVehiclePageList, reassignTestPlanOrder } from '@/service/api/testPlanManagement'
+import { assignTestPlanOrderVehicle, fetchTestPlanVehiclePageList, reassignTestPlanOrder } from '@/service/api/testPlanManagement'
 
 interface VehicleRow {
   id: number | string
@@ -19,6 +19,7 @@
   visible: boolean
   planId?: number | string
   orderId?: number | string | null
+  actionType?: 'assign' | 'reassign'
 }>()
 
 const emit = defineEmits<{
@@ -32,6 +33,9 @@
   get: () => props.visible,
   set: val => emit('update:visible', val),
 })
+
+const isAssignAction = computed(() => props.actionType !== 'reassign')
+const modalTitle = computed(() => (isAssignAction.value ? '选择分派车辆' : '选择改派车辆'))
 
 const model = ref({
   plateNo: '',
@@ -168,20 +172,26 @@
 
   submitting.value = true
   try {
-    const res = await reassignTestPlanOrder({
-      orderId: props.orderId,
-      planId: props.planId,
-      vehicleId: checkedRowKeys.value[0],
-    })
+    const res = isAssignAction.value
+      ? await assignTestPlanOrderVehicle({
+          orderId: props.orderId,
+          planId: props.planId,
+          vehicleIds: [checkedRowKeys.value[0]],
+        })
+      : await reassignTestPlanOrder({
+          orderId: props.orderId,
+          planId: props.planId,
+          vehicleId: checkedRowKeys.value[0],
+        })
     if (res.code !== 200 && res.code !== 0) {
-      throw new Error(res.msg || '分派车辆失败')
+      throw new Error(res.msg || (isAssignAction.value ? '分派车辆失败' : '改派车辆失败'))
     }
-    message.success('分派车辆成功')
+    message.success(isAssignAction.value ? '分派车辆成功' : '改派车辆成功')
     emit('success')
     show.value = false
   }
   catch (error: any) {
-    message.error(error?.message || '分派车辆失败')
+    message.error(error?.message || (isAssignAction.value ? '分派车辆失败' : '改派车辆失败'))
   }
   finally {
     submitting.value = false
@@ -235,7 +245,7 @@
       footer-style="padding: 16px 24px; border-top: 1px solid rgba(46, 61, 77, 0.1); flex-shrink: 0;"
     >
       <template #header>
-        <span style="font-size: 16px; font-weight: bold">选择分派车辆</span>
+        <span style="font-size: 16px; font-weight: bold">{{ modalTitle }}</span>
       </template>
       <template #header-extra>
         <div class="cursor-pointer" text @click="handleCancel">
@@ -345,7 +355,9 @@
       <template #footer>
         <div class="flex justify-end gap-3" style="gap: 12px;">
           <n-button class="footer-btn" @click="handleCancel">取消</n-button>
-          <n-button type="primary" class="footer-btn" :loading="submitting" @click="handleConfirm">确认下发</n-button>
+          <n-button type="primary" class="footer-btn" :loading="submitting" @click="handleConfirm">
+            {{ isAssignAction ? '确认分派' : '确认改派' }}
+          </n-button>
         </div>
       </template>
     </n-card>
diff --git a/src/views/testDemo/testPlanManagement/components/ManageTestVehicleModal.vue b/src/views/testDemo/testPlanManagement/components/ManageTestVehicleModal.vue
index f0d139a..8bdb613 100644
--- a/src/views/testDemo/testPlanManagement/components/ManageTestVehicleModal.vue
+++ b/src/views/testDemo/testPlanManagement/components/ManageTestVehicleModal.vue
@@ -11,6 +11,7 @@
   vehicleCode: string
   plateNo: string
   model: string
+  paused: boolean
   status: number
   statusName: string
 }
@@ -84,6 +85,10 @@
   return statusOptions.find(item => item.value === status)?.label || '-'
 }
 
+function isPausedVehicle(row: VehicleRow) {
+  return row.paused || row.status === 3 || row.statusName.includes('暂停')
+}
+
 async function getList() {
   if (!props.planId)
     return
@@ -108,6 +113,7 @@
       vehicleCode: item.vehicleCode || '-',
       plateNo: item.plateNumber || '-',
       model: item.vehicleModel || '-',
+      paused: Number(item.paused ?? 0) === 1,
       status: Number(item.vehicleStatus ?? 0),
       statusName: item.vehicleStatusName || '',
     }))
@@ -267,8 +273,8 @@
 }
 
 function handleToggleStatus(row: VehicleRow) {
-  const paused: 0 | 1 = row.status === 3 ? 0 : 1
-  const successText = row.status === 3 ? '重启服务成功' : '暂停服务成功'
+  const paused: 0 | 1 = isPausedVehicle(row) ? 0 : 1
+  const successText = isPausedVehicle(row) ? '重启服务成功' : '暂停服务成功'
   runVehiclePause([row.id], paused, successText)
 }
 
@@ -277,7 +283,18 @@
     message.warning('请选择至少一辆测试车辆')
     return
   }
-  await runVehicleManage({ addVehicleIds: rows.map(item => item.id) }, '添加测试车辆成功')
+  const existingIdSet = new Set(existingVehicles.value.map(item => item.id))
+  const addVehicleIds = rows
+    .map(item => item.id)
+    .filter(id => !existingIdSet.has(id))
+
+  if (!addVehicleIds.length) {
+    message.info('未选择新的测试车辆')
+    vehicleSelectVisible.value = false
+    return
+  }
+
+  await runVehicleManage({ addVehicleIds }, '添加测试车辆成功')
   vehicleSelectVisible.value = false
 }
 
@@ -334,7 +351,7 @@
           class="cursor-pointer-text"
           onClick={() => handleToggleStatus(row)}
         >
-          {row.status === 3 ? '重启服务' : '暂停服务'}
+          {isPausedVehicle(row) ? '重启服务' : '暂停服务'}
         </span>
       </NSpace>
     ),
@@ -483,7 +500,7 @@
       <template #footer>
         <div class="flex justify-end gap-3" style="gap: 12px;">
           <n-button class="footer-btn" @click="handleCancel">取消</n-button>
-          <n-button type="primary" class="footer-btn" :loading="actionLoading" @click="handleSave">保存</n-button>
+          <n-button type="primary" class="footer-btn" :loading="actionLoading" @click="handleSave">完成</n-button>
         </div>
       </template>
     </n-card>
diff --git a/src/views/testDemo/testPlanManagement/constants.ts b/src/views/testDemo/testPlanManagement/constants.ts
new file mode 100644
index 0000000..c8d2edc
--- /dev/null
+++ b/src/views/testDemo/testPlanManagement/constants.ts
@@ -0,0 +1,31 @@
+export const TEST_PLAN_STATUS_OPTIONS = [
+  { label: '待下发', value: 1 },
+  { label: '测试中', value: 2 },
+  { label: '已暂停', value: 3 },
+  { label: '已完成', value: 4 },
+  { label: '关闭测试', value: 5 },
+]
+
+export const TEST_PLAN_STATUS_LABEL_MAP: Record<number, string> = {
+  1: '待下发',
+  2: '测试中',
+  3: '已暂停',
+  4: '已完成',
+  5: '关闭测试',
+}
+
+export const TEST_PLAN_STATUS_STYLE_MAP: Record<number, { textColor: string, color: string }> = {
+  1: { textColor: '#764DFF', color: '#F6F0FF' },
+  2: { textColor: '#0068FF', color: '#E6F4FF' },
+  3: { textColor: '#FF9000', color: '#FFF8E6' },
+  4: { textColor: '#0BB52D', color: '#DCF5DD' },
+  5: { textColor: 'rgba(23, 31, 38, 0.8)', color: 'rgba(23, 31, 38, 0.05)' },
+}
+
+export function getTestPlanStatusLabel(status: number) {
+  return TEST_PLAN_STATUS_LABEL_MAP[status] || '-'
+}
+
+export function getTestPlanStatusStyle(status: number) {
+  return TEST_PLAN_STATUS_STYLE_MAP[status] || { textColor: '#333333', color: '#F5F5F5' }
+}
diff --git a/src/views/testDemo/testPlanManagement/detail.vue b/src/views/testDemo/testPlanManagement/detail.vue
index a0c3b6e..41ed848 100644
--- a/src/views/testDemo/testPlanManagement/detail.vue
+++ b/src/views/testDemo/testPlanManagement/detail.vue
@@ -18,6 +18,7 @@
   interruptTestPlanDispatch,
   resumeTestPlanDispatch,
 } from '@/service/api/testPlanManagement'
+import { getTestPlanStatusLabel, getTestPlanStatusStyle } from './constants'
 import AssignVehicleModal from './components/AssignVehicleModal.vue'
 import ManageTestVehicleModal from './components/ManageTestVehicleModal.vue'
 
@@ -29,9 +30,7 @@
 const currentPlanId = computed(() => String(route.query.id || ''))
 
 const planName = ref('-')
-const planStatus = ref('-')
-const planStatusColor = ref('#0068FF')
-const planStatusBg = ref('#E6F4FF')
+const planStatusCode = ref<number>(0)
 const dispatchStatus = ref<number>(0)
 const interruptAllowed = ref(false)
 const totalCount = ref(0)
@@ -49,7 +48,7 @@
   startAddress: null as string | null,
   endAddress: null as string | null,
   plateNumber: null as string | null,
-  orderStatus: null as number | null,
+  status: null as number | null,
 })
 
 const isVehicleSelectOpen = ref(false)
@@ -82,6 +81,7 @@
   status: number
   statusName: string
   statusColor: string
+  statusBg: string
 }
 
 const orderData = ref<OrderRow[]>([])
@@ -90,42 +90,20 @@
 const isAssignVehicleModalVisible = ref(false)
 const isManageTestVehicleModalVisible = ref(false)
 const currentAssignOrderId = ref<number | string | null>(null)
+const currentAssignActionType = ref<'assign' | 'reassign'>('assign')
 const pointOptions = ref<Array<{ label: string, value: string }>>([])
 
-const PLAN_STATUS_STYLE_MAP: Record<number, { textColor: string, color: string }> = {
+const VEHICLE_STATUS_STYLE_MAP: Record<number, { textColor: string, color: string }> = {
+  0: { textColor: '#10B981', color: '#F6FFED' },
   1: { textColor: '#0068FF', color: '#E6F4FF' },
-  2: { textColor: '#F59E0B', color: '#FFF7E6' },
-  3: { textColor: '#10B981', color: '#F6FFED' },
+  2: { textColor: '#A034FF', color: '#F4E6FF' },
+  3: { textColor: '#F59E0B', color: '#FFF7E6' },
 }
 
-const VEHICLE_STATUS_STYLE_MAP: Record<number, string> = {
-  0: '#10B981',
-  1: '#0068FF',
-  2: '#7C3AED',
-  3: '#F59E0B',
-}
-
-const ACTION_LABEL_MAP: Record<string, string> = {
-  cancel: '取消订单',
-  cancel_order: '取消订单',
-  cancelOrder: '取消订单',
-  close: '关闭订单',
-  close_order: '关闭订单',
-  closeOrder: '关闭订单',
-  delete: '删除',
-  delete_order: '删除',
-  deleteOrder: '删除',
-  detail: '详情',
-  view_detail: '详情',
-  reassign: '改派',
-  re_assign: '改派',
-  assign: '分派车辆',
-  allocate: '分派车辆',
-  dispatch: '分派车辆',
-}
-
-const interruptButtonVisible = computed(() => dispatchStatus.value === 1 || dispatchStatus.value === 2)
-const interruptButtonText = computed(() => (dispatchStatus.value === 2 ? '继续下发' : '中断下发'))
+const planStatusText = computed(() => getTestPlanStatusLabel(planStatusCode.value))
+const planStatusStyle = computed(() => getTestPlanStatusStyle(planStatusCode.value))
+const interruptButtonVisible = computed(() => planStatusCode.value === 2 || planStatusCode.value === 3)
+const interruptButtonText = computed(() => (planStatusCode.value === 3 ? '继续测试' : '中断测试'))
 
 const vehicleOptions = computed(() =>
   vehicleData.value.map(item => ({
@@ -135,31 +113,32 @@
 )
 
 const orderStatusOptions = [
-  { label: '已取消', value: 0 },
+  { label: '待下发', value: 0 },
   { label: '待接单', value: 1 },
   { label: '待出发', value: 2 },
-  { label: '接驾中', value: 3 },
-  { label: '待客上车', value: 4 },
-  { label: '乘客已上车', value: 5 },
+  { label: '待到达预约地', value: 3 },
+  { label: '待乘客上车', value: 4 },
   { label: '服务中', value: 6 },
-  { label: '待客下车', value: 7 },
-  { label: '待付款', value: 8 },
-  { label: '待评价', value: 9 },
-  { label: '已完成', value: 10 },
 ]
 
 const ORDER_STATUS_LABEL_MAP: Record<number, string> = {
-  0: '已取消',
+  0: '待下发',
   1: '待接单',
   2: '待出发',
-  3: '接驾中',
-  4: '待客上车',
-  5: '乘客已上车',
+  3: '待到达预约地',
+  4: '待乘客上车',
   6: '服务中',
-  7: '待客下车',
-  8: '待付款',
-  9: '待评价',
-  10: '已完成',
+}
+
+const ORDER_STATUS_STYLE_MAP: Record<string, { textColor: string, color: string }> = {
+  待下发: { textColor: '#764DFF', color: '#F6F0FF' },
+  待接单: { textColor: '#0068FF', color: '#E6F4FF' },
+  待出发: { textColor: '#0BB52D', color: '#DCF5DD' },
+  待到达预约地: { textColor: '#FF9000', color: '#FFF8E6' },
+  待乘客上车: { textColor: '#0068FF', color: '#E6F4FF' },
+  服务中: { textColor: '#0BB52D', color: '#DCF5DD' },
+  已完成: { textColor: 'rgba(23, 31, 38, 0.8)', color: 'rgba(23, 31, 38, 0.05)' },
+  已取消: { textColor: 'rgba(23, 31, 38, 0.8)', color: 'rgba(23, 31, 38, 0.05)' },
 }
 
 const filterItems = computed(() => [
@@ -227,10 +206,10 @@
   },
   {
     label: '订单状态',
-    path: 'orderStatus',
+    path: 'status',
     render: () => (
       <n-select
-        v-model:value={searchModel.value.orderStatus}
+        v-model:value={searchModel.value.status}
         options={orderStatusOptions}
         placeholder="请选择"
         clearable
@@ -243,28 +222,24 @@
   const resolvedStatusName = statusName || (statusCode != null ? ORDER_STATUS_LABEL_MAP[statusCode] : '')
   if (!resolvedStatusName)
     return { textColor: '#8C9299', color: '#F5F5F5' }
-  if (resolvedStatusName.includes('取消') || resolvedStatusName.includes('完成'))
-    return { textColor: '#8C9299', color: '#F5F5F5' }
-  if (resolvedStatusName.includes('服务') || resolvedStatusName.includes('待接单') || resolvedStatusName.includes('待出发') || resolvedStatusName.includes('接驾') || resolvedStatusName.includes('待客') || resolvedStatusName.includes('上车'))
-    return { textColor: '#0068FF', color: '#E6F4FF' }
-  if (resolvedStatusName.includes('付款') || resolvedStatusName.includes('评价'))
-    return { textColor: '#F59E0B', color: '#FFF7E6' }
-  return { textColor: '#10B981', color: '#F6FFED' }
+  return ORDER_STATUS_STYLE_MAP[resolvedStatusName] || { textColor: '#8C9299', color: '#F5F5F5' }
 }
 
-function normalizeActionLabel(actionCode: string) {
-  if (!actionCode)
-    return ''
-  if (actionCode.includes('取消订单') || actionCode.includes('关闭订单') || actionCode.includes('删除') || actionCode.includes('详情') || actionCode.includes('改派') || actionCode.includes('分派车辆'))
-    return actionCode
-  const normalizedKey = actionCode.replace(/-/g, '_')
-  return ACTION_LABEL_MAP[actionCode] || ACTION_LABEL_MAP[normalizedKey] || ACTION_LABEL_MAP[actionCode.toLowerCase()] || ACTION_LABEL_MAP[normalizedKey.toLowerCase()] || ''
-}
-
-function resolveOrderActions(actionCodes: unknown) {
-  if (!Array.isArray(actionCodes))
-    return []
-  return [...new Set(actionCodes.map(item => normalizeActionLabel(String(item))).filter(Boolean))]
+function resolveOrderActions(status?: number) {
+  switch (status) {
+    case 0:
+      return ['分派车辆', '删除']
+    case 1:
+      return ['取消订单', '详情', '删除']
+    case 2:
+    case 3:
+    case 4:
+      return ['取消订单', '改派', '删除']
+    case 6:
+      return ['关闭订单', '详情', '删除']
+    default:
+      return ['删除']
+  }
 }
 
 function buildOrderQueryParams() {
@@ -273,7 +248,7 @@
     startAddress: searchModel.value.startAddress || undefined,
     endAddress: searchModel.value.endAddress || undefined,
     plateNumber: searchModel.value.plateNumber || undefined,
-    orderStatus: searchModel.value.orderStatus ?? undefined,
+    status: searchModel.value.status ?? undefined,
     testPlanId: currentPlanId.value,
   }
 }
@@ -291,6 +266,19 @@
   return [...optionMap.entries()].map(([value, label]) => ({ value, label }))
 }
 
+function mapDispatchStatusToPlanStatus(status?: number) {
+  switch (status) {
+    case 1:
+      return 2
+    case 2:
+      return 3
+    case 3:
+      return 4
+    default:
+      return 0
+  }
+}
+
 async function fetchBaseDetail() {
   if (!currentPlanId.value)
     return
@@ -303,6 +291,9 @@
     }
     const detail = res.data || {}
     planName.value = detail.planName || '-'
+    const detailStatusCode = Number(detail.status ?? detail.planStatus ?? 0)
+    if (detailStatusCode)
+      planStatusCode.value = detailStatusCode
     pointOptions.value = buildPointOptions(detail.points)
   }
   catch (error: any) {
@@ -327,14 +318,13 @@
     }
     const monitor = res.data || {}
     dispatchStatus.value = Number(monitor.dispatchStatus || 0)
-    planStatus.value = monitor.dispatchStatusName || '-'
     interruptAllowed.value = Boolean(monitor.interruptAllowed)
     totalCount.value = Number(monitor.totalCount || 0)
     issuedCount.value = Number(monitor.issuedCount || 0)
     unissuedCount.value = Number(monitor.unissuedCount || 0)
-    const statusStyle = PLAN_STATUS_STYLE_MAP[dispatchStatus.value] || { textColor: '#8C9299', color: '#F5F5F5' }
-    planStatusColor.value = statusStyle.textColor
-    planStatusBg.value = statusStyle.color
+    const monitorPlanStatusCode = Number(monitor.status ?? monitor.planStatus ?? 0) || mapDispatchStatusToPlanStatus(dispatchStatus.value)
+    if (monitorPlanStatusCode)
+      planStatusCode.value = monitorPlanStatusCode
   }
   catch (error: any) {
     message.error(error?.message || '获取监控信息失败')
@@ -361,8 +351,8 @@
     const pageData = res.data || {}
     const records = Array.isArray(pageData.records) ? pageData.records : []
     orderData.value = records.map((item: any) => {
-      const statusCode = item.orderStatus != null ? Number(item.orderStatus) : undefined
-      const statusName = item.orderStatusName || (statusCode != null ? ORDER_STATUS_LABEL_MAP[statusCode] : '-') || '-'
+      const statusCode = item.status != null ? Number(item.status) : undefined
+      const statusName = item.statusName || (statusCode != null ? ORDER_STATUS_LABEL_MAP[statusCode] : '-') || '-'
       const statusStyle = getOrderStatusStyle(statusCode, statusName)
       return {
         orderId: item.orderId ?? item.id,
@@ -375,7 +365,7 @@
         status: statusName,
         statusColor: statusStyle.textColor,
         statusBg: statusStyle.color,
-        actions: resolveOrderActions(item.actionCodes),
+        actions: resolveOrderActions(statusCode),
       }
     })
     orderCount.value = Number(pageData.total || 0)
@@ -428,6 +418,7 @@
         : []
     vehicleData.value = records.map((item: any) => {
       const status = Number(item.vehicleStatus ?? item.paused ?? 0)
+      const statusStyle = VEHICLE_STATUS_STYLE_MAP[status] || { textColor: '#8C9299', color: '#F5F5F5' }
       return {
         id: item.vehicleId ?? item.id,
         plateNo: item.plateNumber || '-',
@@ -435,7 +426,8 @@
         fleetName: item.fleetName || '-',
         status,
         statusName: normalizeVehicleStatusName(status, item.vehicleStatusName || ''),
-        statusColor: VEHICLE_STATUS_STYLE_MAP[status] || '#8C9299',
+        statusColor: statusStyle.textColor,
+        statusBg: statusStyle.color,
       }
     })
   }
@@ -475,7 +467,7 @@
         color={{ textColor: row.statusColor, color: row.statusBg }}
         bordered={false}
         size="small"
-        style="border-radius: 4px;"
+        style="border-radius: 4px; padding: 0 8px; height: 24px; line-height: 24px; font-weight: 400;"
       >
         {row.status}
       </NTag>
@@ -507,7 +499,16 @@
     title: '状态',
     key: 'statusName',
     align: 'left',
-    render: row => <span style={{ color: row.statusColor }}>{row.statusName}</span>,
+    render: row => (
+      <NTag
+        color={{ textColor: row.statusColor, color: row.statusBg }}
+        bordered={false}
+        size="small"
+        style="border-radius: 4px; padding: 0 8px; height: 24px; line-height: 24px; font-weight: 400;"
+      >
+        {row.statusName}
+      </NTag>
+    ),
   },
 ])
 
@@ -526,7 +527,7 @@
     startAddress: null,
     endAddress: null,
     plateNumber: null,
-    orderStatus: null,
+    status: null,
   }
   orderPageParams.value.pageNum = 1
   fetchOrderList()
@@ -560,10 +561,6 @@
   finally {
     exportLoading.value = false
   }
-}
-
-function handleOperationLog() {
-  message.info('操作日志待接入')
 }
 
 async function handleDeleteOrders(orderIds: Array<number | string>) {
@@ -615,6 +612,7 @@
 
   if (action === '分派车辆' || action === '改派') {
     currentAssignOrderId.value = row.orderId
+    currentAssignActionType.value = action === '改派' ? 'reassign' : 'assign'
     isAssignVehicleModalVisible.value = true
     return
   }
@@ -672,6 +670,7 @@
 async function handleAssignVehicleSuccess() {
   isAssignVehicleModalVisible.value = false
   currentAssignOrderId.value = null
+  currentAssignActionType.value = 'assign'
   await fetchOrderList()
 }
 
@@ -682,14 +681,14 @@
 function handleInterrupt() {
   if (!currentPlanId.value)
     return
-  if (dispatchStatus.value === 1 && !interruptAllowed.value) {
-    message.warning('当前状态不允许中断下发')
+  if (planStatusCode.value === 2 && !interruptAllowed.value) {
+    message.warning('当前状态不允许中断测试')
     return
   }
 
-  const isResume = dispatchStatus.value === 2
+  const isResume = planStatusCode.value === 3
   dialog.warning({
-    title: isResume ? '确认继续下发吗?' : '确认中断下发吗?',
+    title: isResume ? '确认继续测试吗?' : '确认中断测试吗?',
     content: isResume ? '继续后将恢复已冻结订单的下发' : '中断后将冻结所有待下发订单',
     positiveText: '确定',
     negativeText: '取消',
@@ -699,13 +698,13 @@
           ? await resumeTestPlanDispatch(currentPlanId.value)
           : await interruptTestPlanDispatch(currentPlanId.value)
         if (res.code !== 200 && res.code !== 0) {
-          throw new Error(res.msg || (isResume ? '继续下发失败' : '中断下发失败'))
+          throw new Error(res.msg || (isResume ? '继续测试失败' : '中断测试失败'))
         }
-        message.success(isResume ? '继续下发成功' : '中断下发成功')
+        message.success(isResume ? '继续测试成功' : '中断测试成功')
         await Promise.all([fetchMonitorInfo(), fetchOrderList()])
       }
       catch (error: any) {
-        message.error(error?.message || (isResume ? '继续下发失败' : '中断下发失败'))
+        message.error(error?.message || (isResume ? '继续测试失败' : '中断测试失败'))
       }
     },
   })
@@ -745,12 +744,12 @@
             size="small"
             :bordered="false"
             :style="{
-              color: planStatusColor,
-              backgroundColor: planStatusBg,
+              color: planStatusStyle.textColor,
+              backgroundColor: planStatusStyle.color,
               borderRadius: '4px'
             }"
           >
-            {{ planStatus }}
+            {{ planStatusText }}
           </n-tag>
         </div>
         <div class="flex items-center gap-24px">
@@ -812,8 +811,6 @@
           <!-- 操作按钮区 -->
           <div class="flex gap-8px mb-12px shrink-0">
             <n-button :loading="exportLoading" @click="handleExportOrders">导出订单</n-button>
-            <n-button @click="handleOperationLog">操作日志</n-button>
-            <n-button @click="confirmDeleteOrders(orderCheckedRowKeys)">批量删除</n-button>
           </div>
 
           <!-- 表格 -->
@@ -901,6 +898,7 @@
       v-model:visible="isAssignVehicleModalVisible"
       :plan-id="currentPlanId"
       :order-id="currentAssignOrderId"
+      :action-type="currentAssignActionType"
       @success="handleAssignVehicleSuccess"
     />
 
diff --git a/src/views/testDemo/testPlanManagement/index.vue b/src/views/testDemo/testPlanManagement/index.vue
index 137aaf9..41a28e7 100644
--- a/src/views/testDemo/testPlanManagement/index.vue
+++ b/src/views/testDemo/testPlanManagement/index.vue
@@ -5,7 +5,8 @@
 import { useRouter } from 'vue-router'
 import { useBoolean } from '@/hooks'
 import { useDialog, useMessage } from 'naive-ui'
-import { fetchTestPlanPageList } from '@/service/api/testPlanManagement'
+import { closeTestPlanDispatch, deleteTestPlan, fetchTestPlanPageList, interruptTestPlanDispatch, resumeTestPlanDispatch } from '@/service/api/testPlanManagement'
+import { getTestPlanStatusLabel, getTestPlanStatusStyle, TEST_PLAN_STATUS_OPTIONS } from './constants'
 
 const router = useRouter()
 const dialog = useDialog()
@@ -37,33 +38,12 @@
 
 const tableScrollX = 1200
 
-const statusOptions = [
-  { label: '待下发', value: 1 },
-  { label: '测试中', value: 2 },
-  { label: '已暂停', value: 3 },
-  { label: '关闭测试', value: 4 },
-  { label: '已完成', value: 5 },
-]
-
 function getStatusLabel(status: number) {
-  return statusOptions.find(item => item.value === status)?.label || '-'
+  return getTestPlanStatusLabel(status)
 }
 
 function getStatusColor(status: number) {
-  switch (status) {
-    case 1: // 待下发 (紫色)
-      return { textColor: '#A034FF', color: '#F9F0FF' }
-    case 2: // 测试中 (蓝色)
-      return { textColor: '#0068FF', color: '#E6F4FF' }
-    case 3: // 已暂停 (橙色)
-      return { textColor: '#F59E0B', color: '#FFF7E6' }
-    case 4: // 关闭测试 (灰色)
-      return { textColor: '#9CA3AF', color: '#F5F5F5' }
-    case 5: // 已完成 (绿色)
-      return { textColor: '#10B981', color: '#F6FFED' }
-    default:
-      return { textColor: '#333333', color: '#F5F5F5' }
-  }
+  return getTestPlanStatusStyle(status)
 }
 
 function handleResetSearch() {
@@ -91,9 +71,9 @@
     listData.value = records.map((item: any) => ({
       id: item.id,
       planName: item.planName || '-',
-      pointNum: Number(item.pointNum ?? item.pointCount ?? item.bindPointNum ?? 0),
-      vehicleNum: Number(item.vehicleNum ?? item.bindVehicleNum ?? 0),
-      orderNum: Number(item.orderNum ?? item.testOrderNum ?? 0),
+      pointCount: Number(item.pointCount ?? 0),
+      vehicleCount: Number(item.vehicleCount ?? 0),
+      orderCount: Number(item.orderCount ?? 0),   
       status: Number(item.status ?? 0),
     }))
     count.value = Number(res.data?.total ?? records.length ?? 0)
@@ -146,9 +126,18 @@
     maskClosable: false,
     autoFocus: false,
     onPositiveClick: async () => {
-      // TODO: 调用删除接口
-      message.success('删除成功')
-      getList()
+      try {
+        const res = await deleteTestPlan(row.id)
+        if (res.code !== 200 && res.code !== 0) {
+          throw new Error(res.msg || '删除失败')
+        }
+        message.success('删除成功')
+        await getList()
+      }
+      catch (error: any) {
+        message.error(error?.message || '删除失败')
+        throw error
+      }
     },
   })
 }
@@ -159,10 +148,20 @@
     content: '中断后测试计划将暂停',
     positiveText: '确定',
     negativeText: '取消',
-    onPositiveClick: () => {
-      message.success('已中断')
-      getList()
-    }
+    onPositiveClick: async () => {
+      try {
+        const res = await interruptTestPlanDispatch(row.id)
+        if (res.code !== 200 && res.code !== 0) {
+          throw new Error(res.msg || '中断测试失败')
+        }
+        message.success('已中断')
+        await getList()
+      }
+      catch (error: any) {
+        message.error(error?.message || '中断测试失败')
+        throw error
+      }
+    },
   })
 }
 
@@ -172,10 +171,20 @@
     content: '关闭后测试计划将终止',
     positiveText: '确定',
     negativeText: '取消',
-    onPositiveClick: () => {
-      message.success('已关闭')
-      getList()
-    }
+    onPositiveClick: async () => {
+      try {
+        const res = await closeTestPlanDispatch(row.id)
+        if (res.code !== 200 && res.code !== 0) {
+          throw new Error(res.msg || '关闭测试失败')
+        }
+        message.success('已关闭')
+        await getList()
+      }
+      catch (error: any) {
+        message.error(error?.message || '关闭测试失败')
+        throw error
+      }
+    },
   })
 }
 
@@ -185,10 +194,20 @@
     content: '测试计划将继续执行',
     positiveText: '确定',
     negativeText: '取消',
-    onPositiveClick: () => {
-      message.success('已继续')
-      getList()
-    }
+    onPositiveClick: async () => {
+      try {
+        const res = await resumeTestPlanDispatch(row.id)
+        if (res.code !== 200 && res.code !== 0) {
+          throw new Error(res.msg || '继续测试失败')
+        }
+        message.success('已继续')
+        await getList()
+      }
+      catch (error: any) {
+        message.error(error?.message || '继续测试失败')
+        throw error
+      }
+    },
   })
 }
 
@@ -227,7 +246,7 @@
   },
   {
     title: '关联点位',
-    key: 'pointNum',
+    key: 'pointCount',
     width: 140,
     align: 'left',
     resizable: true,
@@ -235,7 +254,7 @@
   },
   {
     title: '关联车辆数',
-    key: 'vehicleNum',
+    key: 'vehicleCount',
     width: 140,
     align: 'left',
     resizable: true,
@@ -243,7 +262,7 @@
   },
   {
     title: '测试订单数',
-    key: 'orderNum',
+    key: 'orderCount',
     width: 140,
     align: 'left',
     resizable: true,
@@ -281,7 +300,7 @@
         actions.push(<NButton text type="primary" onClick={() => handleClose(row)}>关闭测试</NButton>)
         actions.push(<NButton text type="primary" onClick={() => handleContinue(row)}>继续测试</NButton>)
       }
-      if (row.status === 4 || row.status === 5) { // 关闭测试 / 已完成
+      if (row.status === 4 || row.status === 5) { // 已完成 / 关闭测试
         actions.push(<NButton text type="primary" onClick={() => handleDetail(row)}>详情</NButton>)
       }
 
@@ -308,7 +327,7 @@
     render: () => (
       <n-select
         v-model:value={model.value.status}
-        options={statusOptions}
+        options={TEST_PLAN_STATUS_OPTIONS}
         placeholder="请选择"
         clearable
         onUpdate:show={(val: boolean) => isStatusSelectOpen.value = val}
@@ -407,7 +426,7 @@
       </div>
     </n-card>
 
-    <OperationLogModal v-model:show="showOperationLogModal" />
+    <OperationLogModal v-model:show="showOperationLogModal" :business-type="48" />
   </div>
 </template>
 
diff --git a/src/views/userContent/whitelistManagement/whitelist/components/WhitelistEditModal.vue b/src/views/userContent/whitelistManagement/whitelist/components/WhitelistEditModal.vue
index ce20f16..01ae866 100644
--- a/src/views/userContent/whitelistManagement/whitelist/components/WhitelistEditModal.vue
+++ b/src/views/userContent/whitelistManagement/whitelist/components/WhitelistEditModal.vue
@@ -11,16 +11,18 @@
   NModal,
   NSpace,
   NSwitch,
-  useDialog,
   useMessage,
 } from 'naive-ui'
 import type { FormInst, FormRules } from 'naive-ui'
 import dayjs from 'dayjs'
-import { editWhitelist } from '@/service/api/whitelist'
+import { addWhitelist, editWhitelist } from '@/service/api/whitelist'
+
+type ModalMode = 'add' | 'edit'
 
 interface Props {
   visible?: boolean
   rowData?: any
+  mode?: ModalMode
 }
 
 interface Emits {
@@ -33,6 +35,7 @@
 const props = withDefaults(defineProps<Props>(), {
   visible: false,
   rowData: null,
+  mode: 'add',
 })
 
 // 弹窗对外事件:
@@ -41,6 +44,10 @@
 const emit = defineEmits<Emits>()
 // 全局消息提示实例。
 const message = useMessage()
+// 当前弹窗模式。
+const isEditMode = computed(() => props.mode === 'edit')
+// 弹窗标题统一跟随模式变化。
+const modalTitle = computed(() => isEditMode.value ? '编辑白名单' : '新增白名单')
 
 // 编辑表单数据结构。
 // 这里保留了 status 字段,虽然当前编辑样式未展示该开关,但保存时仍需沿用原始状态值,避免误改。
@@ -125,38 +132,44 @@
 
 // 监听弹窗显隐与传入数据。
 // 每次打开编辑弹窗时都根据当前 rowData 重新生成表单,避免上一次编辑内容串到下一条数据。
+function createFormModelFromRow(rowData: any): FormModel {
+  return {
+    ...createDefaultFormModel(),
+    account: rowData?.account || '',
+    nickName: rowData?.nickName || '',
+    status: rowData?.status === 1 || rowData?.status === true,
+    isPriorityDispatch:
+      rowData?.isPriorityDispatch === 1
+      || rowData?.isPriorityDispatch === true,
+    // 编辑弹窗同时兼容列表页、详情页等不同来源字段命名,优先按当前业务字段回显,缺失时逐级兜底。
+    expireDate: formatDateValue(
+      rowData?.expireDate
+      ?? rowData?.effectiveDate
+      ?? rowData?.validityDate
+      ?? rowData?.expireTime,
+    ),
+    // 最大下单量存在不同字段名,统一收口到表单内部的 maxOrderCount。
+    maxOrderCount: toOptionalNumber(
+      rowData?.maxOrderCount
+      ?? rowData?.maxOrderNum
+      ?? rowData?.orderLimitCount,
+    ),
+    isTestUser: rowData?.isTestUser === 1 || rowData?.isTestUser === true,
+    remark: rowData?.remark || '',
+  }
+}
+
 watch(
-  () => props.visible,
-  (val) => {
-    if (val && props.rowData) {
-      formModel.value = {
-        ...createDefaultFormModel(),
-        account: props.rowData.account || '',
-        nickName: props.rowData.nickName || '',
-        status: props.rowData.status === 1 || props.rowData.status === true,
-        isPriorityDispatch:
-          props.rowData.isPriorityDispatch === 1
-          || props.rowData.isPriorityDispatch === true,
-        // 编辑弹窗同时兼容列表页、详情页等不同来源字段命名,优先按当前业务字段回显,缺失时逐级兜底。
-        expireDate: formatDateValue(
-          props.rowData.expireDate
-          ?? props.rowData.effectiveDate
-          ?? props.rowData.validityDate
-          ?? props.rowData.expireTime,
-        ),
-        // 最大下单量存在不同字段名,统一收口到表单内部的 maxOrderCount。
-        maxOrderCount: toOptionalNumber(
-          props.rowData.maxOrderCount
-          ?? props.rowData.maxOrderNum
-          ?? props.rowData.orderLimitCount,
-        ),
-        isTestUser: props.rowData.isTestUser === 1 || props.rowData.isTestUser === true,
-        remark: props.rowData.remark || '',
-      }
-    }
-    else {
+  () => [props.visible, props.rowData, props.mode],
+  ([visible]) => {
+    if (!visible) {
       formModel.value = createDefaultFormModel()
+      return
     }
+
+    formModel.value = isEditMode.value && props.rowData
+      ? createFormModelFromRow(props.rowData)
+      : createDefaultFormModel()
   },
 )
 
@@ -177,21 +190,35 @@
   try {
     await formRef.value?.validate()
 
-    const res = await editWhitelist({
-      id: props.rowData.id,
+    const editPayload = {
       account: formModel.value.account,
       nickName: formModel.value.nickName,
-      // 状态开关当前未展示在编辑 UI 中,但仍沿用表单模型中的状态,避免把原始状态丢掉。
       status: formModel.value.status ? 1 : 0,
       isPriorityDispatch: formModel.value.isPriorityDispatch ? 1 : 0,
       expireTime: formModel.value.expireDate ? dayjs(formModel.value.expireDate).format('YYYY-MM-DD') : undefined,
       maxOrderNum: formModel.value.maxOrderCount ?? undefined,
       isTestUser: formModel.value.isTestUser ? 1 : 0,
       remark: formModel.value.remark,
-    })
+    }
+
+    const res = isEditMode.value
+      ? await editWhitelist({
+          id: props.rowData.id,
+          ...editPayload,
+        })
+      : await addWhitelist({
+          account: formModel.value.account,
+          nickName: formModel.value.nickName,
+          status: formModel.value.status,
+          isPriorityDispatch: formModel.value.isPriorityDispatch,
+          expireTime: formModel.value.expireDate ? dayjs(formModel.value.expireDate).format('YYYY-MM-DD') : undefined,
+          maxOrderNum: formModel.value.maxOrderCount ?? undefined,
+          isTestUser: formModel.value.isTestUser,
+          remark: formModel.value.remark || undefined,
+        })
 
     if (res.code === 200) {
-      message.success('编辑白名单成功')
+      message.success(res.msg || `${isEditMode.value ? '编辑' : '新增'}白名单成功`)
       emit('success')
       closeModal()
     }
@@ -207,7 +234,7 @@
     :show="visible"
     :mask-closable="false"
     preset="card"
-    title="编辑白名单"
+    :title="modalTitle"
     class="whitelist-modal"
     :style="{ width: '600px' }"
     :segmented="{
diff --git a/src/views/userContent/whitelistManagement/whitelist/index.vue b/src/views/userContent/whitelistManagement/whitelist/index.vue
index bbf745d..e90ee23 100644
--- a/src/views/userContent/whitelistManagement/whitelist/index.vue
+++ b/src/views/userContent/whitelistManagement/whitelist/index.vue
@@ -1,11 +1,10 @@
 <script setup lang="tsx">
-import type { DataTableColumns, FormInst } from 'naive-ui'
+import type { DataTableColumns } from 'naive-ui'
 import { NButton, NTag, useDialog, useMessage } from 'naive-ui'
 import { useBoolean } from '@/hooks'
 import { downloadFile } from '@/utils'
 import SearchPanel from '@/components/custom/SearchPanel.vue'
 import OperationLogModal from '@/components/custom/OperationLogModal.vue'
-import WhitelistAddModal from './components/WhitelistAddModal.vue'
 import WhitelistEditModal from './components/WhitelistEditModal.vue'
 import {
   deleteWhitelistByIds,
@@ -60,10 +59,10 @@
 const totalCount = ref(0)
 // 表格当前勾选行主键集合,用于批量冻结和批量导出。
 const selectedRowKeys = ref<number[]>([])
-// 新增弹窗显示状态。新增与编辑拆分后,避免单个弹窗内部维护过多分支。
-const showAddModal = ref(false)
-// 编辑弹窗显示状态。编辑弹窗单独承载编辑样式和字段,和新增逻辑彻底解耦。
-const showEditModal = ref(false)
+// 新增/编辑共用弹窗显示状态。
+const showFormModal = ref(false)
+// 当前弹窗模式:新增或编辑。
+const modalMode = ref<'add' | 'edit'>('add')
 // 当前操作的行数据。编辑、详情等动作都会基于该对象获取当前上下文。
 const currentRow = ref<any>(null)
 // 分页组件刷新 key,当前预留给需要强制刷新分页内部状态的场景。
@@ -354,10 +353,11 @@
 }
 
 // 打开新增弹窗。
-// 新增场景不依赖 currentRow,因此先清空当前行上下文,避免残留编辑数据。
+// 新增场景复用编辑样式弹窗,但表单数据从空白状态开始。
 function handleAdd() {
+  modalMode.value = 'add'
   currentRow.value = null
-  showAddModal.value = true
+  showFormModal.value = true
 }
 
 // 导入白名单。
@@ -450,8 +450,9 @@
     message.warning('冻结状态不允许编辑')
     return
   }
+  modalMode.value = 'edit'
   currentRow.value = row
-  showEditModal.value = true
+  showFormModal.value = true
 }
 
 // 弹窗保存成功回调。
@@ -679,15 +680,10 @@
       </div>
     </n-card>
 
-    <!-- 新增白名单弹窗 -->
-    <WhitelistAddModal
-      v-model:visible="showAddModal"
-      @success="handleModalSuccess"
-    />
-
-    <!-- 编辑白名单弹窗 -->
+    <!-- 新增/编辑白名单弹窗 -->
     <WhitelistEditModal
-      v-model:visible="showEditModal"
+      v-model:visible="showFormModal"
+      :mode="modalMode"
       :row-data="currentRow"
       @success="handleModalSuccess"
     />

--
Gitblit v1.7.1