130 lines
3.8 KiB
Vue
130 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted, reactive, ref, watch } from 'vue'
|
|
import { RouterLink } from 'vue-router'
|
|
import { Search, ClipboardList } from 'lucide-vue-next'
|
|
import { fetchOrderList } from '@/api/orders'
|
|
import StatusTag from '@/components/StatusTag.vue'
|
|
import type { CommodityOrder, CommodityOrderSearch } from '@/types/order'
|
|
import { commodityTypeText } from '@/utils/constants'
|
|
|
|
const listLoading = ref(false)
|
|
const requesting = ref(false)
|
|
const refreshing = ref(false)
|
|
const finished = ref(false)
|
|
const records = ref<CommodityOrder[]>([])
|
|
const status = ref('')
|
|
const keyword = ref('')
|
|
|
|
const query = reactive<CommodityOrderSearch>({
|
|
pageNum: 1,
|
|
pageSize: 10
|
|
})
|
|
|
|
const buildSearchQuery = (): CommodityOrderSearch => ({
|
|
...query,
|
|
contactPhone: keyword.value.trim(),
|
|
orderStatus: String(status.value || '')
|
|
})
|
|
|
|
const loadOrders = async (reset = false) => {
|
|
if (requesting.value) {
|
|
refreshing.value = false
|
|
return
|
|
}
|
|
requesting.value = true
|
|
listLoading.value = true
|
|
if (reset) {
|
|
query.pageNum = 1
|
|
finished.value = false
|
|
}
|
|
try {
|
|
const page = await fetchOrderList(buildSearchQuery())
|
|
records.value = reset ? page.records : [...records.value, ...page.records]
|
|
finished.value = records.value.length >= page.total || page.records.length === 0
|
|
query.pageNum += 1
|
|
} finally {
|
|
requesting.value = false
|
|
listLoading.value = false
|
|
refreshing.value = false
|
|
}
|
|
}
|
|
|
|
const onRefresh = () => {
|
|
refreshing.value = true
|
|
loadOrders(true)
|
|
}
|
|
|
|
watch(status, () => loadOrders(true))
|
|
|
|
onMounted(() => loadOrders(true))
|
|
</script>
|
|
|
|
<template>
|
|
<section class="page">
|
|
<header class="page-header">
|
|
<p class="page-kicker">订单管理</p>
|
|
<h1 class="page-title">订单列表</h1>
|
|
</header>
|
|
|
|
<div class="toolbar">
|
|
<van-search
|
|
v-model="keyword"
|
|
shape="round"
|
|
placeholder="手机号"
|
|
@search="loadOrders(true)"
|
|
@clear="loadOrders(true)"
|
|
>
|
|
<template #left-icon><Search :size="16" /></template>
|
|
</van-search>
|
|
</div>
|
|
|
|
<van-tabs v-model:active="status" shrink>
|
|
<van-tab title="全部" name="" />
|
|
<van-tab title="待使用" name="2" />
|
|
<van-tab title="待确认" name="1" />
|
|
<van-tab title="退款中" name="4" />
|
|
<van-tab title="已完成" name="6" />
|
|
</van-tabs>
|
|
|
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
|
<van-list v-model:loading="listLoading" :finished="finished" finished-text="没有更多订单" @load="loadOrders">
|
|
<div v-if="records.length" class="order-list">
|
|
<RouterLink v-for="order in records" :key="order.id" class="order-card" :to="`/orders/${order.id}`">
|
|
<div class="order-card__main">
|
|
<div class="thumb">
|
|
<img :src="order.commodityCoverPhoto" alt="" />
|
|
</div>
|
|
<div class="stack">
|
|
<div class="meta-row">
|
|
<span>{{ order.id }}</span>
|
|
<StatusTag :status="order.orderStatus" />
|
|
</div>
|
|
<h2 class="card-title">{{ order.commodityName }}</h2>
|
|
<div class="meta-row">
|
|
<span>{{ order.visitorName }} · {{ order.contactPhone }}</span>
|
|
<strong>¥{{ order.payAmt }}</strong>
|
|
</div>
|
|
<div class="meta-row">
|
|
<span>{{ commodityTypeText[order.orderType] }} x {{ order.commodityAmount }}</span>
|
|
<span>{{ order.reservationDate || order.createTime }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</RouterLink>
|
|
</div>
|
|
<div v-else-if="!listLoading" class="empty-state">
|
|
<ClipboardList :size="30" />
|
|
<div>暂无订单</div>
|
|
</div>
|
|
</van-list>
|
|
</van-pull-refresh>
|
|
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.order-list {
|
|
padding-top: 12px;
|
|
}
|
|
</style>
|