ソースを参照

fix:修复订单管理审核失败的问题

yangyi 4 ヶ月 前
親
コミット
dd5868ec06

+ 2 - 2
src/router/index.ts

@@ -150,7 +150,7 @@ export const adminRoutes: RouteRecordRaw[] = [
                 name: 'adminOrderList',
                 component: AdminOrderListView,
                 meta: {
-                    title: '订单审核',
+                    title: '预约订单管理',
                     icon: List
                 }
             },
@@ -158,7 +158,7 @@ export const adminRoutes: RouteRecordRaw[] = [
                 path: '/admin/orders/:id',
                 name: 'adminOrderDetail',
                 component: AdminOrderDetailView,
-                meta: { title: '订单审核详情' }
+                meta: { title: '订单详情' }
             },
             {
                 path: '/admin/rooms',

+ 27 - 0
src/type/index.ts

@@ -55,6 +55,7 @@ export interface RoomStatus {
 export interface Appointment {
     id: string
     userId: string
+    userName?: string
     petId: string
     petName?: string
     packageId: string
@@ -74,6 +75,32 @@ export interface Appointment {
     createdAt: string
 }
 
+export const statusLabelMap: Record<string, string> = {
+  PENDING: '待审核',
+  CONFIRMED: '已确认',
+  CANCELLED: '已取消',
+  COMPLETED: '已完成',
+  CANCEL_REQUESTED: '取消申请'
+}
+
+export const statusTagMap: Record<string, string> = {
+  PENDING: 'warning',
+  CONFIRMED: 'success',
+  CANCELLED: 'info',
+  COMPLETED: 'primary',
+  CANCEL_REQUESTED: 'danger'
+}
+
+export const typeLabelMap: Record<string, string> = {
+  BOARDING: '寄养',
+  GROOMING: '美容'
+}
+
+export const typeLabel = (type: string) => typeLabelMap[type] || type
+
+export const statusLabel = (status: string) => statusLabelMap[status] || status
+export const statusTag = (status: string) => statusTagMap[status] || 'info'
+
 export interface Schedule {
     id: string
     designerName: string

+ 10 - 30
src/view/admin/AdminOrderDetailView.vue

@@ -31,8 +31,8 @@
       <el-form>
         <el-form-item label="审核结果">
           <el-radio-group v-model="reviewAction">
-            <el-radio value="CONFIRMED">通过</el-radio>
-            <el-radio value="CANCELLED">拒绝</el-radio>
+            <el-radio value="APPROVE">通过</el-radio>
+            <el-radio value="REJECT">拒绝</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="原因">
@@ -50,8 +50,8 @@
       <el-form>
         <el-form-item label="处理结果">
           <el-radio-group v-model="cancelAction">
-            <el-radio value="CANCELLED">同意取消</el-radio>
-            <el-radio value="CONFIRMED">拒绝取消</el-radio>
+            <el-radio value="AGREE">同意取消</el-radio>
+            <el-radio value="REJECT">拒绝取消</el-radio>
           </el-radio-group>
         </el-form-item>
       </el-form>
@@ -68,6 +68,7 @@ import { ref, onMounted } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import type { Appointment } from '@/type'
+import { statusLabel, statusTag, typeLabel } from '@/type'
 import { getAppointmentController } from '@/api/appointment-controller'
 import { getAdminAppointmentController } from '@/api/admin-appointment-controller'
 import { getPetController } from '@/api/pet-controller'
@@ -80,11 +81,11 @@ const order = ref<Appointment | null>(null)
 const loading = ref(false)
 
 const reviewVisible = ref(false)
-const reviewAction = ref('CONFIRMED')
+const reviewAction = ref('APPROVE')
 const reviewReason = ref('')
 
 const cancelVisible = ref(false)
-const cancelAction = ref('CANCELLED')
+const cancelAction = ref('AGREE')
 
 const loadOrder = async () => {
   loading.value = true
@@ -132,7 +133,7 @@ const loadOrder = async () => {
 }
 
 const openReview = () => {
-  reviewAction.value = 'CONFIRMED'
+  reviewAction.value = 'APPROVE'
   reviewReason.value = ''
   reviewVisible.value = true
 }
@@ -143,7 +144,7 @@ const submitReview = async () => {
     const api = getAdminAppointmentController()
     const dto: ReviewDto = {
       action: reviewAction.value,
-      rejectReason: reviewAction.value === 'CANCELLED' ? reviewReason.value : undefined
+      rejectReason: reviewAction.value === 'REJECT' ? reviewReason.value : undefined
     }
     const res = await api.review(Number(order.value.id), dto)
     if (res.code === 200) {
@@ -159,7 +160,7 @@ const submitReview = async () => {
 }
 
 const openCancelHandle = () => {
-  cancelAction.value = 'CANCELLED'
+  cancelAction.value = 'AGREE'
   cancelVisible.value = true
 }
 
@@ -181,37 +182,16 @@ const submitCancel = async () => {
   }
 }
 
-const typeLabel = (type: string) => {
-  return type === 'BOARDING' ? '寄养' : '美容'
-}
-
 const formatDate = (date?: string) => {
   if (!date) return '-'
   return date.replace('T', ' ').substring(0, 16)
 }
 
-const statusLabel = (status: string) => {
-  const map: Record<string, string> = {
-    PENDING: '待审核', CONFIRMED: '已确认', CANCELLED: '已取消',
-    COMPLETED: '已完成', CANCEL_REQUESTED: '取消申请'
-  }
-  return map[status] || status
-}
-
-const statusTag = (status: string) => {
-  const map: Record<string, string> = {
-    PENDING: 'warning', CONFIRMED: 'success', CANCELLED: 'info',
-    COMPLETED: 'primary', CANCEL_REQUESTED: 'danger'
-  }
-  return map[status] || 'info'
-}
-
 onMounted(loadOrder)
 </script>
 
 <style scoped>
 .admin-order-detail {
-  max-width: 800px;
   padding: 20px;
 }
 

+ 114 - 35
src/view/admin/AdminOrderListView.vue

@@ -1,24 +1,46 @@
 <template>
   <div class="admin-order-list">
-    <h1>订单审核</h1>
+    <h1>订单管理</h1>
 
     <div class="toolbar">
-      <el-select v-model="statusFilter" placeholder="状态筛选" clearable @change="fetchOrders">
-        <el-option label="全部" value="" />
+      <el-select v-model="statusFilter" placeholder="状态筛选" clearable style="width: 140px">
+        <el-option label="全部状态" value="" />
         <el-option label="待审核" value="PENDING" />
         <el-option label="已确认" value="CONFIRMED" />
         <el-option label="已取消" value="CANCELLED" />
         <el-option label="取消申请" value="CANCEL_REQUESTED" />
         <el-option label="已完成" value="COMPLETED" />
       </el-select>
+      <el-select v-model="typeFilter" placeholder="类型筛选" clearable style="width: 140px; margin-left: 12px">
+        <el-option label="全部类型" value="" />
+        <el-option label="寄养" value="BOARDING" />
+        <el-option label="美容" value="GROOMING" />
+      </el-select>
+      <el-button type="primary" style="margin-left: 12px" @click="handleSearch">搜索</el-button>
+      <el-button @click="handleReset">重置</el-button>
     </div>
 
-    <el-table :data="orders" border stripe v-loading="loading">
+    <el-table :data="orders" border stripe v-loading="loading" style="width: 100%">
       <el-table-column prop="id" label="ID" width="80" />
-      <el-table-column prop="userId" label="用户" width="120" />
-      <el-table-column prop="type" label="类型" width="100" />
-      <el-table-column prop="totalPrice" label="总价" width="100" />
-      <el-table-column prop="status" label="状态" width="140">
+      <el-table-column label="用户" min-width="120">
+        <template #default="{ row }">
+          {{ row.userName || row.userId }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="petName" label="宠物" min-width="100" />
+      <el-table-column label="类型" min-width="80">
+        <template #default="{ row }">
+          {{ typeLabel(row.type) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="服务日期" min-width="180">
+        <template #default="{ row }">
+          {{ row.startDate?.replace('T', ' ')?.substring(0, 10) }} ~ {{ row.endDate?.replace('T', ' ')?.substring(0, 10) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="roomName" label="房间" min-width="100" />
+      <el-table-column prop="totalPrice" label="总价" min-width="100" />
+      <el-table-column prop="status" label="状态" min-width="120">
         <template #default="{ row }">
           <el-tag :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag>
         </template>
@@ -47,8 +69,8 @@
       <el-form>
         <el-form-item label="审核结果">
           <el-radio-group v-model="reviewAction">
-            <el-radio value="CONFIRMED">通过</el-radio>
-            <el-radio value="CANCELLED">拒绝</el-radio>
+          <el-radio value="APPROVE">通过</el-radio>
+          <el-radio value="REJECT">拒绝</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="原因">
@@ -67,8 +89,8 @@
       <el-form>
         <el-form-item label="处理结果">
           <el-radio-group v-model="cancelAction">
-            <el-radio value="CANCELLED">同意取消</el-radio>
-            <el-radio value="CONFIRMED">拒绝取消</el-radio>
+          <el-radio value="AGREE">同意取消</el-radio>
+          <el-radio value="REJECT">拒绝取消</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="处理意见">
@@ -88,30 +110,92 @@ import { ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import type { Appointment } from '@/type'
+import { statusLabel, statusTag, typeLabel } from '@/type'
 import { getAdminAppointmentController } from '@/api/admin-appointment-controller'
+import { getUserController } from '@/api/user-controller'
+import { getPetController } from '@/api/pet-controller'
+import { getBoardingRoomController } from '@/api/boarding-room-controller'
 import type { ListAll1Params, ReviewDto, HandleCancelDto } from '@/api/models'
 
 const router = useRouter()
 const orders = ref<Appointment[]>([])
 const loading = ref(false)
 const statusFilter = ref('')
+const typeFilter = ref('')
 const pageNum = ref(1)
 const pageSize = ref(10)
 const total = ref(0)
 
 // Review dialog
 const reviewVisible = ref(false)
-const reviewAction = ref('CONFIRMED')
+const reviewAction = ref('APPROVE')
 const reviewReason = ref('')
 const reviewingOrder = ref<Appointment | null>(null)
 
 // Cancel dialog
 const cancelVisible = ref(false)
-const cancelAction = ref('CANCELLED')
+const cancelAction = ref('AGREE')
 const cancelOpinion = ref('')
 const cancelReason = ref('')
 const cancelOrder = ref<Appointment | null>(null)
 
+const enrichOrders = async (list: Appointment[]) => {
+  if (!list.length) return
+  const userIds = [...new Set(list.map((o) => Number(o.userId)).filter(Boolean))]
+  const petIds = [...new Set(list.map((o) => Number(o.petId)).filter(Boolean))]
+  const roomIds = [
+    ...new Set(list.map((o) => (o.roomId ? Number(o.roomId) : null)).filter(Boolean))
+  ] as number[]
+
+  const userMap = new Map<number, string>()
+  if (userIds.length) {
+    const results = await Promise.allSettled(
+      userIds.map((id) => getUserController().queryById(id))
+    )
+    for (let i = 0; i < results.length; i++) {
+      const r = results[i]
+      if (r.status === 'fulfilled') {
+        const vo = r.value as { code?: number; data?: { username?: string }; message?: string }
+        if (vo.data?.username) userMap.set(userIds[i], vo.data.username)
+      }
+    }
+  }
+
+  const petMap = new Map<number, string>()
+  if (petIds.length) {
+    const results = await Promise.allSettled(
+      petIds.map((id) => getPetController().getById(id))
+    )
+    for (let i = 0; i < results.length; i++) {
+      const r = results[i]
+      if (r.status === 'fulfilled') {
+        const vo = r.value as { code?: number; data?: { name?: string }; message?: string }
+        if (vo.data?.name) petMap.set(petIds[i], vo.data.name)
+      }
+    }
+  }
+
+  const roomMap = new Map<number, string>()
+  if (roomIds.length) {
+    const res = await getBoardingRoomController().listRooms()
+    const body = res as { code?: number; data?: Array<{ id?: number; roomName?: string }>; message?: string }
+    if (body.code === 200 && body.data) {
+      body.data.forEach((r) => {
+        if (r.id != null && r.roomName) roomMap.set(r.id, r.roomName)
+      })
+    }
+  }
+
+  list.forEach((o) => {
+    const uid = Number(o.userId)
+    if (userMap.has(uid)) o.userName = userMap.get(uid)!
+    const pid = Number(o.petId)
+    if (petMap.has(pid)) o.petName = petMap.get(pid)!
+    const rid = o.roomId ? Number(o.roomId) : null
+    if (rid != null && roomMap.has(rid)) o.roomName = roomMap.get(rid)!
+  })
+}
+
 const fetchOrders = async () => {
   loading.value = true
   try {
@@ -121,6 +205,7 @@ const fetchOrders = async () => {
       pageSize: pageSize.value
     }
     if (statusFilter.value) params.status = statusFilter.value
+    if (typeFilter.value) params.type = typeFilter.value
     const res = await api.listAll1(params)
     const page = res.data
     if (res.code === 200 && page) {
@@ -131,6 +216,9 @@ const fetchOrders = async () => {
         petId: String(a.petId ?? ''),
         packageId: String(a.packageId ?? ''),
         roomId: a.roomId ? String(a.roomId) : undefined,
+        roomName: a.roomName,
+        petName: a.petName,
+        packageName: a.packageName,
         type: (a.type as Appointment['type']) ?? 'GROOMING',
         startDate: a.startDate,
         endDate: a.endDate,
@@ -143,6 +231,7 @@ const fetchOrders = async () => {
         createdAt: a.createdAt ?? ''
       }))
       total.value = page.total ?? 0
+      await enrichOrders(orders.value)
     }
   } catch (err: any) {
     ElMessage.error(err.message || '加载失败')
@@ -153,7 +242,7 @@ const fetchOrders = async () => {
 
 const openReview = (order: Appointment) => {
   reviewingOrder.value = order
-  reviewAction.value = 'CONFIRMED'
+  reviewAction.value = 'APPROVE'
   reviewReason.value = ''
   reviewVisible.value = true
 }
@@ -164,7 +253,7 @@ const submitReview = async () => {
     const api = getAdminAppointmentController()
     const dto: ReviewDto = {
       action: reviewAction.value,
-      rejectReason: reviewAction.value === 'CANCELLED' ? reviewReason.value : undefined
+      rejectReason: reviewAction.value === 'REJECT' ? reviewReason.value : undefined
     }
     const res = await api.review(Number(reviewingOrder.value.id), dto)
     if (res.code === 200) {
@@ -182,7 +271,7 @@ const submitReview = async () => {
 const openCancelHandle = (order: Appointment) => {
   cancelOrder.value = order
   cancelReason.value = order.cancelReason || ''
-  cancelAction.value = 'CANCELLED'
+  cancelAction.value = 'AGREE'
   cancelOpinion.value = ''
   cancelVisible.value = true
 }
@@ -209,26 +298,16 @@ const viewDetail = (id: string) => {
   router.push(`/admin/orders/${id}`)
 }
 
-const statusLabel = (status: string) => {
-  const map: Record<string, string> = {
-    PENDING: '待审核',
-    CONFIRMED: '已确认',
-    CANCELLED: '已取消',
-    COMPLETED: '已完成',
-    CANCEL_REQUESTED: '取消申请'
-  }
-  return map[status] || status
+const handleSearch = () => {
+  pageNum.value = 1
+  fetchOrders()
 }
 
-const statusTag = (status: string) => {
-  const map: Record<string, string> = {
-    PENDING: 'warning',
-    CONFIRMED: 'success',
-    CANCELLED: 'info',
-    COMPLETED: 'primary',
-    CANCEL_REQUESTED: 'danger'
-  }
-  return map[status] || 'info'
+const handleReset = () => {
+  statusFilter.value = ''
+  typeFilter.value = ''
+  pageNum.value = 1
+  fetchOrders()
 }
 
 fetchOrders()