Bläddra i källkod

订单列表展示内容完成一半

Huanyi 1 månad sedan
förälder
incheckning
84da317511

+ 2 - 0
src/api/order/subOrder/types.ts

@@ -25,10 +25,12 @@ export interface SubOrderVO {
     status: number;
     fulfiller: number;
     fulfillerName: string;
+    fulfillerStatus?: 'resting' | 'busy' | 'disabled';
     price: number;
     // 以下为可能需要用到的扩充字段以兼顾页面展现
     type?: string;
     transportType?: string;
     splitType?: string;
     detail?: any;
+    serviceTime?: string;
 }

+ 11 - 0
src/api/order/subOrderLog/index.ts

@@ -0,0 +1,11 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SubOrderLogVO, SubOrderLogQuery } from './types';
+
+export const listSubOrderLog = (query: SubOrderLogQuery): AxiosPromise<SubOrderLogVO[]> => {
+    return request({
+        url: '/order/subOrderLog/list',
+        method: 'get',
+        params: query
+    });
+};

+ 15 - 0
src/api/order/subOrderLog/types.ts

@@ -0,0 +1,15 @@
+export interface SubOrderLogVO {
+    id: number;
+    subOrderId: number;
+    actioner: number;
+    actionerType: number;
+    logType: number;
+    actionType: number;
+    title: string;
+    content: string;
+    photos?: string;
+}
+
+export interface SubOrderLogQuery {
+    orderId: string | number;
+}

+ 111 - 43
src/views/order/dispatch/index.vue

@@ -5,19 +5,15 @@
       <div class="filter-left">
         <el-radio-group v-model="filters.orderType" size="default" fill="#2d8cf0">
           <el-radio-button label="all">全部</el-radio-button>
-          <el-radio-button label="transport">宠物接送</el-radio-button>
-          <el-radio-button label="feeding">上门喂遛</el-radio-button>
-          <el-radio-button label="washing">上门洗护</el-radio-button>
+          <el-radio-button v-for="item in serviceOnOrderList" :key="item.id" :label="item.id">{{ item.name
+          }}</el-radio-button>
         </el-radio-group>
       </div>
       <div class="filter-right">
-        <el-select v-model="filters.city" placeholder="选择城市" style="width: 120px; margin-right: 10px">
-          <el-option label="北京市" value="beijing" />
-          <el-option label="上海市" value="shanghai" />
-        </el-select>
-        <el-select v-model="filters.station" placeholder="选择站点" style="width: 140px">
-          <el-option label="朝阳大悦城站" value="cy" />
-          <el-option label="国贸核心站" value="gm" />
+        <el-cascader v-model="regionValue" :options="areaOptions" placeholder="省市区域" clearable
+          style="width: 170px; margin-right: 10px" @change="handleAreaChange" />
+        <el-select v-model="filters.station" placeholder="站点" clearable style="width: 170px">
+          <el-option v-for="item in siteOptions" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
       </div>
     </div>
@@ -31,9 +27,12 @@
         <div class="map-controls-panel">
           <div class="control-group">
             <div class="c-btn" :class="{ active: activeMapFilter === 'all' }" @click="setMapFilter('all')">全部</div>
-            <div class="c-btn red" :class="{ active: activeMapFilter === 'merchants' }" @click="setMapFilter('merchants')">商家(16)</div>
-            <div class="c-btn green" :class="{ active: activeMapFilter === 'fulfillers' }" @click="setMapFilter('fulfillers')">履约者(12)</div>
-            <div class="c-btn blue" :class="{ active: activeMapFilter === 'orders' }" @click="setMapFilter('orders')">订单(5)</div>
+            <div class="c-btn red" :class="{ active: activeMapFilter === 'merchants' }"
+              @click="setMapFilter('merchants')">商家(16)</div>
+            <div class="c-btn green" :class="{ active: activeMapFilter === 'fulfillers' }"
+              @click="setMapFilter('fulfillers')">履约者(12)</div>
+            <div class="c-btn blue" :class="{ active: activeMapFilter === 'orders' }" @click="setMapFilter('orders')">
+              订单(5)</div>
             <div class="c-btn gray">灰色表示离线</div>
           </div>
         </div>
@@ -41,42 +40,26 @@
 
       <!-- Right: Dispatch Control Panel -->
       <div class="right-panel">
-        <OrderListPanel
-          v-model="currentOrderTab"
-          :orders="filteredOrders"
-          :stats="orderStats"
-          @focus="focusMapPoint"
-          @dispatch="openDispatchDialog"
-        />
-
-        <RiderListPanel
-          v-model="currentRiderTab"
-          :riders="filteredRiders"
-          @focus="focusMapPoint"
-          @view-orders="handleViewRiderOrders"
-        />
+        <OrderListPanel v-model="currentOrderTab" :orders="filteredOrders" :stats="orderStats" @focus="focusMapPoint"
+          @dispatch="openDispatchDialog" />
+
+        <RiderListPanel v-model="currentRiderTab" :riders="filteredRiders" @focus="focusMapPoint"
+          @view-orders="handleViewRiderOrders" />
       </div>
     </div>
 
-    <RiderOrdersDialog
-      v-model="riderOrdersVisible"
-      :riderInfo="currentRiderInfo"
-      :orders="currentRiderOrders"
-    />
-
-    <DispatchDialog
-      v-model="dispatchDialogVisible"
-      :order="currentDispatchOrder"
-      :currentRider="currentRider"
-      :ridersList="ridersList"
-      @submit="handleDispatchSubmit"
-    />
+    <RiderOrdersDialog v-model="riderOrdersVisible" :riderInfo="currentRiderInfo" :orders="currentRiderOrders" />
+
+    <DispatchDialog v-model="dispatchDialogVisible" :order="currentDispatchOrder" :currentRider="currentRider"
+      :ridersList="ridersList" @submit="handleDispatchSubmit" />
   </div>
 </template>
 
 <script setup>
 import { ref, computed, reactive, onMounted, watch } from 'vue';
 import { ElMessage } from 'element-plus';
+import { listServiceOnOrder } from '@/api/service/list/index'
+import { listOnStore as listAreaStationOnStore } from '@/api/system/areaStation'
 
 import OrderListPanel from './components/OrderListPanel.vue';
 import RiderListPanel from './components/RiderListPanel.vue';
@@ -90,8 +73,7 @@ import riderOrdersMockData from '@/mock/RiderOrdersDialog.json';
 // --- Data & State ---
 const filters = reactive({
   orderType: 'all',
-  city: 'beijing',
-  station: 'cy'
+  station: undefined
 });
 
 const currentOrderTab = ref('PendingDispatch');
@@ -103,6 +85,82 @@ const ridersList = ref(dispatchMockData.ridersList);
 const merchantList = ref(dispatchMockData.merchantList);
 const orderStats = reactive(dispatchMockData.stats);
 
+const serviceOnOrderList = ref([])
+
+const getServiceList = () => {
+  listServiceOnOrder().then(res => {
+    serviceOnOrderList.value = res?.data?.data || res?.data || []
+  }).catch(() => {
+    serviceOnOrderList.value = []
+  })
+}
+
+const areaStationList = ref([])
+const areaOptions = ref([])
+const siteOptions = ref([])
+const regionValue = ref([])
+
+const buildTree = (data, parentId) => {
+  return (data || [])
+    .filter(item => String(item.parentId) === String(parentId))
+    .map(item => ({
+      value: item.id,
+      label: item.name,
+      children: buildTree(data, item.id)
+    }))
+}
+
+const handleAreaChange = (value) => {
+  filters.station = undefined
+  if (value && value.length > 0) {
+    const areaId = value[value.length - 1]
+    siteOptions.value = areaStationList.value
+      .filter(item => Number(item.type) === 2 && String(item.parentId) === String(areaId))
+      .map(item => ({ value: item.id, label: item.name }))
+  } else {
+    siteOptions.value = []
+  }
+}
+
+const getAreaStationList = async () => {
+  try {
+    const res = await listAreaStationOnStore()
+    const data = res?.data || res
+    areaStationList.value = Array.isArray(data) ? data : []
+    const areaData = areaStationList.value.filter(item => Number(item.type) === 0 || Number(item.type) === 1)
+    areaOptions.value = buildTree(areaData, 0)
+    siteOptions.value = []
+    
+    const allStations = areaStationList.value.filter(item => Number(item.type) === 2)
+    if (allStations.length > 0) {
+      const randomStation = allStations[Math.floor(Math.random() * allStations.length)]
+      const areaId = randomStation.parentId
+      
+      const path = []
+      let currentId = areaId
+      while (currentId && String(currentId) !== '0') {
+        path.unshift(currentId)
+        const currentArea = areaStationList.value.find(item => String(item.id) === String(currentId))
+        if (currentArea) {
+          currentId = currentArea.parentId
+        } else {
+          break
+        }
+      }
+      
+      regionValue.value = path
+      siteOptions.value = areaStationList.value
+        .filter(item => Number(item.type) === 2 && String(item.parentId) === String(areaId))
+        .map(item => ({ value: item.id, label: item.name }))
+      filters.station = randomStation.id
+    }
+  } catch {
+    areaStationList.value = []
+    areaOptions.value = []
+    siteOptions.value = []
+  }
+}
+
 // Rider Orders State
 const riderOrdersVisible = ref(false);
 const currentRiderInfo = ref(null);
@@ -264,6 +322,8 @@ watch([currentOrderTab, currentRiderTab], () => {
 });
 
 onMounted(() => {
+  getServiceList()
+  getAreaStationList()
   loadBMapScript()
     .then(() => {
       initMap();
@@ -311,7 +371,7 @@ const filteredOrders = computed(() => {
     result = result.filter((o) => o.status === targetStatus);
   }
   if (filters.orderType !== 'all') {
-    result = result.filter((o) => o.typeCode === filters.orderType);
+    result = result.filter((o) => String(o.serviceId || o.service) === String(filters.orderType));
   }
   return result;
 });
@@ -365,6 +425,7 @@ const filteredRiders = computed(() => {
   background: #fcf9f2;
   overflow: hidden;
 }
+
 .map-view {
   width: 100%;
   height: 100%;
@@ -380,6 +441,7 @@ const filteredRiders = computed(() => {
   gap: 16px;
   z-index: 10;
 }
+
 .control-group {
   background: #fff;
   border-radius: 8px;
@@ -388,6 +450,7 @@ const filteredRiders = computed(() => {
   gap: 8px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 }
+
 .c-btn {
   padding: 6px 16px;
   border-radius: 6px;
@@ -398,26 +461,31 @@ const filteredRiders = computed(() => {
   font-weight: 500;
   transition: all 0.2s;
 }
+
 .c-btn.active {
   background: #2d8cf0;
   color: #fff;
 }
+
 .c-btn.red {
   background: #fef0f0;
   color: #f56c6c;
   border: 1px solid #fde2e2;
 }
+
 .c-btn.green {
   background: #f0f9eb;
   color: #67c23a;
   border: 1px solid #e1f3d8;
 }
+
 .c-btn.gray {
   background: #f0f2f5;
   color: #909399;
   border: 1px solid #dcdfe6;
   cursor: default;
 }
+
 .c-btn.blue {
   background: #ecf5ff;
   color: #409eff;

+ 25 - 2
src/views/order/orderList/components/DispatchDialog.vue

@@ -47,6 +47,9 @@
                             <div style="display:flex; align-items:baseline; gap:8px;">
                                 <span class="r-name">{{ currentRider.name || '--' }}</span>
                                 <span class="r-phone">{{ currentRider.phone || '--' }}</span>
+                                <el-tag v-if="currentRider.status" size="small" :type="getStatusType(currentRider.status)" effect="plain">
+                                    {{ getStatusText(currentRider.status) }}
+                                </el-tag>
                             </div>
                         </div>
 
@@ -78,12 +81,14 @@
                                     <el-avatar :src="rider.avatar" :size="40" />
                                 </div>
                                 <div class="card-main">
-                                    <div class="row-1"
-                                        style="justify-content: space-between; align-items: flex-start; display: flex;">
+                                    <div class="row-1" style="justify-content: space-between; align-items: flex-start; display: flex;">
                                         <div style="display:flex; align-items:baseline; gap:8px;">
                                             <span class="r-name">{{ rider.name || '--' }}</span>
                                             <span class="r-phone">{{ rider.phone || '--' }}</span>
                                         </div>
+                                        <el-tag v-if="rider.status" size="small" :type="getStatusType(rider.status)" effect="plain">
+                                            {{ getStatusText(rider.status) }}
+                                        </el-tag>
                                     </div>
 
                                     <div class="row-2 categories-row"
@@ -235,6 +240,24 @@ const getShortType = (code) => {
     return map[code] || '订单'
 }
 
+const getStatusText = (status) => {
+    const statusMap = {
+        resting: '休息',
+        busy: '接单中',
+        disabled: '禁用'
+    }
+    return statusMap[status] || status
+}
+
+const getStatusType = (status) => {
+    const typeMap = {
+        resting: 'info',
+        busy: 'success',
+        disabled: 'danger'
+    }
+    return typeMap[status] || 'info'
+}
+
 const filteredDispatchRiders = computed(() => {
     return ridersList.value || []
 })

+ 50 - 75
src/views/order/orderList/components/OrderDetailDrawer.vue

@@ -16,7 +16,9 @@
                     <!-- Action Buttons Group -->
                     <div class="detail-actions">
                         <template v-if="[0, 1, 2].includes(order.status)">
-                            <el-button type="success" icon="Bicycle" @click="emit('dispatch', order)">立即派单</el-button>
+                            <el-button type="success" icon="Bicycle" @click="emit('dispatch', order)">
+                                {{ order.fulfiller || order.fulfillerName ? '重新派单' : '立即派单' }}
+                            </el-button>
                         </template>
 
                         <template v-if="order.status === 0">
@@ -185,10 +187,6 @@
                                 <el-descriptions :column="2" border size="default" class="custom-desc">
                                     <el-descriptions-item label="服务地址" :span="2">{{ order.detail.area || order.address
                                     }}</el-descriptions-item>
-                                    <el-descriptions-item label="服务套餐">{{ order.detail.packageName
-                                    }}</el-descriptions-item>
-                                    <el-descriptions-item label="特殊要求">{{ order.detail.petStatus || '无'
-                                    }}</el-descriptions-item>
                                 </el-descriptions>
                             </div>
                         </div>
@@ -262,21 +260,13 @@
                                     @click="handleExportLogs">导出日志Excel</el-button>
                             </div>
                             <el-timeline>
-                                <el-timeline-item v-for="(log, index) in (order.orderLogs || [])" :key="index"
-                                    :timestamp="log.time" :type="log.type || 'primary'" :icon="log.icon"
-                                    placement="top">
+                                <el-timeline-item v-for="(log, index) in (orderLogs || [])" :key="index" :timestamp="''"
+                                    :type="'primary'" :icon="undefined" placement="top">
                                     <div class="log-card">
                                         <div class="l-tit">{{ log.title }}</div>
                                         <div class="l-txt">{{ log.content }}</div>
                                     </div>
                                 </el-timeline-item>
-
-                                <el-timeline-item
-                                    v-if="(!order.orderLogs || order.orderLogs.length === 0) && order.timeline"
-                                    v-for="(log, idx) in order.timeline" :key="'old-' + idx" :timestamp="log.time"
-                                    :type="log.type">
-                                    {{ log.content }}
-                                </el-timeline-item>
                             </el-timeline>
                         </div>
                     </el-tab-pane>
@@ -291,6 +281,7 @@ import { ref, computed, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { getPet } from '@/api/archieves/pet'
 import { getCustomer } from '@/api/archieves/customer'
+import { listSubOrderLog } from '@/api/order/subOrderLog'
 
 const props = defineProps({
     visible: Boolean,
@@ -309,6 +300,28 @@ const order = computed(() => orderDetail.value || props.order)
 
 const loadSeq = ref(0)
 
+const orderLogs = ref([])
+const fulfillerLogs = ref([])
+
+const loadOrderLogs = async (order) => {
+    const id = order?.id
+    if (!id) {
+        orderLogs.value = []
+        fulfillerLogs.value = []
+        return
+    }
+    try {
+        const res = await listSubOrderLog({ orderId: id })
+        const list = res?.data?.data || res?.data || []
+        const arr = Array.isArray(list) ? list : []
+        orderLogs.value = arr.filter(i => Number(i?.logType) === 0)
+        fulfillerLogs.value = arr.filter(i => Number(i?.logType) === 1)
+    } catch {
+        orderLogs.value = []
+        fulfillerLogs.value = []
+    }
+}
+
 const loadPetAndCustomer = async (order) => {
     const seq = ++loadSeq.value
     const next = { ...(order || {}) }
@@ -359,9 +372,12 @@ const loadPetAndCustomer = async (order) => {
 watch(() => props.order, (val) => {
     if (!val) {
         orderDetail.value = null
+        orderLogs.value = []
+        fulfillerLogs.value = []
         return
     }
     loadPetAndCustomer(val)
+    loadOrderLogs(val)
 }, { immediate: true, deep: true })
 
 const activeDetailTab = ref('basic')
@@ -415,7 +431,7 @@ const currentOrderSteps = computed(() => {
         { title: '待商家确认', status: 'confirming', time: '' },
         { title: '已完成', status: 'completed', time: '' }
     ]
-    const logs = props.order.orderLogs || []
+    const logs = orderLogs.value || []
     const status = props.order.status
     let active = 0
     const findTime = (keyword) => {
@@ -468,75 +484,34 @@ const currentOrderSteps = computed(() => {
 })
 
 const serviceProgressSteps = computed(() => {
-    const order = props.order
-    if (!order) return []
-    if ([0, 1, 5].includes(order.status)) {
-        return []
-    }
-    const baseTime = order.serviceTime || '2024-02-10 10:00'
-    const datePart = baseTime.split(' ')[0]
-    const isTransport = order.type === 'transport'
-    let steps = []
-    steps.push({
-        title: '已接单', time: `${datePart} 09:30`, icon: 'Bicycle', color: '#ff9900',
-        desc: `履约者 ${order.fulfillerName || '当前履约者'} 已确认接单,准备前往服务地点`, media: []
-    })
-    steps.push({
-        title: '到达打卡', time: `${datePart} 09:50`, icon: 'Location', color: '#ff9900',
-        desc: '已到达指定位置,打卡确认', media: [{ type: 'image', url: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg' }]
-    })
-    if (isTransport) {
-        steps.push({
-            title: '确认出发', time: `${datePart} 10:10`, icon: 'Van', color: '#ff9900',
-            desc: '接到宠物,状态良好,开始运输。备注:宠物很乖,已放入航空箱。',
-            media: [
-                { type: 'image', url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg' },
-                { type: 'image', url: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg' }
-            ]
-        })
-        if ([3, 4].includes(order.status)) {
-            steps.push({
-                title: '送达打卡', time: `${datePart} 10:50`, icon: 'Place', color: '#ff9900',
-                desc: '宠物已安全送达目的地,等待商家验收',
-                media: [{ type: 'image', url: 'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg' }]
-            })
-        }
-    } else {
-        steps.push({
-            title: '开始服务', time: `${datePart} 10:00`, icon: 'VideoPlay', color: '#ff9900',
-            desc: '已确认宠物状态,开始进行服务视频录制',
-            media: [{ type: 'image', url: 'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg' }]
-        })
-        if ([3, 4].includes(order.status)) {
-            steps.push({
-                title: '服务结束', time: `${datePart} 10:50`, icon: 'VideoPause', color: '#ff9900',
-                desc: '服务项目已全部完成,清理现场完毕。备注:狗狗今天很配合,完成了梳毛和喂食。',
-                media: [
-                    { type: 'image', url: 'https://fuss10.elemecdn.com/3/28/bbf893f792f03a54408b3b7a7ebf0jpeg.jpeg' },
-                    { type: 'image', url: 'https://fuss10.elemecdn.com/2/11/6535bcfb26e4c79b48ddde44f4b6fjpeg.jpeg' }
-                ]
-            })
+    const list = fulfillerLogs.value || []
+    return list.map((i) => {
+        const photos = (i?.photos || '')
+            .split(',')
+            .map(s => s.trim())
+            .filter(Boolean)
+            .map(url => ({ type: 'image', url }))
+        return {
+            title: i?.title || '--',
+            time: '',
+            icon: undefined,
+            color: '#ff9900',
+            desc: i?.content || '',
+            media: photos
         }
-    }
-    if ([3, 4].includes(order.status)) {
-        steps.push({ title: '待商家确认', time: `${datePart} 10:55`, icon: 'Clock', color: '#ff9900', desc: '履约者已提交完成信息,等待商家确认订单', media: [] })
-    }
-    if (order.status === 4) {
-        steps.push({ title: '订单完成', time: `${datePart} 11:00`, icon: 'Select', color: '#67C23A', desc: '用户/商家已确认,服务圆满结束', media: [] })
-    }
-    return steps
+    })
 })
 
 const handleExportLogs = () => {
-    const logs = props.order.orderLogs || []
+    const logs = orderLogs.value || []
     if (logs.length === 0) {
         ElMessage.warning('暂无日志可导出')
         return
     }
     let csvContent = "时间,类型,标题,内容\n"
     logs.forEach(log => {
-        const time = log.time || ''
-        const type = log.type || ''
+        const time = ''
+        const type = log.logType ?? ''
         const title = (log.title || '').replace(/"/g, '""')
         const content = (log.content || '').replace(/"/g, '""')
         csvContent += `${time},${type},"${title}","${content}"\n`

+ 616 - 573
src/views/order/orderList/index.vue

@@ -1,750 +1,793 @@
 <template>
-    <div class="page-container">
-        <el-card shadow="never" class="table-card">
-            <template #header>
-                <div class="card-header">
-                    <span class="title">订单列表</span>
-                    <div class="right-panel">
-                        <el-radio-group v-model="filters.service" size="default" @change="handleSearch">
-                            <el-radio-button label="">全部类型</el-radio-button>
-                            <el-radio-button v-for="item in serviceOptions" :key="item.id" :label="item.id">{{ item.name
-                                }}</el-radio-button>
-                        </el-radio-group>
-                        <el-input v-model="filters.content" placeholder="订单号/商户/宠主/手机号" class="search-input"
-                            prefix-icon="Search" clearable @clear="handleSearch" @keyup.enter="handleSearch" />
-                        <el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
-                    </div>
+  <div class="page-container">
+    <el-card shadow="never" class="table-card">
+      <template #header>
+        <div class="card-header">
+          <span class="title">订单列表</span>
+          <div class="right-panel">
+            <el-radio-group v-model="filters.service" size="default" @change="handleSearch">
+              <el-radio-button label="">全部类型</el-radio-button>
+              <el-radio-button v-for="item in serviceOptions" :key="item.id" :label="item.id">{{ item.name }}</el-radio-button>
+            </el-radio-group>
+            <el-input
+              v-model="filters.content"
+              placeholder="订单号/商户/宠主/手机号"
+              class="search-input"
+              prefix-icon="Search"
+              clearable
+              @clear="handleSearch"
+              @keyup.enter="handleSearch"
+            />
+            <el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
+          </div>
+        </div>
+
+        <el-tabs v-model="filters.status" class="status-tabs" @tab-change="handleStatusTabChange">
+          <el-tab-pane label="全部订单" name="" />
+          <el-tab-pane label="待派单" name="0" />
+          <el-tab-pane label="待接单" name="1" />
+          <el-tab-pane label="服务中" name="2" />
+          <el-tab-pane label="待商家确认" name="3" />
+          <el-tab-pane label="已完成" name="4" />
+          <el-tab-pane label="已取消" name="5" />
+        </el-tabs>
+      </template>
+
+      <el-table :data="tableData" style="width: 100%" v-loading="loading" :header-cell-style="{ background: '#f5f7fa' }">
+        <el-table-column prop="code" label="订单号" width="170" fixed="left" />
+
+        <el-table-column label="服务类型" width="190">
+          <template #default="{ row }">
+            <div class="service-type-cell">
+              <el-tag>{{ getServiceName(row.service) }}</el-tag>
+              <el-tag v-if="getServiceModeTag(row)" class="sub-tag" type="warning" effect="plain">{{ getServiceModeTag(row) }}</el-tag>
+              <el-tag v-if="getServiceOrderTypeTag(row)" class="sub-tag" :type="getServiceOrderTypeTag(row).type" effect="dark">{{
+                getServiceOrderTypeTag(row).label
+              }}</el-tag>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="宠物信息" min-width="150">
+          <template #default="{ row }">
+            <div class="pet-info">
+              <el-avatar :size="30" class="avatar-type">{{ row.petName?.charAt(0) }}</el-avatar>
+              <div class="pet-detail">
+                <div class="pet-name">
+                  {{ row.petName }}
                 </div>
-
-                <el-tabs v-model="filters.status" class="status-tabs" @tab-change="handleStatusTabChange">
-                    <el-tab-pane label="全部订单" name="" />
-                    <el-tab-pane label="待派单" name="0" />
-                    <el-tab-pane label="待接单" name="1" />
-                    <el-tab-pane label="服务中" name="2" />
-                    <el-tab-pane label="待商家确认" name="3" />
-                    <el-tab-pane label="已完成" name="4" />
-                    <el-tab-pane label="已取消" name="5" />
-                </el-tabs>
-            </template>
-
-            <el-table :data="tableData" style="width: 100%" v-loading="loading"
-                :header-cell-style="{ background: '#f5f7fa' }">
-                <el-table-column prop="code" label="订单号" width="170" fixed="left" />
-
-                <el-table-column label="服务类型" width="190">
-                    <template #default="{ row }">
-                        <div class="service-type-cell">
-                            <el-tag>{{ getServiceName(row.service) }}</el-tag>
-                            <el-tag v-if="getServiceModeTag(row)" class="sub-tag" type="warning" effect="plain">{{
-                                getServiceModeTag(row) }}</el-tag>
-                            <el-tag v-if="getServiceOrderTypeTag(row)" class="sub-tag"
-                                :type="getServiceOrderTypeTag(row).type" effect="dark">{{
-                                    getServiceOrderTypeTag(row).label }}</el-tag>
-                        </div>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="宠物信息" min-width="150">
-                    <template #default="{ row }">
-                        <div class="pet-info">
-                            <el-avatar :size="30" class="avatar-type">{{ row.petName?.charAt(0) }}</el-avatar>
-                            <div class="pet-detail">
-                                <div class="pet-name">
-                                    {{ row.petName }}
-                                </div>
-                                <div class="pet-breed">{{ row.petBreed }}</div>
-                            </div>
-                        </div>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="所属用户" width="120" prop="customerName">
-                    <template #default="{ row }">
-                        <span style="font-weight: 500">{{ row.customerName }}</span>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="城市/区域" width="140">
-                    <template #default="{ row }">
-                        <div>{{ getCityDistrictText(row).city || '-' }}</div>
-                        <div class="sub-text">{{ getCityDistrictText(row).district || '-' }}</div>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="商户/下单人" min-width="160">
-                    <template #default="{ row }">
-                        <div class="merchant-info">
-                            <div>{{ row.storeName }}</div>
-                            <div class="sub-text" v-if="row.placerUsername">{{ row.placerUsername }}</div>
-                        </div>
-                    </template>
-                </el-table-column>
-
-                <el-table-column prop="createTime" label="下单时间" width="165" sortable>
-                    <template #default="{ row }">
-                        <span class="time-text">{{ row.createTime }}</span>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="订单状态" width="100">
-                    <template #default="{ row }">
-                        <div class="status-cell">
-                            <div class="status-dot" :class="getStatusClass(row.status)"></div>
-                            <span>{{ getStatusName(row.status) }}</span>
-                        </div>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="履约信息" width="140">
-                    <template #default="{ row }">
-                        <div v-if="row.fulfillerName" class="fulfiller-info">
-                            <span class="fulfiller-name">{{ row.fulfillerName }}</span>
-                            <span class="fulfiller-fee" v-if="row.price !== null && row.price !== undefined">¥{{
-                                row.price }}</span>
-                        </div>
-                        <span v-else class="text-gray">暂未指派</span>
-                    </template>
-                </el-table-column>
-
-                <el-table-column label="操作" width="200" fixed="right">
-                    <template #default="{ row }">
-                        <div class="op-cell">
-                            <el-button link type="primary" size="small" @click="handleDetail(row)">详情</el-button>
-                            <el-button v-if="row.status === 0" link type="success" size="small"
-                                @click="openDispatchDialog(row)">派单</el-button>
-                            <el-button v-if="[1, 2].includes(row.status)" link type="warning" size="small"
-                                @click="openDispatchDialog(row)">重新派单</el-button>
-                            <el-button v-if="[0, 1].includes(row.status)" link type="danger" size="small"
-                                @click="handleCancel(row)">取消</el-button>
-
-                            <el-dropdown v-if="[2, 3, 4].includes(row.status)" trigger="click"
-                                @command="(cmd) => handleCommand(cmd, row)">
-                                <span class="el-dropdown-link">
-                                    更多<el-icon class="el-icon--right">
-                                        <ArrowDown />
-                                    </el-icon>
-                                </span>
-                                <template #dropdown>
-                                    <el-dropdown-menu>
-                                        <el-dropdown-item v-if="row.status === 3"
-                                            command="complete">确认完成</el-dropdown-item>
-                                        <el-dropdown-item v-if="[3, 4].includes(row.status)"
-                                            command="care_summary">护理小结</el-dropdown-item>
-                                        <el-dropdown-item command="reward">奖惩</el-dropdown-item>
-                                        <el-dropdown-item command="remark">备注</el-dropdown-item>
-                                    </el-dropdown-menu>
-                                </template>
-                            </el-dropdown>
-                        </div>
-                    </template>
-                </el-table-column>
-            </el-table>
-
-            <div class="pagination-container">
-                <el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size"
-                    :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper"
-                    :total="pagination.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
+                <div class="pet-breed">{{ row.petBreed }}</div>
+              </div>
             </div>
-        </el-card>
-
-        <!-- 组件 -->
-        <OrderDetailDrawer v-model:visible="detailVisible" :order="currentOrder" @dispatch="openDispatchDialog"
-            @cancel="handleCancel" @command="handleCommand" @care-summary="openCareSummary" />
-
-        <DispatchDialog v-model:visible="dispatchDialogVisible" :order="currentDispatchOrder"
-            @submit="handleDispatchSubmit" />
-
-        <CareSummaryDrawer v-model:visible="careSummaryVisible" :order="careSummaryOrder" @submit="saveCareSummary" />
-
-        <RewardDialog v-model:visible="rewardDialogVisible" :order="currentOperateRow" @submit="handleRewardSubmit" />
-
-        <RemarkDialog v-model:visible="remarkDialogVisible" :order="currentOperateRow" @submit="handleRemarkSubmit" />
-
-    </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="所属用户" width="120" prop="customerName">
+          <template #default="{ row }">
+            <span style="font-weight: 500">{{ row.customerName }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="城市/区域" width="140">
+          <template #default="{ row }">
+            <div>{{ getCityDistrictText(row).city || '-' }}</div>
+            <div class="sub-text">{{ getCityDistrictText(row).district || '-' }}</div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="商户/下单人" min-width="160">
+          <template #default="{ row }">
+            <div class="merchant-info">
+              <div>{{ row.storeName }}</div>
+              <div class="sub-text" v-if="row.placerUsername">{{ row.placerUsername }}</div>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="createTime" label="下单时间" width="165" sortable>
+          <template #default="{ row }">
+            <span class="time-text">{{ row.createTime }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="createTime" label="预约服务时间" width="165" sortable>
+          <template #default="{ row }">
+            <span class="time-text">{{ row.serviceTime }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="订单状态" width="100">
+          <template #default="{ row }">
+            <div class="status-cell">
+              <div class="status-dot" :class="getStatusClass(row.status)"></div>
+              <span>{{ getStatusName(row.status) }}</span>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="履约信息" width="140">
+          <template #default="{ row }">
+            <div v-if="row.fulfillerName" class="fulfiller-info">
+              <span class="fulfiller-name">{{ row.fulfillerName }}</span>
+              <span class="fulfiller-fee" v-if="row.price !== null && row.price !== undefined">¥{{ row.price }}</span>
+            </div>
+            <span v-else class="text-gray">暂未指派</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" width="200" fixed="right">
+          <template #default="{ row }">
+            <div class="op-cell">
+              <el-button link type="primary" size="small" @click="handleDetail(row)">详情</el-button>
+              <el-button v-if="row.status === 0" link type="success" size="small" @click="openDispatchDialog(row)">派单</el-button>
+              <el-button v-if="[1, 2].includes(row.status)" link type="warning" size="small" @click="openDispatchDialog(row)">重新派单</el-button>
+              <el-button v-if="[0, 1].includes(row.status)" link type="danger" size="small" @click="handleCancel(row)">取消</el-button>
+
+              <el-dropdown v-if="[2, 3, 4].includes(row.status)" trigger="click" @command="(cmd) => handleCommand(cmd, row)">
+                <span class="el-dropdown-link">
+                  更多<el-icon class="el-icon--right">
+                    <ArrowDown />
+                  </el-icon>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item v-if="row.status === 3" command="complete">确认完成</el-dropdown-item>
+                    <el-dropdown-item v-if="[3, 4].includes(row.status)" command="care_summary">护理小结</el-dropdown-item>
+                    <el-dropdown-item command="reward">奖惩</el-dropdown-item>
+                    <el-dropdown-item command="remark">备注</el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="pagination.current"
+          v-model:page-size="pagination.size"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="pagination.total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <!-- 组件 -->
+    <OrderDetailDrawer
+      v-model:visible="detailVisible"
+      :order="currentOrder"
+      @dispatch="openDispatchDialog"
+      @cancel="handleCancel"
+      @command="handleCommand"
+      @care-summary="openCareSummary"
+    />
+
+    <DispatchDialog v-model:visible="dispatchDialogVisible" :order="currentDispatchOrder" @submit="handleDispatchSubmit" />
+
+    <CareSummaryDrawer v-model:visible="careSummaryVisible" :order="careSummaryOrder" @submit="saveCareSummary" />
+
+    <RewardDialog v-model:visible="rewardDialogVisible" :order="currentOperateRow" @submit="handleRewardSubmit" />
+
+    <RemarkDialog v-model:visible="remarkDialogVisible" :order="currentOperateRow" @submit="handleRemarkSubmit" />
+  </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted, nextTick } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import OrderDetailDrawer from './components/OrderDetailDrawer.vue'
-import DispatchDialog from './components/DispatchDialog.vue'
-import CareSummaryDrawer from './components/CareSummaryDrawer.vue'
-import RewardDialog from './components/RewardDialog.vue'
-import RemarkDialog from './components/RemarkDialog.vue'
-import { listOnStore as listServiceOnStore } from '@/api/service/list/index'
-import { listSubOrder } from '@/api/order/subOrder/index'
-import { dispatchSubOrder } from '@/api/order/subOrder/index'
-import { getSubOrderInfo } from '@/api/order/subOrder/index'
-import { cancelSubOrder } from '@/api/order/subOrder/index'
-import { listOnStore as listAreaStationOnStore } from '@/api/system/areaStation'
-import { getStore } from '@/api/system/store'
-
-const loading = ref(false)
+import { ref, reactive, onMounted, nextTick } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import OrderDetailDrawer from './components/OrderDetailDrawer.vue';
+import DispatchDialog from './components/DispatchDialog.vue';
+import CareSummaryDrawer from './components/CareSummaryDrawer.vue';
+import RewardDialog from './components/RewardDialog.vue';
+import RemarkDialog from './components/RemarkDialog.vue';
+import { listOnStore as listServiceOnStore } from '@/api/service/list/index';
+import { listSubOrder } from '@/api/order/subOrder/index';
+import { dispatchSubOrder } from '@/api/order/subOrder/index';
+import { getSubOrderInfo } from '@/api/order/subOrder/index';
+import { cancelSubOrder } from '@/api/order/subOrder/index';
+import { listOnStore as listAreaStationOnStore } from '@/api/system/areaStation';
+import { getStore } from '@/api/system/store';
+
+const loading = ref(false);
 
 const filters = reactive({
-    service: '',
-    status: '',
-    content: ''
-})
+  service: '',
+  status: '',
+  content: ''
+});
 
 const pagination = reactive({
-    current: 1,
-    size: 10,
-    total: 100
-})
+  current: 1,
+  size: 10,
+  total: 100
+});
 
-const tableData = ref([])
-const serviceOptions = ref([])
-const areaStationList = ref([])
-const areaStationMap = ref({})
-const storeMap = ref({})
+const tableData = ref([]);
+const serviceOptions = ref([]);
+const areaStationList = ref([]);
+const areaStationMap = ref({});
+const storeMap = ref({});
 
 onMounted(() => {
-    getServiceList()
-    getAreaStationList()
-    handleSearch()
-})
+  getServiceList();
+  getAreaStationList();
+  handleSearch();
+});
 
 const getServiceList = () => {
-    listServiceOnStore().then(res => {
-        serviceOptions.value = res.data || []
-    })
-}
+  listServiceOnStore().then((res) => {
+    serviceOptions.value = res.data || [];
+  });
+};
 
 const getAreaStationList = () => {
-    listAreaStationOnStore().then(res => {
-        const list = res.data || []
-        areaStationList.value = list
-        const map = {}
-        for (const item of list) {
-            if (item && item.id !== undefined && item.id !== null) map[item.id] = item
-        }
-        areaStationMap.value = map
-    })
-}
-
-const getCityDistrictText = (row) => {
-    if (!row) return { city: '', district: '' }
-    const map = areaStationMap.value || {}
-    let stationId = row.site
-    if (!map[stationId] && row.store) {
-        const store = (storeMap.value || {})[row.store]
-        if (store?.site) stationId = store.site
-    }
-    if (!stationId) return { city: '', district: '' }
-
-    const station = map[stationId]
-    if (!station) return { city: '', district: '' }
-
-    const parent = station.parentId ? map[station.parentId] : undefined
-    if (!parent) return { city: station.name || '', district: '' }
-
-    if (parent.type === 0) return { city: parent.name || '', district: '' }
-    if (parent.type === 1) {
-        const city = parent.parentId ? map[parent.parentId] : undefined
-        return { city: city?.name || '', district: parent.name || '' }
+  listAreaStationOnStore().then((res) => {
+    const list = res.data || [];
+    areaStationList.value = list;
+    const map = {};
+    for (const item of list) {
+      if (item && item.id !== undefined && item.id !== null) map[item.id] = item;
     }
+    areaStationMap.value = map;
+  });
+};
 
-    return { city: '', district: parent.name || '' }
-}
+const getCityDistrictText = (row) => {
+  if (!row) return { city: '', district: '' };
+  const map = areaStationMap.value || {};
+  let stationId = row.site;
+  if (!map[stationId] && row.store) {
+    const store = (storeMap.value || {})[row.store];
+    if (store?.site) stationId = store.site;
+  }
+  if (!stationId) return { city: '', district: '' };
+
+  const station = map[stationId];
+  if (!station) return { city: '', district: '' };
+
+  const parent = station.parentId ? map[station.parentId] : undefined;
+  if (!parent) return { city: station.name || '', district: '' };
+
+  if (parent.type === 0) return { city: parent.name || '', district: '' };
+  if (parent.type === 1) {
+    const city = parent.parentId ? map[parent.parentId] : undefined;
+    return { city: city?.name || '', district: parent.name || '' };
+  }
+
+  return { city: '', district: parent.name || '' };
+};
 
 const handleStatusTabChange = async () => {
-    pagination.current = 1
-    await nextTick()
-    handleSearch()
-}
+  pagination.current = 1;
+  await nextTick();
+  handleSearch();
+};
 
 const handleSearch = () => {
-    loading.value = true
-    listSubOrder({
-        pageNum: pagination.current,
-        pageSize: pagination.size,
-        service: filters.service !== '' ? filters.service : undefined,
-        status: filters.status !== '' ? Number(filters.status) : undefined,
-        content: filters.content || undefined
-    }).then(res => {
-        tableData.value = res.rows || []
-        pagination.total = res.total || 0
-        loadStoresForRows(tableData.value)
-        loading.value = false
-    }).catch(() => {
-        loading.value = false
+  loading.value = true;
+  listSubOrder({
+    pageNum: pagination.current,
+    pageSize: pagination.size,
+    service: filters.service !== '' ? filters.service : undefined,
+    status: filters.status !== '' ? Number(filters.status) : undefined,
+    content: filters.content || undefined
+  })
+    .then((res) => {
+      tableData.value = res.rows || [];
+      pagination.total = res.total || 0;
+      loadStoresForRows(tableData.value);
+      loading.value = false;
     })
-}
+    .catch(() => {
+      loading.value = false;
+    });
+};
 
 const loadStoresForRows = async (rows) => {
-    const map = storeMap.value || {}
-    const ids = Array.from(new Set((rows || []).map(r => r?.store).filter(Boolean)))
-    const missing = ids.filter(id => map[id] === undefined)
-    if (missing.length === 0) return
-    await Promise.all(missing.map(async (id) => {
-        try {
-            const res = await getStore(id)
-            if (res?.data) map[id] = res.data
-        } catch {
-            map[id] = null
-        }
-    }))
-    storeMap.value = { ...map }
-}
+  const map = storeMap.value || {};
+  const ids = Array.from(new Set((rows || []).map((r) => r?.store).filter(Boolean)));
+  const missing = ids.filter((id) => map[id] === undefined);
+  if (missing.length === 0) return;
+  await Promise.all(
+    missing.map(async (id) => {
+      try {
+        const res = await getStore(id);
+        if (res?.data) map[id] = res.data;
+      } catch {
+        map[id] = null;
+      }
+    })
+  );
+  storeMap.value = { ...map };
+};
 
 const handleSizeChange = (val) => {
-    pagination.size = val
-    handleSearch()
-}
+  pagination.size = val;
+  handleSearch();
+};
 const handleCurrentChange = (val) => {
-    pagination.current = val
-    handleSearch()
-}
+  pagination.current = val;
+  handleSearch();
+};
 
 const getServiceName = (serviceId) => {
-    const item = serviceOptions.value.find(i => i.id === serviceId)
-    return item ? item.name : '未知服务'
-}
+  const item = serviceOptions.value.find((i) => i.id === serviceId);
+  return item ? item.name : '未知服务';
+};
 
 const getServiceModeTag = (row) => {
-    const t = row?.type
-    if (t === 0 || t === '0' || t === 1 || t === '1') return '往返'
-    return ''
-}
+  const t = row?.type;
+  if (t === 0 || t === '0' || t === 1 || t === '1') return '往返';
+  return '';
+};
 
 const getServiceOrderTypeTag = (row) => {
-    const t = row?.type
-    if (t === 0 || t === '0') return { label: '接', type: 'primary' }
-    if (t === 1 || t === '1') return { label: '送', type: 'success' }
-    if (t === 2 || t === '2') return { label: '单程接', type: 'primary' }
-    if (t === 3 || t === '3') return { label: '单程送', type: 'success' }
-    return null
-}
+  const t = row?.type;
+  if (t === 0 || t === '0') return { label: '接', type: 'primary' };
+  if (t === 1 || t === '1') return { label: '送', type: 'success' };
+  if (t === 2 || t === '2') return { label: '单程接', type: 'primary' };
+  if (t === 3 || t === '3') return { label: '单程送', type: 'success' };
+  return null;
+};
 
 const getStatusName = (status) => {
-    const map = { 0: '待派单', 1: '待接单', 2: '服务中', 3: '待商家确认', 4: '已完成', 5: '已取消' }
-    return map[status] || '未知'
-}
+  const map = { 0: '待派单', 1: '待接单', 2: '服务中', 3: '待商家确认', 4: '已完成', 5: '已取消' };
+  return map[status] || '未知';
+};
 
 const getStatusClass = (status) => {
-    const map = { 0: 'pending_dispatch', 1: 'pending_accept', 2: 'serving', 3: 'pending_confirm', 4: 'completed', 5: 'cancelled' }
-    return map[status] || 'pending_dispatch'
-}
+  const map = { 0: 'pending_dispatch', 1: 'pending_accept', 2: 'serving', 3: 'pending_confirm', 4: 'completed', 5: 'cancelled' };
+  return map[status] || 'pending_dispatch';
+};
+
+const getFulfillerStatusText = (status) => {
+  const statusMap = {
+    resting: '休息',
+    busy: '接单中',
+    disabled: '禁用'
+  };
+  return statusMap[status] || status;
+};
+
+const getFulfillerStatusType = (status) => {
+  const typeMap = {
+    resting: 'info',
+    busy: 'success',
+    disabled: 'danger'
+  };
+  return typeMap[status] || 'info';
+};
 
 // 弹窗状态管理
-const detailVisible = ref(false)
-const currentOrder = ref(null)
+const detailVisible = ref(false);
+const currentOrder = ref(null);
 
-const dispatchDialogVisible = ref(false)
-const currentDispatchOrder = ref(null)
+const dispatchDialogVisible = ref(false);
+const currentDispatchOrder = ref(null);
 
-const careSummaryVisible = ref(false)
-const careSummaryOrder = ref(null)
+const careSummaryVisible = ref(false);
+const careSummaryOrder = ref(null);
 
-const rewardDialogVisible = ref(false)
-const remarkDialogVisible = ref(false)
-const currentOperateRow = ref(null)
+const rewardDialogVisible = ref(false);
+const remarkDialogVisible = ref(false);
+const currentOperateRow = ref(null);
 
 // 详情
 const handleDetail = async (row) => {
-    const typeName = getServiceName(row?.service)
-    const isTransport = row?.mode === 1 || row?.mode === '1'
-    const typeCode = isTransport ? 'transport' : (typeName?.includes('喂') || typeName?.includes('遛') ? 'feeding' : 'washing')
-    currentOrder.value = {
-        ...row,
-        orderNo: row?.code || row?.orderCode || row?.orderNo || row?.orderNumber || row?.no || '',
-        type: row?.typeCode || row?.type || typeCode,
-        serviceItem: getServiceName(row?.service) || row?.serviceName || row?.service || '',
-        userAvatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
-        address: '某小区5号楼2单元101',
-        groupBuyPackage: '',
-        transportType: row.splitType || row.transportType,
+  const typeName = getServiceName(row?.service);
+  const isTransport = row?.mode === 1 || row?.mode === '1';
+  const typeCode = isTransport ? 'transport' : typeName?.includes('喂') || typeName?.includes('遛') ? 'feeding' : 'washing';
+  currentOrder.value = {
+    ...row,
+    orderNo: row?.code || row?.orderCode || row?.orderNo || row?.orderNumber || row?.no || '',
+    type: row?.typeCode || row?.type || typeCode,
+    serviceItem: getServiceName(row?.service) || row?.serviceName || row?.service || '',
+    userAvatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
+    address: '某小区5号楼2单元101',
+    groupBuyPackage: '',
+    transportType: row.splitType || row.transportType,
+    detail: {
+      ...row.detail,
+      pickTime: '2024-02-05 09:30',
+      pickAddr: row.detail?.pickAddr || '北京市朝阳区某小区5号楼2单元101',
+      pickContact: '李先生',
+      pickPhone: '13812345678',
+      dropTime: '2024-02-05 18:30',
+      dropAddr: row.detail?.dropAddr || '北京市朝阳区某小区5号楼2单元101',
+      dropContact: '李先生',
+      dropPhone: '13812345678',
+      packageName: row.detail?.packageName || '精细洗护套餐A',
+      petStatus: '胆小,需安抚',
+      area: '北京市朝阳区某小区5号楼2单元101'
+    },
+    petGender: 'male',
+    petAge: '2岁',
+    petWeight: '15kg',
+    petVaccine: '已接种',
+    petSterilized: true,
+    petCharacter: '活泼好动,喜欢球类玩具',
+    petHealth: '健康良好',
+    fulfillerAvatar: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
+    fulfillerPhone: '13812345678',
+    fulfillerStation: '朝阳服务站',
+    orderLogs: [
+      { time: '2024-02-04 09:30', title: '订单创建', content: '商户提交订单', icon: 'Document' },
+      { time: '2024-02-04 10:00', title: '系统派单', content: '指派给 王大力', icon: 'Bicycle' },
+      { time: '2024-02-04 10:05', title: '接单成功', content: '履约者已确认接单', icon: 'CircleCheck' },
+      { time: '2024-02-04 13:55', title: '到达服务点', content: '履约者已打卡', icon: 'Location' }
+    ]
+  };
+
+  try {
+    const res = await getSubOrderInfo(row?.id);
+    const info = res?.data?.data || res?.data;
+    if (info) {
+      currentOrder.value = {
+        ...(currentOrder.value || {}),
+        id: info.id,
+        orderNo: info.code || currentOrder.value?.orderNo,
+        code: info.code,
+        subOrderType: info.type,
+        status: info.status ?? currentOrder.value?.status,
+        mode: info.mode ?? currentOrder.value?.mode,
+        type: info.mode === 1 || info.mode === '1' ? 'transport' : currentOrder.value?.type,
+        transportType: info.mode === 1 || info.mode === '1' ? 'round' : currentOrder.value?.transportType,
+        serviceTime: info.serviceTime || currentOrder.value?.serviceTime,
+        endServiceTime: info.endServiceTime || currentOrder.value?.endServiceTime,
+        contact: info.contact || currentOrder.value?.contact,
+        contactPhoneNumber: info.contactPhoneNumber || currentOrder.value?.contactPhoneNumber,
+        toAddress: info.toAddress || currentOrder.value?.toAddress,
+        address:
+          info.mode === 1 || info.mode === '1'
+            ? info.toAddress || currentOrder.value?.address
+            : info.address || info.toAddress || currentOrder.value?.address,
+        price: info.price !== undefined && info.price !== null ? Number(info.price) / 100 : currentOrder.value?.price,
+        fulfillerFee: info.price !== undefined && info.price !== null ? Number(info.price) / 100 : currentOrder.value?.fulfillerFee,
+        merchantName: info.storeName || currentOrder.value?.merchantName,
+        platformId: info.platformId ?? currentOrder.value?.platformId,
+        groupBuyPackage: info.groupPurchasePackageName || currentOrder.value?.groupBuyPackage,
+        fulfiller: info.fulfiller ?? currentOrder.value?.fulfiller,
         detail: {
-            ...row.detail,
-            pickTime: '2024-02-05 09:30',
-            pickAddr: row.detail?.pickAddr || '北京市朝阳区某小区5号楼2单元101',
-            pickContact: '李先生',
-            pickPhone: '13812345678',
-            dropTime: '2024-02-05 18:30',
-            dropAddr: row.detail?.dropAddr || '北京市朝阳区某小区5号楼2单元101',
-            dropContact: '李先生',
-            dropPhone: '13812345678',
-            packageName: row.detail?.packageName || '精细洗护套餐A',
-            petStatus: '胆小,需安抚',
-            area: '北京市朝阳区某小区5号楼2单元101'
-        },
-        petGender: 'male',
-        petAge: '2岁',
-        petWeight: '15kg',
-        petVaccine: '已接种',
-        petSterilized: true,
-        petCharacter: '活泼好动,喜欢球类玩具',
-        petHealth: '健康良好',
-        fulfillerAvatar: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
-        fulfillerPhone: '13812345678',
-        fulfillerStation: '朝阳服务站',
-        orderLogs: [
-            { time: '2024-02-04 09:30', title: '订单创建', content: '商户提交订单', icon: 'Document' },
-            { time: '2024-02-04 10:00', title: '系统派单', content: '指派给 王大力', icon: 'Bicycle' },
-            { time: '2024-02-04 10:05', title: '接单成功', content: '履约者已确认接单', icon: 'CircleCheck' },
-            { time: '2024-02-04 13:55', title: '到达服务点', content: '履约者已打卡', icon: 'Location' }
-        ]
-    }
-
-    try {
-        const res = await getSubOrderInfo(row?.id)
-        const info = res?.data?.data || res?.data
-        if (info) {
-            currentOrder.value = {
-                ...(currentOrder.value || {}),
-                id: info.id,
-                orderNo: info.code || currentOrder.value?.orderNo,
-                code: info.code,
-                subOrderType: info.type,
-                status: info.status ?? currentOrder.value?.status,
-                mode: info.mode ?? currentOrder.value?.mode,
-                type: (info.mode === 1 || info.mode === '1') ? 'transport' : currentOrder.value?.type,
-                transportType: (info.mode === 1 || info.mode === '1') ? 'round' : currentOrder.value?.transportType,
-                serviceTime: info.serviceTime || currentOrder.value?.serviceTime,
-                endServiceTime: info.endServiceTime || currentOrder.value?.endServiceTime,
-                contact: info.contact || currentOrder.value?.contact,
-                contactPhoneNumber: info.contactPhoneNumber || currentOrder.value?.contactPhoneNumber,
-                toAddress: info.toAddress || currentOrder.value?.toAddress,
-                address: info.toAddress || currentOrder.value?.address,
-                price: (info.price !== undefined && info.price !== null) ? (Number(info.price) / 100) : currentOrder.value?.price,
-                fulfillerFee: (info.price !== undefined && info.price !== null) ? (Number(info.price) / 100) : currentOrder.value?.fulfillerFee,
-                merchantName: info.storeName || currentOrder.value?.merchantName,
-                platformId: info.platformId ?? currentOrder.value?.platformId,
-                groupBuyPackage: info.groupPurchasePackageName || currentOrder.value?.groupBuyPackage,
-                fulfiller: info.fulfiller ?? currentOrder.value?.fulfiller,
-                detail: {
-                    ...(currentOrder.value?.detail || {}),
-                    pickTime: info.serviceTime || currentOrder.value?.detail?.pickTime,
-                    pickAddr: info.fromAddress || currentOrder.value?.detail?.pickAddr,
-                    pickContact: info.contact || currentOrder.value?.detail?.pickContact,
-                    pickPhone: info.contactPhoneNumber || currentOrder.value?.detail?.pickPhone,
-                    dropTime: info.endServiceTime || currentOrder.value?.detail?.dropTime,
-                    dropAddr: info.toAddress || currentOrder.value?.detail?.dropAddr,
-                    dropContact: info.contact || currentOrder.value?.detail?.dropContact,
-                    dropPhone: info.contactPhoneNumber || currentOrder.value?.detail?.dropPhone,
-                    fromAddress: info.fromAddress || currentOrder.value?.detail?.fromAddress,
-                    toAddress: info.toAddress || currentOrder.value?.detail?.toAddress,
-                    area: info.toAddress || currentOrder.value?.detail?.area,
-                    fromCode: info.fromCode || currentOrder.value?.detail?.fromCode,
-                    toCode: info.toCode || currentOrder.value?.detail?.toCode
-                }
-            }
+          ...(currentOrder.value?.detail || {}),
+          pickTime: info.serviceTime || currentOrder.value?.detail?.pickTime,
+          pickAddr: info.fromAddress || currentOrder.value?.detail?.pickAddr,
+          pickContact: info.contact || currentOrder.value?.detail?.pickContact,
+          pickPhone: info.contactPhoneNumber || currentOrder.value?.detail?.pickPhone,
+          dropTime: info.endServiceTime || currentOrder.value?.detail?.dropTime,
+          dropAddr: info.toAddress || currentOrder.value?.detail?.dropAddr,
+          dropContact: info.contact || currentOrder.value?.detail?.dropContact,
+          dropPhone: info.contactPhoneNumber || currentOrder.value?.detail?.dropPhone,
+          fromAddress: info.fromAddress || currentOrder.value?.detail?.fromAddress,
+          toAddress: info.toAddress || currentOrder.value?.detail?.toAddress,
+          area: info.area || info.address || info.toAddress || currentOrder.value?.detail?.area,
+          packageName: info.packageName || info.servicePackageName || info.package || currentOrder.value?.detail?.packageName,
+          petStatus: info.petStatus || info.specialRequirement || info.requirement || currentOrder.value?.detail?.petStatus,
+          fromCode: info.fromCode || currentOrder.value?.detail?.fromCode,
+          toCode: info.toCode || currentOrder.value?.detail?.toCode
         }
-    } catch {
+      };
     }
-    detailVisible.value = true
-}
+  } catch {}
+  detailVisible.value = true;
+};
 
 // 取消订单
 const handleCancel = (row) => {
-    ElMessageBox.confirm('确认取消该订单吗?', '提示', { type: 'warning' })
-        .then(() => {
-            cancelSubOrder({ orderId: row?.id }).then(() => {
-                ElMessage.success('订单已取消')
-                handleSearch()
-            })
-        })
-}
+  ElMessageBox.confirm('确认取消该订单吗?', '提示', { type: 'warning' }).then(() => {
+    cancelSubOrder({ orderId: row?.id }).then(() => {
+      ElMessage.success('订单已取消');
+      handleSearch();
+    });
+  });
+};
 
 // 派单
 const openDispatchDialog = (row) => {
-    const typeName = getServiceName(row?.service)
-    const isTransport = row?.mode === 1 || row?.mode === '1'
-    const typeCode = isTransport ? 'transport' : (typeName?.includes('喂') || typeName?.includes('遛') ? 'feeding' : 'washing')
-
-    const t = row?.subOrderType ?? row?.type
-    const transportType = (t === 0 || t === '0' || t === 1 || t === '1') ? 'round' : ((t === 2 || t === '2') ? 'pick' : ((t === 3 || t === '3') ? 'drop' : (row?.splitType || row?.transportType)))
-
-    const toAddress = row?.toAddress || ''
-    const pickAddr = isTransport ? toAddress : ''
-    const dropAddr = isTransport ? toAddress : ''
-    const address = isTransport ? '' : toAddress
-
-    const orderObj = {
-        id: row.id,
-        typeCode,
-        transportType,
-        time: row.serviceTime || row.appointTime || row.createTime,
-        status: row.status,
-        address,
-        pickAddr,
-        dropAddr,
-        riderId: row.riderId || row.fulfiller || null
-    }
-    currentDispatchOrder.value = orderObj
-    dispatchDialogVisible.value = true
-}
+  const typeName = getServiceName(row?.service);
+  const isTransport = row?.mode === 1 || row?.mode === '1';
+  const typeCode = isTransport ? 'transport' : typeName?.includes('喂') || typeName?.includes('遛') ? 'feeding' : 'washing';
+
+  const t = row?.subOrderType ?? row?.type;
+  const transportType =
+    t === 0 || t === '0' || t === 1 || t === '1'
+      ? 'round'
+      : t === 2 || t === '2'
+        ? 'pick'
+        : t === 3 || t === '3'
+          ? 'drop'
+          : row?.splitType || row?.transportType;
+
+  const toAddress = row?.toAddress || '';
+  const pickAddr = isTransport ? toAddress : '';
+  const dropAddr = isTransport ? toAddress : '';
+  const address = isTransport ? '' : toAddress;
+
+  const orderObj = {
+    id: row.id,
+    typeCode,
+    transportType,
+    time: row.serviceTime || row.appointTime || row.createTime,
+    status: row.status,
+    address,
+    pickAddr,
+    dropAddr,
+    riderId: row.riderId || row.fulfiller || null
+  };
+  currentDispatchOrder.value = orderObj;
+  dispatchDialogVisible.value = true;
+};
 
 const handleDispatchSubmit = (payload) => {
-    if (!currentDispatchOrder.value) return
-    const priceFen = Math.round(Number(payload.fee || 0) * 100)
-    dispatchSubOrder({
-        orderId: currentDispatchOrder.value.id,
-        fulfiller: payload.riderId,
-        price: priceFen
-    }).then(() => {
-        ElMessage.success('派单成功')
-        const row = tableData.value.find(r => r.id === currentDispatchOrder.value.id)
-        if (row) {
-            row.status = 1
-            row.fulfillerName = payload.riderName || 'Unknown'
-            row.price = payload.fee
-        }
-        handleSearch()
-    })
-}
+  if (!currentDispatchOrder.value) return;
+  const priceFen = Math.round(Number(payload.fee || 0) * 100);
+  dispatchSubOrder({
+    orderId: currentDispatchOrder.value.id,
+    fulfiller: payload.riderId,
+    price: priceFen
+  }).then(() => {
+    ElMessage.success('派单成功');
+    const row = tableData.value.find((r) => r.id === currentDispatchOrder.value.id);
+    if (row) {
+      row.status = 1;
+      row.fulfillerName = payload.riderName || 'Unknown';
+      row.price = payload.fee;
+    }
+    handleSearch();
+  });
+};
 
 // 护理小结
 const openCareSummary = (row) => {
-    careSummaryOrder.value = {
-        ...row,
-        petAge: '3岁',
-        petGender: 'male',
-        petTags: ['易过敏', '胆小'],
-        petWeight: '30 kg',
-        petPersonality: '活泼,超级粘人,喜欢玩球',
-        homeTime: '2023-01-01',
-        houseType: '电梯',
-        entryMethod: '密码开门',
-        entryDetail: '密码: 123456 (仅限服务期间使用)',
-        healthStatus: '健康',
-        vaccineImg: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
-        allergy: '海鲜'
-    }
-    careSummaryVisible.value = true
-}
+  careSummaryOrder.value = {
+    ...row,
+    petAge: '3岁',
+    petGender: 'male',
+    petTags: ['易过敏', '胆小'],
+    petWeight: '30 kg',
+    petPersonality: '活泼,超级粘人,喜欢玩球',
+    homeTime: '2023-01-01',
+    houseType: '电梯',
+    entryMethod: '密码开门',
+    entryDetail: '密码: 123456 (仅限服务期间使用)',
+    healthStatus: '健康',
+    vaccineImg: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
+    allergy: '海鲜'
+  };
+  careSummaryVisible.value = true;
+};
 
 const saveCareSummary = (text) => {
-    if (careSummaryOrder.value) {
-        careSummaryOrder.value.careSummary = text
-        ElMessage.success('护理小结已保存')
-        handleSearch()
-    }
-}
+  if (careSummaryOrder.value) {
+    careSummaryOrder.value.careSummary = text;
+    ElMessage.success('护理小结已保存');
+    handleSearch();
+  }
+};
 
 // 奖惩
 const openRewardDialog = (row) => {
-    currentOperateRow.value = row
-    rewardDialogVisible.value = true
-}
+  currentOperateRow.value = row;
+  rewardDialogVisible.value = true;
+};
 const handleRewardSubmit = (form) => {
-    ElMessage.success(`操作成功:${form.type === 'reward' ? '奖励' : '惩罚'}已执行`)
-}
+  ElMessage.success(`操作成功:${form.type === 'reward' ? '奖励' : '惩罚'}已执行`);
+};
 
 // 备注
 const openRemarkDialog = (row) => {
-    currentOperateRow.value = row
-    remarkDialogVisible.value = true
-}
+  currentOperateRow.value = row;
+  remarkDialogVisible.value = true;
+};
 const handleRemarkSubmit = (text) => {
-    if (currentOperateRow.value) {
-        currentOperateRow.value.remark = text
-        ElMessage.success('备注已更新')
-    }
-}
+  if (currentOperateRow.value) {
+    currentOperateRow.value.remark = text;
+    ElMessage.success('备注已更新');
+  }
+};
 
 // 更多操作
 const handleCommand = (cmd, row) => {
-    if (cmd === 'reward') openRewardDialog(row)
-    if (cmd === 'remark') openRemarkDialog(row)
-    if (cmd === 'care_summary') openCareSummary(row)
-    if (cmd === 'complete') {
-        ElMessageBox.confirm('确认将该订单手动标记为完成吗?', '提示', { type: 'warning' })
-            .then(() => {
-                row.status = 4
-                ElMessage.success('订单已标记完成')
-            })
-    }
-    if (cmd === 'delete') {
-        ElMessageBox.confirm('确认删除该订单吗?此操作不可恢复', '警告', { type: 'error' })
-            .then(() => {
-                tableData.value = tableData.value.filter(item => item.id !== row.id)
-                ElMessage.success('订单已删除')
-            })
-    }
-}
+  if (cmd === 'reward') openRewardDialog(row);
+  if (cmd === 'remark') openRemarkDialog(row);
+  if (cmd === 'care_summary') openCareSummary(row);
+  if (cmd === 'complete') {
+    ElMessageBox.confirm('确认将该订单手动标记为完成吗?', '提示', { type: 'warning' }).then(() => {
+      row.status = 4;
+      ElMessage.success('订单已标记完成');
+    });
+  }
+  if (cmd === 'delete') {
+    ElMessageBox.confirm('确认删除该订单吗?此操作不可恢复', '警告', { type: 'error' }).then(() => {
+      tableData.value = tableData.value.filter((item) => item.id !== row.id);
+      ElMessage.success('订单已删除');
+    });
+  }
+};
 </script>
 
 <style scoped>
 .page-container {
-    padding: 20px;
+  padding: 20px;
 }
 
 .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 }
 
 .title {
-    font-weight: bold;
-    font-size: 18px;
+  font-weight: bold;
+  font-size: 18px;
 }
 
 .right-panel {
-    display: flex;
-    gap: 10px;
-    align-items: center;
+  display: flex;
+  gap: 10px;
+  align-items: center;
 }
 
 .search-input {
-    width: 220px;
+  width: 220px;
 }
 
 .status-tabs {
-    margin-top: 10px;
-    margin-bottom: -10px;
+  margin-top: 10px;
+  margin-bottom: -10px;
 }
 
 .pagination-container {
-    display: flex;
-    justify-content: flex-end;
-    margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 20px;
 }
 
 /* Table Content Styles */
 .service-type-cell {
-    display: flex;
-    flex-direction: row;
-    gap: 4px;
-    align-items: center;
+  display: flex;
+  flex-direction: row;
+  gap: 4px;
+  align-items: center;
 }
 
 .sub-tag {
-    font-size: 11px;
-    height: 20px;
-    padding: 0 5px;
+  font-size: 11px;
+  height: 20px;
+  padding: 0 5px;
 }
 
 .pet-info {
-    display: flex;
-    align-items: center;
-    gap: 10px;
+  display: flex;
+  align-items: center;
+  gap: 10px;
 }
 
 .pet-info .el-avatar {
-    background: #e0eaff;
-    color: #409eff;
-    font-weight: bold;
-    flex-shrink: 0;
+  background: #e0eaff;
+  color: #409eff;
+  font-weight: bold;
+  flex-shrink: 0;
 }
 
 .pet-info .avatar-feeding {
-    background: #fdf6ec;
-    color: #e6a23c;
+  background: #fdf6ec;
+  color: #e6a23c;
 }
 
 .pet-info .avatar-washing {
-    background: #f0f9eb;
-    color: #67c23a;
+  background: #f0f9eb;
+  color: #67c23a;
 }
 
 .pet-detail {
-    display: flex;
-    flex-direction: column;
-    line-height: 1.4;
+  display: flex;
+  flex-direction: column;
+  line-height: 1.4;
 }
 
 .pet-name {
-    font-weight: bold;
-    font-size: 14px;
-    color: #303133;
+  font-weight: bold;
+  font-size: 14px;
+  color: #303133;
 }
 
 .pet-breed {
-    color: #909399;
-    font-weight: normal;
-    font-size: 12px;
+  color: #909399;
+  font-weight: normal;
+  font-size: 12px;
 }
 
 .merchant-info {
-    display: flex;
-    flex-direction: column;
-    line-height: 1.4;
+  display: flex;
+  flex-direction: column;
+  line-height: 1.4;
 }
 
 .sub-text {
-    font-size: 12px;
-    color: #999;
+  font-size: 12px;
+  color: #999;
 }
 
 .text-gray {
-    color: #ccc;
-    font-style: italic;
+  color: #ccc;
+  font-style: italic;
 }
 
 .time-text {
-    font-size: 13px;
-    color: #606266;
+  font-size: 13px;
+  color: #606266;
 }
 
 .status-cell {
-    display: flex;
-    align-items: center;
+  display: flex;
+  align-items: center;
 }
 
 .status-dot {
-    width: 6px;
-    height: 6px;
-    border-radius: 50%;
-    margin-right: 6px;
-    background-color: #909399;
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  margin-right: 6px;
+  background-color: #909399;
 }
 
 .status-dot.pending_dispatch {
-    background-color: #f56c6c;
-    box-shadow: 0 0 4px rgba(245, 108, 108, 0.4);
+  background-color: #f56c6c;
+  box-shadow: 0 0 4px rgba(245, 108, 108, 0.4);
 }
 
 .status-dot.pending_accept {
-    background-color: #e6a23c;
+  background-color: #e6a23c;
 }
 
 .status-dot.serving {
-    background-color: #409eff;
+  background-color: #409eff;
 }
 
 .status-dot.pending_confirm {
-    background-color: #bf24e8;
+  background-color: #bf24e8;
 }
 
 .status-dot.completed {
-    background-color: #67c23a;
+  background-color: #67c23a;
 }
 
 .status-dot.cancelled {
-    background-color: #909399;
+  background-color: #909399;
 }
 
 .fulfiller-info {
-    display: flex;
-    flex-direction: column;
+  display: flex;
+  flex-direction: column;
 }
 
 .fulfiller-name {
-    font-weight: 500;
-    color: #333;
+  font-weight: 500;
+  color: #333;
 }
 
 .fulfiller-fee {
-    font-size: 12px;
-    color: #e6a23c;
+  font-size: 12px;
+  color: #e6a23c;
 }
 
 .op-cell {
-    display: flex;
-    align-items: center;
-    gap: 8px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
 }
 
 .el-dropdown-link {
-    cursor: pointer;
-    color: #409eff;
-    font-size: 12px;
-    display: flex;
-    align-items: center;
-    line-height: 1;
-    height: 24px;
+  cursor: pointer;
+  color: #409eff;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  line-height: 1;
+  height: 24px;
 }
 </style>