<script setup lang="ts">
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
import { Download, Refresh, Search } from '@element-plus/icons-vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { getOrderDetail, getOrders, payOrder, refundOrder, verifyOrder } from '../api/admin'
|
import type { Order } from '../types/admin'
|
import { downloadExcel } from '../utils/excel'
|
|
const loading = ref(false)
|
const detailLoading = ref(false)
|
const detailVisible = ref(false)
|
const activeTab = ref<'GROUP' | 'STORE'>('GROUP')
|
const orders = ref<Order[]>([])
|
const activeOrder = ref<Order>()
|
|
const cardLevels = ['钻石卡', '卓越人才卡', '精英人才卡', '青年人才卡']
|
|
const filters = reactive({
|
talentKeyword: '',
|
merchantName: '',
|
cardLevel: '',
|
hasSubsidy: undefined as number | undefined,
|
familyOrder: undefined as number | undefined,
|
status: undefined as number | undefined,
|
orderTime: [] as string[],
|
})
|
|
const pagination = reactive({
|
page: 1,
|
size: 10,
|
total: 0,
|
})
|
|
const isGroupOrder = computed(() => activeTab.value === 'GROUP')
|
const itemNameLabel = computed(() => (isGroupOrder.value ? '团购名称' : '消费项目'))
|
const amountLabel = computed(() => (isGroupOrder.value ? '团购金额' : '消费金额'))
|
|
function moneyText(value?: number) {
|
if (value === undefined || value === null) return ''
|
const yuan = Number(value) / 100
|
return Number.isInteger(yuan) ? String(yuan) : yuan.toFixed(2)
|
}
|
|
function formatDateTime(value?: string) {
|
return value ? value.replace('T', ' ') : ''
|
}
|
|
function orderStatusText(row: Order) {
|
if (row.orderStatus === 0) return '待支付'
|
if (row.orderStatus === 2) return '已退款'
|
if (row.verifyStatus === 1) return '已核销'
|
return '待核销'
|
}
|
|
function canPay(row?: Order) {
|
return row?.orderStatus === 0
|
}
|
|
function canVerify(row?: Order) {
|
return row?.orderStatus === 1 && row.verifyStatus !== 1
|
}
|
|
function canRefund(row?: Order) {
|
return row?.orderStatus === 1 && row.verifyStatus !== 1
|
}
|
|
async function loadData() {
|
loading.value = true
|
try {
|
const result = await getOrders({
|
page: pagination.page,
|
size: pagination.size,
|
orderType: activeTab.value,
|
talentKeyword: filters.talentKeyword || undefined,
|
merchantName: filters.merchantName || undefined,
|
cardLevel: filters.cardLevel || undefined,
|
hasSubsidy: filters.hasSubsidy,
|
familyOrder: isGroupOrder.value ? filters.familyOrder : undefined,
|
status: filters.status,
|
orderStart: filters.orderTime?.[0],
|
orderEnd: filters.orderTime?.[1],
|
})
|
orders.value = result.records
|
pagination.total = result.total
|
} catch {
|
orders.value = []
|
pagination.total = 0
|
} finally {
|
loading.value = false
|
}
|
}
|
|
function search() {
|
pagination.page = 1
|
loadData()
|
}
|
|
function resetFilters() {
|
filters.talentKeyword = ''
|
filters.merchantName = ''
|
filters.cardLevel = ''
|
filters.hasSubsidy = undefined
|
filters.familyOrder = undefined
|
filters.status = undefined
|
filters.orderTime = []
|
search()
|
}
|
|
async function download() {
|
const result = await getOrders({
|
page: 1, size: 1000, orderType: activeTab.value, talentKeyword: filters.talentKeyword || undefined,
|
merchantName: filters.merchantName || undefined, cardLevel: filters.cardLevel || undefined,
|
hasSubsidy: filters.hasSubsidy, familyOrder: isGroupOrder.value ? filters.familyOrder : undefined,
|
status: filters.status, orderStart: filters.orderTime?.[0], orderEnd: filters.orderTime?.[1],
|
})
|
downloadExcel(activeTab.value === 'GROUP' ? '团购订单' : '到店消费订单', [
|
{ title: '订单号', value: row => row.orderNo }, { title: '持卡人', value: row => row.talentName },
|
{ title: '手机号', value: row => row.talentMobile }, { title: '人才等级', value: row => row.talentLevel },
|
{ title: '商家', value: row => row.merchantName }, { title: '商品/权益', value: row => row.productName || row.benefitName },
|
{ title: '订单金额(元)', value: row => moneyText(row.orderAmount) }, { title: '补贴金额(元)', value: row => moneyText(row.subsidyAmount) },
|
{ title: '下单时间', value: row => formatDateTime(row.createdAt) }, { title: '支付时间', value: row => formatDateTime(row.payTime) },
|
{ title: '订单状态', value: row => orderStatusText(row) },
|
], result.records)
|
ElMessage.success(`已导出 ${result.records.length} 条订单`)
|
}
|
|
async function viewDetail(row: Order) {
|
if (!row.orderNo) return
|
detailVisible.value = true
|
detailLoading.value = true
|
try {
|
activeOrder.value = await getOrderDetail(row.orderNo)
|
} finally {
|
detailLoading.value = false
|
}
|
}
|
|
async function payActiveOrder() {
|
if (!activeOrder.value?.orderNo) return
|
await ElMessageBox.confirm('确认将该订单标记为已支付?', '订单支付', { type: 'warning' })
|
activeOrder.value = await payOrder(activeOrder.value.orderNo)
|
ElMessage.success('订单已支付')
|
await loadData()
|
}
|
|
async function verifyActiveOrder() {
|
if (!activeOrder.value?.orderNo) return
|
const verifyType = activeTab.value === 'GROUP' ? 'GROUP' : 'STORE_CONSUME'
|
await ElMessageBox.confirm('确认核销该订单?', '订单核销', { type: 'warning' })
|
activeOrder.value = await verifyOrder(activeOrder.value.orderNo, verifyType, activeOrder.value.merchantId)
|
ElMessage.success('订单已核销')
|
await loadData()
|
}
|
|
async function refundActiveOrder() {
|
if (!activeOrder.value?.orderNo) return
|
const defaultAmount = moneyText(activeOrder.value.payAmount)
|
const { value } = await ElMessageBox.prompt('请输入退款金额(元)', '订单退款', {
|
inputValue: defaultAmount,
|
inputPattern: /^(0|[1-9]\d*)(\.\d{1,2})?$/,
|
inputErrorMessage: '请输入正确的金额',
|
})
|
await refundOrder(activeOrder.value.orderNo, Number(value))
|
ElMessage.success('退款成功')
|
detailVisible.value = false
|
await loadData()
|
}
|
|
watch(activeTab, () => {
|
filters.familyOrder = undefined
|
search()
|
})
|
|
onMounted(loadData)
|
</script>
|
|
<template>
|
<div class="page-stack prototype-page order-list-page">
|
<el-segmented
|
v-model="activeTab"
|
class="order-type-tabs"
|
:options="[
|
{ label: '团购订单', value: 'GROUP' },
|
{ label: '到店消费', value: 'STORE' },
|
]"
|
/>
|
|
<section class="panel">
|
<div class="panel-header">
|
<h3>查询表格</h3>
|
</div>
|
|
<el-form class="order-filter-form" :model="filters" label-width="84px">
|
<el-form-item label="持卡人">
|
<el-input v-model="filters.talentKeyword" placeholder="持卡人姓名、手机号、卡号" clearable />
|
</el-form-item>
|
<el-form-item label="商家名称">
|
<el-input v-model="filters.merchantName" placeholder="请输入商家名称" clearable />
|
</el-form-item>
|
<el-form-item label="卡片等级">
|
<el-select v-model="filters.cardLevel" placeholder="全部" clearable>
|
<el-option label="全部" value="" />
|
<el-option v-for="level in cardLevels" :key="level" :label="level" :value="level" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="是否补贴">
|
<el-select v-model="filters.hasSubsidy" placeholder="全部" clearable>
|
<el-option label="全部" :value="undefined" />
|
<el-option label="是" :value="1" />
|
<el-option label="否" :value="0" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="订单时间">
|
<el-date-picker
|
v-model="filters.orderTime"
|
type="daterange"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
range-separator="至"
|
value-format="YYYY-MM-DD"
|
/>
|
</el-form-item>
|
<el-form-item label="状态">
|
<el-select v-model="filters.status" placeholder="全部" clearable>
|
<el-option label="全部" :value="undefined" />
|
<el-option label="待支付" :value="0" />
|
<el-option label="已支付" :value="1" />
|
<el-option label="已退款" :value="2" />
|
</el-select>
|
</el-form-item>
|
<el-form-item v-if="isGroupOrder" label="是否家属下单">
|
<el-select v-model="filters.familyOrder" placeholder="全部" clearable>
|
<el-option label="全部" :value="undefined" />
|
<el-option label="是" :value="1" />
|
<el-option label="否" :value="0" />
|
</el-select>
|
</el-form-item>
|
<div class="order-filter-actions">
|
<el-button type="primary" :icon="Search" @click="search">查询</el-button>
|
<el-button :icon="Refresh" @click="resetFilters">重置</el-button>
|
</div>
|
</el-form>
|
|
<div class="order-table-tools">
|
<el-button :icon="Download" @click="download">下载</el-button>
|
</div>
|
|
<el-table v-loading="loading" :data="orders" border class="order-list-table" empty-text="暂无订单数据">
|
<el-table-column prop="orderNo" label="订单编号" width="90" show-overflow-tooltip />
|
<el-table-column prop="talentName" label="持卡人名称" width="92" />
|
<el-table-column prop="talentMobile" label="持卡人电话" width="112" />
|
<el-table-column prop="talentLevel" label="人才等级" width="104" />
|
<el-table-column prop="merchantName" label="商家名称" width="118" show-overflow-tooltip />
|
<el-table-column prop="merchantType" label="商家类型" width="86" />
|
<el-table-column :label="itemNameLabel" min-width="100" show-overflow-tooltip>
|
<template #default="{ row }">{{ row.benefitName || row.productName || '-' }}</template>
|
</el-table-column>
|
<el-table-column :label="amountLabel" width="82" align="center">
|
<template #default="{ row }">{{ moneyText(row.orderAmount) }}</template>
|
</el-table-column>
|
<el-table-column label="实际支付" width="82" align="center">
|
<template #default="{ row }">{{ moneyText(row.payAmount) }}</template>
|
</el-table-column>
|
<el-table-column label="优惠金额" width="82" align="center">
|
<template #default="{ row }">{{ moneyText((row.orderAmount || 0) - (row.payAmount || 0)) }}</template>
|
</el-table-column>
|
<el-table-column label="政府补贴" width="82" align="center">
|
<template #default="{ row }">{{ moneyText(row.subsidyAmount) }}</template>
|
</el-table-column>
|
<el-table-column label="下单时间" width="116" align="center"><template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template></el-table-column>
|
<el-table-column label="支付时间" width="116" align="center"><template #default="{ row }">{{ formatDateTime(row.payTime) }}</template></el-table-column>
|
<el-table-column label="状态" width="86" align="center">
|
<template #default="{ row }">{{ orderStatusText(row) }}</template>
|
</el-table-column>
|
<el-table-column label="操作" width="82" fixed="right" align="center">
|
<template #default="{ row }">
|
<el-button link type="primary" @click="viewDetail(row)">查看</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<div class="pagination-row">
|
<el-pagination
|
v-model:current-page="pagination.page"
|
v-model:page-size="pagination.size"
|
background
|
layout="total, sizes, prev, pager, next"
|
:page-sizes="[10, 20, 50]"
|
:total="pagination.total"
|
@change="loadData"
|
/>
|
</div>
|
</section>
|
|
<el-dialog v-model="detailVisible" title="订单详情" width="560px">
|
<div v-loading="detailLoading">
|
<el-descriptions v-if="activeOrder" :column="1" border>
|
<el-descriptions-item label="订单编号">{{ activeOrder.orderNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="持卡人">{{ activeOrder.talentName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="手机号码">{{ activeOrder.talentMobile || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="人才等级">{{ activeOrder.talentLevel || activeOrder.cardNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="商家名称">{{ activeOrder.merchantName || '-' }}</el-descriptions-item>
|
<el-descriptions-item :label="itemNameLabel">
|
{{ activeOrder.benefitName || activeOrder.productName || '-' }}
|
</el-descriptions-item>
|
<el-descriptions-item :label="amountLabel">{{ moneyText(activeOrder.orderAmount) }}</el-descriptions-item>
|
<el-descriptions-item label="实际支付">{{ moneyText(activeOrder.payAmount) }}</el-descriptions-item>
|
<el-descriptions-item label="政府补贴">{{ moneyText(activeOrder.subsidyAmount) }}</el-descriptions-item>
|
<el-descriptions-item label="下单时间">{{ formatDateTime(activeOrder.createdAt) || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="状态">{{ orderStatusText(activeOrder) }}</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
<template #footer>
|
<el-button @click="detailVisible = false">关闭</el-button>
|
<el-button v-if="canPay(activeOrder)" type="primary" @click="payActiveOrder">标记支付</el-button>
|
<el-button v-if="canVerify(activeOrder)" type="primary" @click="verifyActiveOrder">核销</el-button>
|
<el-button v-if="canRefund(activeOrder)" type="danger" plain @click="refundActiveOrder">退款</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|