<script setup lang="tsx">
|
import { computed, onMounted, ref } from 'vue'
|
import {
|
NButton,
|
NCard,
|
NDataTable,
|
NForm,
|
NFormItem,
|
NGi,
|
NInput,
|
useMessage,
|
} from 'naive-ui'
|
import type { DataTableColumns } from 'naive-ui'
|
import { exportActivityOrderExcel, getFleetList, getOrderList } from '@/service/api/transaction'
|
import { operationPageList } from '@/service/api/vehicleConvoy'
|
import SearchPanel from '@/components/custom/SearchPanel.vue'
|
import Pagination from '@/components/common/Pagination.vue'
|
import { getRegionCascaderNoArea } from '@/service/api/operations'
|
import { downloadFile } from '@/utils'
|
import dayjs from 'dayjs'
|
|
const props = defineProps({
|
activityId: {
|
type: Number,
|
default: 0,
|
},
|
})
|
const router = useRouter()
|
const message = useMessage()
|
|
const searchModel = ref({
|
orderNumber: '',
|
nickName: '',
|
plateNumber: null,
|
fleetId: null,
|
orderSource: null,
|
cityCode: null,
|
status: null,
|
orderTime: null,
|
})
|
|
const loading = ref(false)
|
const list = ref<any[]>([])
|
const total = ref(0)
|
const currentPage = ref(1)
|
const pageSize = ref(10)
|
const checkedRowKeys = ref<string[]>([])
|
const isStatusOpen = ref(false)
|
const isCityCodeIdOpen = ref(false)
|
const isOrderSourceOpen = ref(false)
|
|
const orderTotal = ref(0)
|
const discountTotal = ref(0)
|
|
const isFleetIdOpen = ref(false)
|
const isPlateNumberOpen = ref(false)
|
// 车队
|
const options = ref<any[]>([])
|
const plateNumberOptions = ref<any[]>([])
|
// 渠道
|
const chanleOptions = [
|
{
|
label: '小鹏小程序',
|
value: 1,
|
},
|
]
|
const pcaOptions = ref<any[]>([])
|
|
function handleSearch() {
|
currentPage.value = 1
|
getList()
|
}
|
|
function handleReset() {
|
searchModel.value = {
|
orderNumber: '',
|
nickName: '',
|
plateNumber: null,
|
fleetId: null,
|
orderSource: null,
|
cityCode: null,
|
status: null,
|
orderTime: null,
|
}
|
currentPage.value = 1
|
getList()
|
}
|
|
function handleCheck(keys: string[]) {
|
checkedRowKeys.value = keys
|
}
|
|
function handleDetail(row: any) {
|
router.push({
|
name: 'orderDetail',
|
query: {
|
orderId: row.id!,
|
},
|
})
|
}
|
|
// 订单状态
|
const statusOptions = [
|
{
|
label: '待接单',
|
value: 1,
|
type: 'primary',
|
},
|
{
|
label: '待出发',
|
value: 2,
|
type: 'success',
|
},
|
{
|
label: '待到达预约地点',
|
value: 3,
|
type: 'warning',
|
},
|
{
|
label: '待乘客上车',
|
value: 4,
|
type: 'primary',
|
},
|
{
|
label: '乘客已上车',
|
value: 5,
|
type: 'success',
|
},
|
{
|
label: '服务中',
|
value: 6,
|
type: 'success',
|
},
|
{
|
label: '待客下车',
|
value: 7,
|
type: 'warning',
|
},
|
{
|
label: '待支付',
|
value: 8,
|
type: 'warning',
|
},
|
{
|
label: '待评价',
|
value: 9,
|
type: 'default',
|
},
|
{
|
label: '已完成',
|
value: 10,
|
type: 'success',
|
},
|
{
|
label: '已取消',
|
value: 0,
|
type: 'default',
|
},
|
]
|
|
async function getList() {
|
loading.value = true
|
const params = {
|
pageNum: currentPage.value,
|
pageSize: pageSize.value,
|
activityId: props.activityId,
|
status: searchModel.value.status,
|
orderNumber: searchModel.value.orderNumber,
|
nickName: searchModel.value.nickName,
|
plateNumber: searchModel.value.plateNumber,
|
fleetId: searchModel.value.fleetId,
|
orderSource: searchModel.value.orderSource,
|
cityCode: searchModel.value.cityCode,
|
startTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[0]).format('YYYY-MM-DD') : null,
|
endTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[1]).format('YYYY-MM-DD') : null,
|
}
|
getOrderList(params).then((res) => {
|
if (res.code === 200) {
|
total.value = res.data.total || res.data.records?.length
|
list.value = res.data.records || []
|
orderTotal.value = res.data.records[0]?.orderTotalAmount || 0
|
discountTotal.value = res.data.records[0]?.discountTotalAmount || 0
|
}
|
}).finally(() => {
|
loading.value = false
|
})
|
}
|
|
function handlePageChange(page: number, size: number) {
|
currentPage.value = page
|
pageSize.value = size
|
getList()
|
}
|
|
const filterItems = computed(() => [
|
{
|
label: '订单号',
|
path: 'orderNumber',
|
render: () => <NInput v-model:value={searchModel.value.orderNumber} placeholder="请输入" clearable />,
|
},
|
{
|
label: '用户',
|
path: 'nickName',
|
render: () => <NInput v-model:value={searchModel.value.nickName} placeholder="请输入" clearable />,
|
},
|
{
|
label: '车辆',
|
path: 'plateNumber',
|
render: () => (
|
<n-select
|
v-model:value={searchModel.value.plateNumber}
|
onUpdate:show={(val: any) => isPlateNumberOpen.value = val}
|
options={plateNumberOptions.value}
|
filterable
|
clearable
|
>
|
{{
|
arrow: () => (
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class={`arrow-icon ${isPlateNumberOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src="/src/assets/element-img/no-data.png"
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '车队',
|
path: 'fleet',
|
render: () => (
|
<n-select
|
v-model:value={searchModel.value.fleetId}
|
onUpdate:show={(val: any) => isFleetIdOpen.value = val}
|
options={options.value}
|
clearable
|
>
|
{{
|
arrow: () => (
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class={`arrow-icon ${isFleetIdOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src="/src/assets/element-img/no-data.png"
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '渠道',
|
path: 'orderSource',
|
render: () => (
|
<n-select
|
v-model:value={searchModel.value.orderSource}
|
onUpdate:show={(val: any) => isOrderSourceOpen.value = val}
|
options={chanleOptions}
|
clearable
|
>
|
{{
|
arrow: () => (
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class={`arrow-icon ${isOrderSourceOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src="/src/assets/element-img/no-data.png"
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '城市',
|
path: 'city',
|
render: () => (
|
<n-cascader v-model:value={searchModel.value.cityCode} options={pcaOptions.value} onUpdate:show={(val: any) => isCityCodeIdOpen.value = val} value-field="code" label-field="name" check-strategy="child" clearable>
|
{{
|
arrow: () => (
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class={`arrow-icon ${isCityCodeIdOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
}}
|
</n-cascader>
|
),
|
},
|
{
|
label: '订单状态',
|
path: 'status',
|
render: () => (
|
<n-select
|
v-model:value={searchModel.value.status}
|
onUpdate:show={(val: any) => isStatusOpen.value = val}
|
options={statusOptions}
|
clearable
|
>
|
{{
|
arrow: () => (
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class={`arrow-icon ${isStatusOpen.value ? 'arrow-icon--open' : ''}`}
|
style={{ width: '16px', height: '16px' }}
|
alt="arrow"
|
/>
|
),
|
empty: () => (
|
<div class="flex-center">
|
<img
|
src="/src/assets/element-img/no-data.png"
|
style={{ width: '50%' }}
|
/>
|
</div>
|
),
|
}}
|
</n-select>
|
),
|
},
|
{
|
label: '下单时间',
|
path: 'orderTime',
|
render: () => <n-date-picker format="yyyy/MM/dd" v-model:value={searchModel.value.orderTime} type="daterange" clearable actions={[]} close-on-select={true} />,
|
},
|
])
|
|
const columns = computed<DataTableColumns<any>>(() => [
|
{
|
type: 'selection',
|
width: 40,
|
fixed: 'left',
|
},
|
{
|
title: '订单编号',
|
key: 'orderNumber',
|
width: 150,
|
resizable: true,
|
fixed: 'left',
|
},
|
{
|
title: '下单时间',
|
key: 'createTime',
|
width: 160,
|
resizable: true,
|
render: row => <span>{row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-'}</span>,
|
},
|
{
|
title: '订单来源',
|
key: 'orderSource',
|
width: 100,
|
resizable: true,
|
render(row) {
|
return row.orderSource === 1 ? '小鹏小程序' : '-'
|
},
|
},
|
{
|
title: '乘车时间',
|
key: 'rideTime',
|
width: 160,
|
resizable: true,
|
},
|
{
|
title: '下单用户昵称',
|
key: 'nickName',
|
width: 120,
|
resizable: true,
|
},
|
{
|
title: '下单用户手机',
|
key: 'phoneNumber',
|
width: 120,
|
resizable: true,
|
},
|
{
|
title: '起点',
|
key: 'startPointName',
|
width: 160,
|
resizable: true,
|
ellipsis: {
|
tooltip: true,
|
},
|
},
|
{
|
title: '终点',
|
key: 'endPointName',
|
width: 160,
|
resizable: true,
|
ellipsis: {
|
tooltip: true,
|
},
|
},
|
{
|
title: '订单金额',
|
key: 'orderMoney',
|
width: 120,
|
resizable: true,
|
render(row) {
|
return row.orderMoney ? `¥${row.orderMoney.toFixed(2)}` : '-'
|
},
|
},
|
{
|
title: '优惠金额',
|
key: 'discountAmount',
|
width: 120,
|
resizable: true,
|
render(row) {
|
return row.discountAmount ? `¥${row.discountAmount.toFixed(2)}` : '-'
|
},
|
},
|
{
|
title: '操作',
|
key: 'actions',
|
width: 80,
|
fixed: 'right',
|
render: (row) => {
|
return <NButton text type="primary" onClick={() => handleDetail(row)}>详情</NButton>
|
},
|
},
|
])
|
|
async function handleExport() {
|
let params: any = {}
|
if (!checkedRowKeys.value.length) {
|
return
|
}
|
params = {
|
pageNum: currentPage.value,
|
pageSize: pageSize.value,
|
activityId: props.activityId,
|
status: searchModel.value.status,
|
orderNumber: searchModel.value.orderNumber,
|
nickName: searchModel.value.nickName,
|
plateNumber: searchModel.value.plateNumber,
|
fleetId: searchModel.value.fleetId,
|
orderSource: searchModel.value.orderSource,
|
cityCode: searchModel.value.cityCode,
|
orderTime: searchModel.value.orderTime,
|
startTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[0]).format('YYYY-MM-DD') : null,
|
endTime: searchModel.value.orderTime ? dayjs(searchModel.value.orderTime?.[1]).format('YYYY-MM-DD') : null,
|
}
|
const response = await exportActivityOrderExcel(params)
|
if (response instanceof Blob) {
|
downloadFile(response, `活动订单列表_${new Date().getTime()}.xlsx`)
|
message.success(`导出成功,共${checkedRowKeys.value.length}条数据`)
|
}
|
}
|
|
let parentIdCounter = 0
|
|
function formatCascaderData(list: any[]) {
|
return list.map((item) => {
|
const newItem = { ...item }
|
if (newItem.children && newItem.children.length > 0) {
|
parentIdCounter++
|
newItem.code = `parent_${item.code}_${parentIdCounter}`
|
newItem.children = formatCascaderData(newItem.children)
|
}
|
else {
|
delete newItem.children
|
}
|
return newItem
|
})
|
}
|
|
async function fetchCascaderData() {
|
const res = await getRegionCascaderNoArea()
|
if (res.code === 200 && res.data) {
|
pcaOptions.value = formatCascaderData(res.data)
|
}
|
}
|
|
async function fetchFleetList() {
|
const res = await getFleetList()
|
if (res.code === 200) {
|
options.value = res.data.map((item: any) => ({
|
label: item.fleetName,
|
value: item.id,
|
}))
|
}
|
}
|
|
async function fetchPlateNumberOptions() {
|
const res = await operationPageList({
|
pageNum: 1,
|
pageSize: 99999,
|
})
|
if (res.code !== 200 || !res.data || !res.data.records) {
|
plateNumberOptions.value = []
|
return
|
}
|
const records = Array.isArray(res.data.records) ? res.data.records : []
|
const uniquePlateNumbers = Array.from(new Set(
|
records
|
.map((item: any) => item.plateNumber)
|
.filter((item: any) => item !== null && item !== undefined && String(item).trim() !== ''),
|
))
|
plateNumberOptions.value = uniquePlateNumbers.map((item: any) => ({
|
label: String(item),
|
value: String(item),
|
}))
|
}
|
|
onMounted(() => {
|
getList()
|
fetchCascaderData()
|
fetchFleetList()
|
fetchPlateNumberOptions()
|
})
|
</script>
|
|
<template>
|
<div class="orderListCard roboTaxi">
|
<div class="roboTaxi-form-card">
|
<NForm
|
:model="searchModel"
|
label-placement="left"
|
inline
|
:show-feedback="false"
|
>
|
<SearchPanel>
|
<NGi v-for="item in filterItems" :key="item.path" class="n-grid-item">
|
<NFormItem :label="item.label" :path="item.path">
|
<component :is="item.render" />
|
</NFormItem>
|
</NGi>
|
<template #action>
|
<NButton @click="handleReset">
|
重置
|
</NButton>
|
<NButton type="primary" @click="handleSearch">
|
查询
|
</NButton>
|
</template>
|
</SearchPanel>
|
</NForm>
|
</div>
|
|
<NCard class="flex-1 robotaxi-table-card" :bordered="false">
|
<template #header>
|
<div class="flex items-center gap-16px">
|
<div class="flex gap-8px items-center">
|
<span
|
v-if="checkedRowKeys.length > 0"
|
style="color: rgba(23, 31, 38, 0.6); font-weight: 400"
|
>
|
{{
|
$t("accountManagement.messages.selectedRecords", {
|
count: checkedRowKeys.length,
|
})
|
}}
|
</span>
|
<NButton :disabled="checkedRowKeys.length === 0" @click="handleExport">
|
批量导出订单
|
</NButton>
|
</div>
|
<div class="label">
|
<span class="mr-16px">订单金额合计: <span class="value">{{ orderTotal }}元</span>
|
</span>
|
<span>优惠金额合计: <span class="value">{{ discountTotal }}元</span>
|
</span>
|
</div>
|
</div>
|
</template>
|
<div class="flex-1 flex flex-col h-full">
|
<NDataTable
|
class="flex-1"
|
:columns="columns"
|
:data="list"
|
:loading="loading"
|
:bordered="false"
|
:scroll-x="1500"
|
:row-key="(row) => row.id"
|
flex-height
|
@update:checked-row-keys="handleCheck"
|
/>
|
<Pagination
|
:count="total"
|
:page="currentPage"
|
:page-size="pageSize"
|
class="robotaxi-pagination mt-12px"
|
@change="handlePageChange"
|
/>
|
</div>
|
</NCard>
|
</div>
|
</template>
|
|
<style scoped>
|
.orderListCard {
|
height: 100% !important;
|
display: flex;
|
flex-direction: column;
|
gap: 12px;
|
}
|
.label {
|
font-size: 13px;
|
font-weight: 400;
|
line-height: 18px;
|
color: rgba(23, 31, 38, 0.6);
|
}
|
.value {
|
font-size: 13px;
|
font-weight: 400;
|
line-height: 18px;
|
color: #171f26;
|
}
|
|
.arrow-icon {
|
transition: transform 0.2s ease;
|
}
|
|
.arrow-icon--open {
|
transform: rotate(180deg);
|
}
|
</style>
|