OrderList.vue 8.69 KB
<template>
  <div>
    <a-card title="订单管理" :bordered="false">
      <template #extra>
        <a-space>
          <a-select v-model:value="filterStatus" placeholder="订单状态" allowClear style="width:130px" @change="loadList">
            <a-select-option :value="2">已支付</a-select-option>
            <a-select-option :value="3">已接单</a-select-option>
            <a-select-option :value="4">服务中</a-select-option>
            <a-select-option :value="6">已完成</a-select-option>
            <a-select-option :value="7">退款申请</a-select-option>
            <a-select-option :value="10">已取消</a-select-option>
          </a-select>
          <a-select v-model:value="filterTrans" placeholder="转单状态" allowClear style="width:130px" @change="loadList">
            <a-select-option :value="2">转单申请中</a-select-option>
            <a-select-option :value="1">已转单</a-select-option>
            <a-select-option :value="3">转单拒绝</a-select-option>
          </a-select>
          <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" style="width:200px" />
        </a-space>
      </template>
      <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'status'">
            <a-tag :color="statusColor[record.status]">{{ statusMap[record.status] }}</a-tag>
          </template>
          <template v-if="column.key === 'isTrans'">
            <a-tag v-if="record.isTrans === 2" color="orange">申请中</a-tag>
            <a-tag v-else-if="record.isTrans === 1" color="blue">已转单</a-tag>
            <a-tag v-else-if="record.isTrans === 3" color="red">已拒绝</a-tag>
            <span v-else>-</span>
          </template>
          <template v-if="column.key === 'action'">
            <a-space>
              <a @click="openDesignate(record)" v-if="record.status === 2">指派骑手</a>
              <template v-if="record.isTrans === 2 && record.status === 4">
                <a-popconfirm title="通过转单申请?" @confirm="handleTrans(record.id, 1)">
                  <a style="color:green">通过转单</a>
                </a-popconfirm>
                <a-popconfirm title="拒绝转单申请?" @confirm="handleTrans(record.id, 3)">
                  <a style="color:red">拒绝转单</a>
                </a-popconfirm>
              </template>
              <a v-if="record.status === 7" @click="openRefund(record)">查看退款</a>
            </a-space>
          </template>
        </template>
      </a-table>
    </a-card>

    <!-- 指派骑手弹窗 -->
    <a-modal v-model:open="designateVisible" title="指派骑手" @ok="handleDesignate" :confirmLoading="saving">
      <a-form layout="vertical">
        <a-form-item label="选择骑手">
          <a-select
            v-model:value="designateRiderId"
            style="width:100%"
            placeholder="请选择可指派骑手"
            :loading="candidateLoading"
            show-search
            :filter-option="filterCandidateOption"
            option-label-prop="label"
          >
            <a-select-option
              v-for="item in designateCandidates"
              :key="item.id"
              :value="item.id"
              :label="`${item.userNickname || '未命名'}(ID:${item.id})`"
            >
              {{ item.userNickname || '未命名' }}(ID:{{ item.id }} / {{ item.mobile || '无手机号' }} / {{ item.isRest === 1 ? '休息' : '在线' }})
            </a-select-option>
          </a-select>
        </a-form-item>
      </a-form>
    </a-modal>

    <!-- 退款记录弹窗 -->
    <a-modal v-model:open="refundVisible" title="退款记录" :footer="null">
      <a-descriptions :column="1" bordered size="small" v-if="refundRecord">
        <a-descriptions-item label="订单号">{{ refundRecord.orderNo }}</a-descriptions-item>
        <a-descriptions-item label="申请角色">{{ refundRecord.role === 1 ? '用户' : '骑手' }}</a-descriptions-item>
        <a-descriptions-item label="退款原因">{{ refundRecord.reason }}</a-descriptions-item>
        <a-descriptions-item label="退款金额">¥{{ refundRecord.money }}</a-descriptions-item>
        <a-descriptions-item label="状态">
          <a-tag :color="refundRecord.status === 1 ? 'green' : refundRecord.status === 2 ? 'red' : 'orange'">
            {{ ({'0': '待处理', '1': '已通过', '2': '已拒绝'} as Record<string, string>)[String(refundRecord.status)] }}
          </a-tag>
        </a-descriptions-item>
        <a-descriptions-item label="处理备注">{{ refundRecord.remark || '-' }}</a-descriptions-item>
      </a-descriptions>
      <a-space style="margin-top:16px" v-if="refundRecord && refundRecord.status === 0">
        <a-popconfirm title="确认通过退款?" @confirm="handleRefund(1)">
          <a-button type="primary">通过退款</a-button>
        </a-popconfirm>
        <a-button danger @click="openReject">拒绝退款</a-button>
      </a-space>
    </a-modal>

    <!-- 拒绝退款弹窗 -->
    <a-modal v-model:open="rejectVisible" title="拒绝退款" @ok="handleRefund(2)" :confirmLoading="saving">
      <a-textarea v-model:value="rejectRemark" :rows="3" placeholder="填写拒绝原因" />
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { riderApi, refundApi } from '@/api'

const loading = ref(false)
const saving = ref(false)
const list = ref<any[]>([])
const filterStatus = ref<number | undefined>()
const filterTrans = ref<number | undefined>()
const keyword = ref('')
const designateVisible = ref(false)
const candidateLoading = ref(false)
const refundVisible = ref(false)
const rejectVisible = ref(false)
const rejectRemark = ref('')
const designateOrderId = ref(0)
const designateRiderId = ref<number | undefined>()
const designateCandidates = ref<any[]>([])
const refundRecord = ref<any>(null)
const currentRefundRecordId = ref(0)

const statusMap: Record<number, string> = {
  1: '待支付', 2: '已支付', 3: '已接单', 4: '服务中',
  6: '已完成', 7: '退款申请', 8: '退款成功', 9: '退款拒绝', 10: '已取消'
}
const statusColor: Record<number, string> = {
  1: 'default', 2: 'blue', 3: 'cyan', 4: 'processing',
  6: 'green', 7: 'orange', 8: 'green', 9: 'red', 10: 'red'
}

const columns = [
  { title: 'ID', dataIndex: 'id', width: 80 },
  { title: '订单号', dataIndex: 'orderNo', ellipsis: true },
  { title: '状态', key: 'status' },
  { title: '骑手ID', dataIndex: 'riderId' },
  { title: '转单', key: 'isTrans' },
  { title: '配送费', dataIndex: 'moneyDelivery' },
  { title: '操作', key: 'action' },
]

async function loadList() {
  loading.value = true
  try {
    const res: any = await riderApi.orderList({
      status: filterStatus.value,
      isTrans: filterTrans.value,
      keyword: keyword.value,
      page: 1
    })
    list.value = Array.isArray(res?.data) ? res.data : []
  } finally { loading.value = false }
}

async function openDesignate(record: any) {
  designateOrderId.value = record.id
  designateRiderId.value = undefined
  designateCandidates.value = []
  candidateLoading.value = true
  try {
    const res: any = await riderApi.designateCandidates(record.id)
    designateCandidates.value = Array.isArray(res?.data) ? res.data : []
  } finally {
    candidateLoading.value = false
  }
  designateVisible.value = true
}

function filterCandidateOption(input: string, option: any) {
  return String(option.label || '').toLowerCase().includes(input.toLowerCase())
}

async function handleDesignate() {
  if (!designateRiderId.value) { message.error('请选择骑手'); return }
  saving.value = true
  try {
    await riderApi.designate(designateOrderId.value, designateRiderId.value)
    message.success('指派成功')
    designateVisible.value = false
    loadList()
  } finally { saving.value = false }
}

async function handleTrans(orderId: number, trans: number) {
  await riderApi.setTrans(orderId, trans)
  message.success('操作成功')
  loadList()
}

async function openRefund(record: any) {
  const res: any = await refundApi.record(record.id)
  refundRecord.value = res.data
  if (refundRecord.value) currentRefundRecordId.value = refundRecord.value.id
  refundVisible.value = true
}

function openReject() {
  rejectRemark.value = ''
  rejectVisible.value = true
}

async function handleRefund(status: number) {
  saving.value = true
  try {
    await refundApi.handle(currentRefundRecordId.value, status, rejectRemark.value)
    message.success('操作成功')
    refundVisible.value = false
    rejectVisible.value = false
    loadList()
  } finally { saving.value = false }
}

onMounted(loadList)
</script>