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