@@ -1,5 +1,5 @@
< template >
< view class = "page" : style = "{ paddingTop: nav BarHeight }" >
< view class = "page" : style = "{ '--status-bar': status BarHeight + 'px' }" >
< CustomNavBar title = "订单管理" show -back / >
<!-- Summary stats bar -- >
@@ -106,14 +106,14 @@
< / view >
< view class = "info-right" >
< text class = "info-label" > 下单时间 < / text >
< text class = "info-value" > { { formatDate ( order . createdAt ) } } < / text >
< text class = "info-value" > { { formatDateTime ( order . createdAt ) } } < / text >
< / view >
< / view >
<!-- Paid time if available -- >
< view v-if = "order.paidAt && order.status === OrderStatus.PAID" class="info-row" >
< text class = "info-label" > 支付时间 < / text >
< text class = "info-value" > { { formatDate ( order . paidAt ) } } < / text >
< text class = "info-value" > { { formatDateTime ( order . paidAt ) } } < / text >
< / view >
< / view >
< / view >
@@ -133,19 +133,21 @@
< / template >
< script setup lang = "ts" >
import { ref , onMounted } from 'vue'
import { ref , computed , onMounted } from 'vue'
import CustomNavBar from '../../components/CustomNavBar.vue'
import { getSystemLayout } from '../../utils/system'
import { useAdminStore } from '../../stores/admin'
import { formatPrice , formatDate } from '../../utils/format'
import { formatPrice , formatDateTime } from '../../utils/format'
import { OrderStatus } from '@mp-pilates/shared'
import type { OrderWithDetails } from '@mp-pilates/shared'
const adminStore = useAdminStore ( )
const navBarHeight = ref ( '64px' )
// 动态计算顶部模块高度
const statusBarHeight = ref ( 0 )
onMounted ( ( ) => {
navBarHeight . value = ` ${ getSystemLayout ( ) . navBarHeight } px `
const windowInfo = uni . getWindowInfo ( )
statusBarHeight . value = windowInfo . statusBarHeight ? ? 20
} )
const filters = [
@@ -165,6 +167,21 @@ const totalCount = ref<number | null>(null)
const paidCount = ref < number | null > ( null )
const pendingCount = ref < number | null > ( null )
// 每个 tab 单独缓存数据
const orderCache : Record < string , { items : OrderWithDetails [ ] ; total : number ; page : number ; hasMore : boolean } > = { }
function getCacheKey ( filter : string ) : string {
return filter || 'all'
}
function getCachedData ( filter : string ) {
return orderCache [ getCacheKey ( filter ) ]
}
function setCachedData ( filter : string , data : { items : OrderWithDetails [ ] ; total : number ; page : number ; hasMore : boolean } ) {
orderCache [ getCacheKey ( filter ) ] = data
}
const LIMIT = 20
function statusLabel ( s : string ) {
@@ -191,23 +208,45 @@ function statusAccentClass(s: string) {
}
async function loadOrders ( reset = false ) {
const filter = activeFilter . value
// 如果有缓存且是重置( 切换tab) , 直接用缓存数据
if ( reset ) {
const cached = getCachedData ( filter )
if ( cached ) {
orders . value = [ ... cached . items ]
hasMore . value = cached . hasMore
page . value = cached . page
return
}
}
// 初始加载或下拉刷新,需要请求接口
if ( loading . value ) return
if ( reset ) page . value = 1
loading . value = true
try {
const params : { page : number ; limit : number ; status ? : string } = {
page : page . value ,
limit : LIMIT ,
}
if ( activeFilter . value ) params . status = activeFilter . value
if ( filter ) params . status = filter
const result = await adminStore . fetchAdminOrders ( params )
if ( reset ) {
orders . value = [ ... result . items ]
} else {
orders . value . push ( ... result . items )
}
hasMore . value = orders . value . length < result . total
totalCount . value = result . total
const newItems = reset ? [ ... result . items ] : [ ... orders . value , ... result . items ]
const newHasMore = newItems . length < result . total
// 缓存数据
setCachedData ( filter , {
items : newItems ,
total : result . total ,
page : page . value ,
hasMore : newHasMore ,
} )
orders . value = newItems
hasMore . value = newHasMore
} catch {
uni . showToast ( { title : '加载失败' , icon : 'none' } )
} finally {
@@ -216,30 +255,79 @@ async function loadOrders(reset = false) {
}
}
async function loadSummaryCounts ( ) {
// 初始加载所有分类的数据
async function loadAllFiltersData ( ) {
loading . value = true
try {
const [ allResult , paidResult , pendingResult ] = await Promise . all ( [
adminStore . fetchAdminOrders ( { page : 1 , limit : 1 } ) ,
adminStore . fetchAdminOrders ( { page : 1 , limit : 1 , status : OrderStatus . PAID } ) ,
adminStore . fetchAdminOrders ( { page : 1 , limit : 1 , status : OrderStatus . PENDING } ) ,
// 并行请求所有分类(第一页数据)
const [ allResult , paidResult , pendingResult , refundedResult ] = await Promise . all ( [
adminStore . fetchAdminOrders ( { page : 1 , limit : LIMIT } ) ,
adminStore . fetchAdminOrders ( { page : 1 , limit : LIMIT , status : OrderStatus . PAID } ) ,
adminStore . fetchAdminOrders ( { page : 1 , limit : LIMIT , status : OrderStatus . PENDING } ) ,
adminStore . fetchAdminOrders ( { page : 1 , limit : LIMIT , status : OrderStatus . REFUNDED } ) ,
] )
// 缓存全部
setCachedData ( '' , {
items : [ ... allResult . items ] ,
total : allResult . total ,
page : 1 ,
hasMore : allResult . items . length < allResult . total ,
} )
totalCount . value = allResult . total
// 缓存已支付
setCachedData ( OrderStatus . PAID , {
items : [ ... paidResult . items ] ,
total : paidResult . total ,
page : 1 ,
hasMore : paidResult . items . length < paidResult . total ,
} )
paidCount . value = paidResult . total
// 缓存待支付
setCachedData ( OrderStatus . PENDING , {
items : [ ... pendingResult . items ] ,
total : pendingResult . total ,
page : 1 ,
hasMore : pendingResult . items . length < pendingResult . total ,
} )
pendingCount . value = pendingResult . total
// 缓存已退款
setCachedData ( OrderStatus . REFUNDED , {
items : [ ... refundedResult . items ] ,
total : refundedResult . total ,
page : 1 ,
hasMore : refundedResult . items . length < refundedResult . total ,
} )
// 设置当前 tab 的数据
orders . value = [ ... allResult . items ]
hasMore . value = allResult . items . length < allResult . total
} catch {
// non-critical, ignore
uni . showToast ( { title : '加载失败' , icon : 'none' } )
} finally {
loading . value = false
refreshing . value = false
}
}
function selectFilter ( value : string ) {
activeFilter . value = value
totalCount . value = null
loadOrders ( tr ue )
// 切换 tab 直接从缓存读取
const cached = getCachedData ( val ue)
if ( cached ) {
orders . value = [ ... cached . items ]
hasMore . value = cached . hasMore
page . value = cached . page
}
}
async function onRefresh ( ) {
refreshing . value = true
await Promise . all ( [ loadOrders ( true ) , loadSummaryCounts ( ) ] )
// 下拉刷新重新请求所有分类的数据
await loadAllFiltersData ( )
}
function loadMore ( ) {
@@ -249,8 +337,7 @@ function loadMore() {
}
onMounted ( ( ) => {
loadOrders ( true )
loadSummaryCounts ( )
loadAllFiltersData ( )
} )
< / script >
@@ -266,13 +353,18 @@ onMounted(() => {
/* ── Stats bar ──────────────────────────────── */
. stats - bar {
position : fixed ;
top : calc ( var ( -- status - bar ) + 44 px ) ;
left : 0 ;
right : 0 ;
display : flex ;
align - items : center ;
height : 96 rpx ;
background : # FFFFFF ;
padding : 28 rpx 0;
margin : 0 ;
padding : 0 ;
border - bottom : 1 rpx solid rgba ( 180 , 160 , 130 , 0.2 ) ;
box - shadow : 0 2 rpx 12 rpx rgba ( 180 , 160 , 130 , 0.08 ) ;
z - index : 100 ;
}
. stat - item {
@@ -309,9 +401,13 @@ onMounted(() => {
/* ── Filter pills ───────────────────────────── */
. filter - wrap {
position : fixed ;
top : calc ( var ( -- status - bar ) + 92 px ) ;
left : 0 ;
right : 0 ;
background : # FAF8F5 ;
border - bottom : 1 rpx solid rgba ( 180 , 160 , 130 , 0.15 ) ;
flex - shrink : 0 ;
z - index : 99 ;
}
. filter - scroll { overflow : hidden ; }
@@ -364,7 +460,11 @@ onMounted(() => {
/* ── List ───────────────────────────────────── */
. list - scroll {
flex : 1 ;
position : fixed ;
top : calc ( var ( -- status - bar ) + 144 px ) ;
left : 0 ;
right : 0 ;
bottom : 0 ;
overflow : hidden ;
}