OrderList.vue
9.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<template>
<div>
<a-card title="订单管理" :bordered="false" class="list-table-card">
<div class="list-toolbar">
<div class="list-toolbar-left">
<a-select v-model:value="filterStatus" placeholder="订单状态" allowClear class="list-filter" @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 class="list-filter" @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" class="list-search" />
</div>
</div>
<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">
<div class="soft-page-stack">
<div class="soft-note-card">
<strong>指派说明</strong>
<p>这里只展示当前订单可指派的骑手候选,列表中会带出手机号和在线/休息状态,避免只能靠 ID 操作。</p>
</div>
<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>
</div>
</a-modal>
<a-modal v-model:open="refundVisible" title="退款记录" :footer="null">
<div v-if="refundRecord" class="soft-page-stack">
<div class="soft-note-card">
<strong>退款处理说明</strong>
<p>通过或拒绝退款后会更新订单退款状态,处理前建议先核对申请角色、原因和金额。</p>
</div>
<a-descriptions :column="1" bordered size="small">
<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>
<div v-if="refundRecord.status === 0" class="soft-inline-actions">
<a-popconfirm title="确认通过退款?" @confirm="handleRefund(1)">
<a-button type="primary">通过退款</a-button>
</a-popconfirm>
<a-button danger @click="openReject">拒绝退款</a-button>
</div>
</div>
</a-modal>
<a-modal v-model:open="rejectVisible" title="拒绝退款" @ok="handleRefund(2)" :confirmLoading="saving">
<div class="soft-page-stack">
<div class="soft-note-card">
<strong>填写拒绝原因</strong>
<p>拒绝原因会进入退款处理记录,建议填写明确、可追溯的说明。</p>
</div>
<a-textarea v-model:value="rejectRemark" :rows="3" placeholder="填写拒绝原因" />
</div>
</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>