<template>
|
<view class="page">
|
<view class="header">
|
<view class="status-safe-space"></view>
|
<view class="title-row"><text class="page-title">订单管理</text></view>
|
<view class="category-tabs">
|
<view class="category-tab" :class="activeCategory == 'group' ? 'active' : ''" @tap="selectCategory('group')"><text>团购</text></view>
|
<view class="category-tab" :class="activeCategory == 'store' ? 'active' : ''" @tap="selectCategory('store')"><text>到店消费</text></view>
|
<view class="category-tab" :class="activeCategory == 'stay' ? 'active' : ''" @tap="selectCategory('stay')"><text>免费入住</text></view>
|
</view>
|
<view class="filter-bar">
|
<view v-if="activeCategory != 'stay'" class="filter-trigger" @tap="toggleFilter"><text class="filter-text">{{statusFilterText}}</text><image class="chevron" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view>
|
<view class="filter-trigger" @tap="showDateFilter"><text class="filter-text">{{dateFilterText}}</text><image class="chevron" src="/static/icons/lucide/chevron-down.svg" mode="aspectFit"></image></view>
|
</view>
|
</view>
|
|
<scroll-view class="order-scroll" scroll-y="true" :show-scrollbar="false" :enhanced="false" lower-threshold="80" @scrolltolower="loadMoreOrders">
|
<page-feedback :state="listLoadState" loading-text="正在加载订单…" :empty-text="'暂无' + categoryName + '订单'" :error-text="listErrorText" :trace-id="listTraceId" @retry="retryListLoad"></page-feedback>
|
|
<view v-if="listLoadState == 'success'" class="order-list">
|
<view v-for="order in orders" :key="order.id" class="order-card" :class="order.category == 'stay' ? 'stay-order-card' : (order.category == 'store' ? 'store-order-card' : '')" @tap="openOrderItem(order)">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">{{order.customerName}}</text><text class="customer-phone">{{order.customerMobile}}</text></view><text class="status" :class="order.status == 'completed' ? 'verified' : ((order.status == 'cancelled' || order.status == 'refunded') ? 'closed' : 'pending')">{{order.statusText}}</text></view>
|
<text v-if="order.category != 'stay'" class="order-name">{{order.title}}</text>
|
<view v-if="order.category == 'stay'" class="stay-title-row"><text class="stay-order-name">{{order.title}}</text><text class="card-tag">{{order.cardLevel}}</text></view>
|
<view v-else class="price-row" :class="order.category == 'store' ? 'store-price-row' : ''"><view class="price-group"><text class="price">¥{{formatAmount(order.payAmount)}}</text><text v-if="order.orderAmount > order.payAmount" class="original-price">¥{{formatAmount(order.orderAmount)}}</text></view><text class="card-tag">{{order.cardLevel}}</text></view>
|
<text class="time-text">{{order.status == 'completed' && order.verifyTime.length > 0 ? '核销时间:' + formatDateTime(order.verifyTime) : '下单时间:' + formatDateTime(order.createdAt)}}</text>
|
</view>
|
</view>
|
|
<view v-if="false" class="order-list">
|
<view v-if="statusFilter == 'all' || statusFilter == pendingStatus" class="order-card" @tap="openOrder('pending')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">刘先生</text><text class="customer-phone">183***4568</text></view><text class="status pending">待核销</text></view>
|
<text class="order-name">【川味盛宴】川菜12人餐</text>
|
<view class="price-row"><view class="price-group"><text class="price">¥256.00</text><text class="original-price">¥256.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">下单时间: 2026-05-21至2026-05-22</text>
|
</view>
|
|
<view v-if="statusFilter == 'all' || statusFilter == 'completed'" class="order-card" @tap="openOrder('verified')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">刘先生</text><text class="customer-phone">183***4568</text></view><text class="status verified">已完成</text></view>
|
<text class="order-name">【川味盛宴】川菜12人餐</text>
|
<view class="price-row"><view class="price-group"><text class="price">¥256.00</text><text class="original-price">¥256.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-05-21至2026-05-22</text>
|
</view>
|
|
<view v-if="statusFilter == 'all' || statusFilter == pendingStatus" class="order-card" @tap="openOrder('pending-2')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">张女士</text><text class="customer-phone">186***2096</text></view><text class="status pending">待核销</text></view>
|
<text class="order-name">【江南小馆】双人精选套餐</text>
|
<view class="price-row"><view class="price-group"><text class="price">¥128.00</text><text class="original-price">¥158.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">下单时间: 2026-06-02至2026-06-03</text>
|
</view>
|
|
<view v-if="currentPage >= 2 && (statusFilter == 'all' || statusFilter == 'completed')" class="order-card" @tap="openOrder('verified-2')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">王先生</text><text class="customer-phone">135***8831</text></view><text class="status verified">已完成</text></view>
|
<text class="order-name">【悦宴餐厅】特色四人餐</text>
|
<view class="price-row"><view class="price-group"><text class="price">¥328.00</text><text class="original-price">¥398.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-06-12至2026-06-13</text>
|
</view>
|
</view>
|
<view v-else-if="false" class="order-list">
|
<view v-if="statusFilter == 'all' || statusFilter == pendingStatus" class="order-card store-order-card" @tap="openOrder('store-pending')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">刘先生</text><text class="customer-phone">183***4568</text></view><text class="status pending">待支付</text></view>
|
<view class="price-row store-price-row"><view class="price-group"><text class="price">¥256.00</text><text class="original-price">¥256.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">下单时间: 2026-05-21至2026-05-22</text>
|
</view>
|
|
<view v-if="statusFilter == 'all' || statusFilter == 'completed'" class="order-card store-order-card" @tap="openOrder('store-verified')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">刘先生</text><text class="customer-phone">183***4568</text></view><text class="status verified">已完成</text></view>
|
<view class="price-row store-price-row"><view class="price-group"><text class="price">¥256.00</text><text class="original-price">¥256.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-05-21至2026-05-22</text>
|
</view>
|
|
<view v-if="statusFilter == 'all' || statusFilter == pendingStatus" class="order-card store-order-card" @tap="openOrder('store-pending-2')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">李女士</text><text class="customer-phone">188***7620</text></view><text class="status pending">待支付</text></view>
|
<view class="price-row store-price-row"><view class="price-group"><text class="price">¥89.00</text><text class="original-price">¥108.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">下单时间: 2026-06-05至2026-06-05</text>
|
</view>
|
|
<view v-if="currentPage >= 2 && (statusFilter == 'all' || statusFilter == 'completed')" class="order-card store-order-card" @tap="openOrder('store-verified-2')">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">陈先生</text><text class="customer-phone">139***4512</text></view><text class="status verified">已完成</text></view>
|
<view class="price-row store-price-row"><view class="price-group"><text class="price">¥168.00</text><text class="original-price">¥198.00</text></view><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-06-18至2026-06-18</text>
|
</view>
|
</view>
|
<view v-else-if="false" class="order-list">
|
<view class="order-card stay-order-card">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">刘先生</text><text class="customer-phone">183***4568</text></view><text class="status verified">已完成</text></view>
|
<view class="stay-title-row"><text class="stay-order-name">1天免费入住</text><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-05-21至2026-05-22</text>
|
</view>
|
|
<view class="order-card stay-order-card">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">张女士</text><text class="customer-phone">186***2096</text></view><text class="status verified">已完成</text></view>
|
<view class="stay-title-row"><text class="stay-order-name">2天免费入住</text><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-06-03至2026-06-05</text>
|
</view>
|
|
<view class="order-card stay-order-card">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">王先生</text><text class="customer-phone">135***8831</text></view><text class="status verified">已完成</text></view>
|
<view class="stay-title-row"><text class="stay-order-name">1天免费入住</text><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-06-16至2026-06-17</text>
|
</view>
|
|
<view v-if="currentPage >= 2" class="order-card stay-order-card">
|
<view class="customer-row"><view class="customer-info"><image class="customer-icon" src="/static/icons/lucide/circle-user-round.svg" mode="aspectFit"></image><text class="customer-name">陈女士</text><text class="customer-phone">137***6678</text></view><text class="status verified">已完成</text></view>
|
<view class="stay-title-row"><text class="stay-order-name">3天免费入住</text><text class="card-tag">卓越人才卡</text></view>
|
<text class="time-text">核销时间: 2026-06-22至2026-06-25</text>
|
</view>
|
</view>
|
<view v-if="listLoadState == 'success'" class="load-more-state" @tap="retryLoadMore">
|
<text v-if="loadMoreState == 'loading'" class="load-more-text">正在加载更多…</text>
|
<text v-else-if="loadMoreState == 'error'" class="load-more-text load-more-retry">加载失败,点击重试</text>
|
<text v-else-if="loadMoreState == 'no-more'" class="load-more-text">没有更多订单了</text>
|
<text v-else class="load-more-text">上滑加载更多</text>
|
</view>
|
</scroll-view>
|
|
<view class="merchant-tabbar-host"><merchant-tabbar active="orders"></merchant-tabbar></view>
|
|
<view v-if="filterOpen" class="status-mask" @tap="closeStatusFilter"></view>
|
<view v-if="filterOpen" class="status-sheet">
|
<view class="status-sheet-head"><text class="status-sheet-title">订单状态</text><image class="close-icon" src="/static/icons/lucide/x.svg" mode="aspectFit" @tap="closeStatusFilter"></image></view>
|
<view class="status-options">
|
<view class="status-option" :class="statusFilter == 'all' ? 'selected' : ''" @tap="selectStatus('all')"><text class="status-option-text">全部</text><text v-if="statusFilter == 'all'" class="status-selected-mark">已选</text></view>
|
<view class="status-option" :class="statusFilter == pendingStatus ? 'selected' : ''" @tap="selectStatus(pendingStatus)"><text class="status-option-text">{{pendingStatusText}}</text><text v-if="statusFilter == pendingStatus" class="status-selected-mark">已选</text></view>
|
<view class="status-option" :class="statusFilter == 'completed' ? 'selected' : ''" @tap="selectStatus('completed')"><text class="status-option-text">已完成</text><text v-if="statusFilter == 'completed'" class="status-selected-mark">已选</text></view>
|
<view class="status-option" :class="statusFilter == 'closed' ? 'selected' : ''" @tap="selectStatus('closed')"><text class="status-option-text">已取消/已退款</text><text v-if="statusFilter == 'closed'" class="status-selected-mark">已选</text></view>
|
</view>
|
<view class="status-cancel" @tap="closeStatusFilter"><text class="status-cancel-text">取消</text></view>
|
</view>
|
|
<view v-if="datePickerVisible" class="date-mask" @tap="closeDateFilter"></view>
|
<view v-if="datePickerVisible" class="date-sheet">
|
<view class="date-sheet-head"><text class="date-sheet-title">选择日期区间</text><image class="close-icon" src="/static/icons/lucide/x.svg" mode="aspectFit" @tap="closeDateFilter"></image></view>
|
<view class="date-fields">
|
<view class="date-field" :class="activeDateField == 'start' ? 'active' : ''" @tap="selectDateField('start')"><text class="date-field-label">起始日期</text><text class="date-field-value" :class="startDate == datePlaceholder ? 'placeholder' : ''">{{startDate}}</text></view>
|
<view class="date-field" :class="activeDateField == 'end' ? 'active' : ''" @tap="selectDateField('end')"><text class="date-field-label">截止日期</text><text class="date-field-value" :class="endDate == datePlaceholder ? 'placeholder' : ''">{{endDate}}</text></view>
|
</view>
|
<picker-view class="date-picker" :value="pickerIndexes" :indicator-style="pickerIndicatorStyle" :mask-style="pickerMaskStyle" @change="onPickerChange">
|
<picker-view-column><view v-for="(year, index) in years" :key="year" class="picker-item" :class="pickerIndexes[0] == index ? 'selected' : ''">{{year}}</view></picker-view-column>
|
<picker-view-column><view v-for="(month, index) in months" :key="month" class="picker-item" :class="pickerIndexes[1] == index ? 'selected' : ''">{{month}}</view></picker-view-column>
|
<picker-view-column><view v-for="(day, index) in days" :key="day" class="picker-item" :class="pickerIndexes[2] == index ? 'selected' : ''">{{day}}</view></picker-view-column>
|
</picker-view>
|
<view class="date-actions"><view class="action-button reset-button" @tap="resetDateFilter"><text class="reset-text">重置</text></view><view class="action-button confirm-button" @tap="confirmDateFilter"><text class="confirm-text">确认</text></view></view>
|
</view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { getOrderStatusText, getPendingStatusByCategory } from '../../config/order.uts'
|
import { loadMerchantBusinessDate, loadMerchantOrders, MerchantOrder, MerchantOrderPage, MerchantOrderQuery } from '../../services/orders.uts'
|
import { hasActiveSession } from '../../services/session.uts'
|
|
const activeCategory = ref('group')
|
const statusFilter = ref('all')
|
const statusFilterText = ref('全部')
|
const pendingStatus = ref('pending_verification')
|
const pendingStatusText = ref('待核销')
|
const filterOpen = ref(false)
|
const categoryName = ref('团购')
|
const listLoadState = ref('loading')
|
const listErrorText = ref('加载失败,点击重试')
|
const listTraceId = ref('')
|
const loadMoreState = ref('more')
|
const currentPage = ref(1)
|
const loadMoreStep = ref(0)
|
const orders = ref<Array<MerchantOrder>>([])
|
const dateFilterApplied = ref(false)
|
const pageSize = 3
|
let cancelOrderRequest: (() => void) | null = null
|
const datePickerVisible = ref(false)
|
const activeDateField = ref('start')
|
const datePlaceholder = '一年一月一日'
|
const allMonths = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
|
const allDays = ['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日']
|
|
const deviceDate = new Date()
|
const currentYear = ref(deviceDate.getFullYear())
|
const currentMonth = ref(deviceDate.getMonth() + 1)
|
const currentDay = ref(deviceDate.getDate())
|
const buildYears = (fromYear: number): Array<string> => {
|
const values: Array<string> = []
|
for (let year = fromYear; year <= 2099; year++) values.push(year.toString() + '年')
|
return values
|
}
|
const years = ref(buildYears(currentYear.value - 2))
|
const months = ref(allMonths.slice())
|
const days = ref(allDays.slice(0, new Date(currentYear.value, currentMonth.value, 0).getDate()))
|
const defaultDate = ref(currentYear.value.toString() + '年' + currentMonth.value.toString() + '月' + currentDay.value.toString() + '日')
|
const defaultDateValue = ref(currentYear.value * 10000 + currentMonth.value * 100 + currentDay.value)
|
const startDate = ref(datePlaceholder)
|
const endDate = ref(datePlaceholder)
|
const startDateValue = ref(0)
|
const endDateValue = ref(0)
|
const dateFilterText = ref('时间筛选')
|
const pickerIndexes = ref([2, currentMonth.value - 1, currentDay.value - 1])
|
const pickerIndicatorStyle = 'height: 39px; background-color: rgba(255, 241, 228, 0.22); border-top: 1px solid #FFE1C8; border-bottom: 1px solid #FFE1C8;'
|
const pickerMaskStyle = 'background: transparent;'
|
|
const toIsoDate = (value: number): string => {
|
if (value <= 0) return ''
|
const year = Math.floor(value / 10000)
|
const month = Math.floor(value / 100) % 100
|
const day = value % 100
|
return year.toString() + '-' + (month < 10 ? '0' : '') + month.toString() + '-' + (day < 10 ? '0' : '') + day.toString()
|
}
|
|
const applyBusinessDate = (value: string) => {
|
const parts = value.split('-')
|
if (parts.length != 3) return
|
const year = Number(parts[0]); const month = Number(parts[1]); const day = Number(parts[2])
|
if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day) || month < 1 || month > 12 || day < 1 || day > 31) return
|
currentYear.value = year; currentMonth.value = month; currentDay.value = day
|
years.value = buildYears(year - 2)
|
months.value = allMonths.slice(); days.value = allDays.slice(0, new Date(year, month, 0).getDate())
|
defaultDate.value = year.toString() + '年' + month.toString() + '月' + day.toString() + '日'
|
defaultDateValue.value = year * 10000 + month * 100 + day
|
if (!dateFilterApplied.value) { startDate.value = datePlaceholder; startDateValue.value = 0; endDate.value = datePlaceholder; endDateValue.value = 0; pickerIndexes.value = [2, month - 1, day - 1] }
|
}
|
|
const buildOrderQuery = (page: number): MerchantOrderQuery => ({
|
page: page,
|
size: pageSize,
|
category: activeCategory.value,
|
status: activeCategory.value == 'stay' ? 'all' : statusFilter.value,
|
startDate: dateFilterApplied.value ? toIsoDate(startDateValue.value) : '',
|
endDate: dateFilterApplied.value ? toIsoDate(endDateValue.value) : ''
|
})
|
|
const applyOrderPage = (result: MerchantOrderPage, append: boolean) => {
|
const nextOrders = append ? orders.value.concat(result.records) : result.records
|
orders.value = nextOrders
|
currentPage.value = result.page
|
listTraceId.value = ''
|
if (nextOrders.length == 0) {
|
listLoadState.value = 'empty'
|
loadMoreState.value = 'no-more'
|
return
|
}
|
listLoadState.value = 'success'
|
loadMoreState.value = result.page * result.size >= result.total ? 'no-more' : 'more'
|
}
|
|
const showOrderError = (message: string, traceId: string, append: boolean) => {
|
if (append && orders.value.length > 0) {
|
loadMoreState.value = 'error'
|
listTraceId.value = traceId
|
return
|
}
|
listLoadState.value = 'error'
|
listErrorText.value = message
|
listTraceId.value = traceId
|
}
|
|
const requestOrders = (page: number, append: boolean) => {
|
if (cancelOrderRequest != null) {
|
const cancel = cancelOrderRequest
|
cancelOrderRequest = null
|
cancel()
|
}
|
if (!append) {
|
orders.value = []
|
listLoadState.value = 'loading'
|
loadMoreState.value = 'more'
|
} else {
|
loadMoreState.value = 'loading'
|
}
|
const query = buildOrderQuery(page)
|
if (!hasActiveSession()) {
|
showOrderError('请登录后加载真实订单', '', append)
|
return
|
}
|
cancelOrderRequest = loadMerchantOrders(query, (result: MerchantOrderPage, _traceId: string) => {
|
cancelOrderRequest = null
|
applyOrderPage(result, append)
|
}, (error: any) => {
|
cancelOrderRequest = null
|
showOrderError(error.message != null ? error.message : '订单加载失败,点击重试', error.traceId != null ? error.traceId : '', append)
|
})
|
}
|
|
const reloadOrders = () => requestOrders(1, false)
|
|
const formatAmount = (amount: number): string => (amount / 100).toFixed(2)
|
const formatDateTime = (value: string): string => value.length > 0 ? value.replace('T', ' ') : '—'
|
|
const openOrderItem = (order: MerchantOrder) => {
|
const query = 'id=' + encodeURIComponent(order.id) + '&orderNo=' + encodeURIComponent(order.orderNo)
|
if (order.status == 'pending_verification') {
|
uni.navigateTo({ url: '/pages/verification-detail/verification-detail?' + query })
|
return
|
}
|
// 已取消/已退款的订单进详情看状态,不能按「已完成」打开,否则会把取消说成完成。
|
if (order.status == 'cancelled' || order.status == 'refunded') {
|
uni.navigateTo({ url: '/pages/order-detail/order-detail?status=' + order.status + '&' + query })
|
return
|
}
|
uni.navigateTo({ url: '/pages/order-detail/order-detail?status=completed&' + query })
|
}
|
|
const selectCategory = (category: string) => {
|
activeCategory.value = category
|
statusFilter.value = 'all'
|
statusFilterText.value = '全部'
|
pendingStatus.value = getPendingStatusByCategory(category)
|
pendingStatusText.value = getOrderStatusText(pendingStatus.value)
|
filterOpen.value = false
|
listLoadState.value = 'success'
|
loadMoreState.value = 'more'
|
currentPage.value = 1
|
loadMoreStep.value = 0
|
if (category == 'group') {
|
categoryName.value = '团购'
|
} else if (category == 'store') {
|
categoryName.value = '到店消费'
|
} else {
|
categoryName.value = '免费入住'
|
}
|
reloadOrders()
|
}
|
|
const loadMoreDemoOrders = () => {
|
if (listLoadState.value != 'success' || loadMoreState.value != 'more') {
|
return
|
}
|
loadMoreState.value = 'loading'
|
setTimeout(() => {
|
if (loadMoreStep.value == 0) {
|
currentPage.value = 2
|
loadMoreStep.value = 1
|
loadMoreState.value = 'more'
|
} else if (loadMoreStep.value == 1) {
|
loadMoreStep.value = 2
|
loadMoreState.value = 'error'
|
} else {
|
loadMoreState.value = 'no-more'
|
}
|
}, 650)
|
}
|
|
const retryDemoLoadMore = () => {
|
if (loadMoreState.value == 'error') {
|
loadMoreState.value = 'more'
|
loadMoreDemoOrders()
|
}
|
}
|
|
const retryDemoListLoad = () => {
|
listLoadState.value = 'loading'
|
setTimeout(() => {
|
listLoadState.value = 'success'
|
}, 500)
|
}
|
|
const loadMoreOrders = () => {
|
if (listLoadState.value != 'success' || loadMoreState.value != 'more') return
|
requestOrders(currentPage.value + 1, true)
|
}
|
|
const retryLoadMore = () => {
|
if (loadMoreState.value == 'error') requestOrders(currentPage.value + 1, true)
|
}
|
|
const retryListLoad = () => reloadOrders()
|
|
const toggleFilter = () => {
|
filterOpen.value = !filterOpen.value
|
}
|
|
const closeStatusFilter = () => {
|
filterOpen.value = false
|
}
|
|
const selectStatus = (status: string) => {
|
statusFilter.value = status
|
statusFilterText.value = getOrderStatusText(status)
|
filterOpen.value = false
|
reloadOrders()
|
}
|
|
const showDateFilter = () => {
|
filterOpen.value = false
|
datePickerVisible.value = true
|
}
|
|
const closeDateFilter = () => {
|
datePickerVisible.value = false
|
}
|
|
const selectDateField = (field: string) => {
|
activeDateField.value = field
|
if (field == 'start') {
|
setPickerDate(startDateValue.value > 0 ? startDateValue.value : defaultDateValue.value)
|
return
|
}
|
// 不再把起始日期抄进截止日期:现在支持开区间,抄一次会把"起始日期之后"悄悄变成"只有这一天"。
|
setPickerDate(endDateValue.value > 0 ? endDateValue.value : (startDateValue.value > 0 ? startDateValue.value : defaultDateValue.value))
|
}
|
|
const getMaxDay = (year: number, month: number) => {
|
if (month == 2) {
|
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) ? 29 : 28
|
}
|
return month == 4 || month == 6 || month == 9 || month == 11 ? 30 : 31
|
}
|
|
const setPickerDate = (dateValue: number) => {
|
// dateValue 为 0 表示这一头还没选过(占位符状态)。以前直接按 0 算,滚轮会跳到"0 年 0 月 0 日"
|
// 这种不存在的列上,表现为"点进来滚轮是空的"。统一回落到业务日期。
|
const value = dateValue > 0 ? dateValue : defaultDateValue.value
|
const year = Math.floor(value / 10000)
|
const month = Math.floor(value / 100) % 100
|
const day = value % 100
|
const yearIndex = year - (currentYear.value - 2)
|
months.value = allMonths.slice()
|
days.value = allDays.slice(0, getMaxDay(year, month))
|
pickerIndexes.value = [yearIndex, month - 1, day - 1]
|
}
|
|
const onPickerChange = (event: any) => {
|
const indexes = event.detail.value as Array<number>
|
const yearIndex = indexes[0]
|
let monthIndex = indexes[1]
|
let dayIndex = indexes[2]
|
const selectedYear = currentYear.value - 2 + yearIndex
|
months.value = allMonths.slice()
|
if (monthIndex >= months.value.length) {
|
monthIndex = months.value.length - 1
|
}
|
const selectedMonth = monthIndex + 1
|
days.value = allDays.slice(0, getMaxDay(selectedYear, selectedMonth))
|
if (dayIndex >= days.value.length) {
|
dayIndex = days.value.length - 1
|
}
|
const selectedDateValue = selectedYear * 10000 + selectedMonth * 100 + dayIndex + 1
|
if (activeDateField.value == 'start' && endDateValue.value > 0 && selectedDateValue > endDateValue.value) {
|
uni.showToast({ title: '起始日不能晚于截止日', icon: 'none' })
|
setPickerDate(startDateValue.value)
|
return
|
}
|
if (activeDateField.value == 'end' && selectedDateValue < startDateValue.value) {
|
uni.showToast({ title: '截止日不能早于起始日', icon: 'none' })
|
setPickerDate(endDateValue.value > 0 ? endDateValue.value : startDateValue.value)
|
return
|
}
|
pickerIndexes.value = [yearIndex, monthIndex, dayIndex]
|
const selectedDate = years.value[yearIndex] + months.value[monthIndex] + days.value[dayIndex]
|
if (activeDateField.value == 'start') {
|
startDate.value = selectedDate
|
startDateValue.value = selectedDateValue
|
} else {
|
endDate.value = selectedDate
|
endDateValue.value = selectedDateValue
|
}
|
}
|
|
const resetDateFilter = () => {
|
activeDateField.value = 'start'
|
startDate.value = datePlaceholder
|
endDate.value = datePlaceholder
|
startDateValue.value = 0
|
endDateValue.value = 0
|
months.value = allMonths.slice()
|
days.value = allDays.slice(0, getMaxDay(currentYear.value, currentMonth.value))
|
pickerIndexes.value = [2, currentMonth.value - 1, currentDay.value - 1]
|
dateFilterText.value = '时间筛选'
|
dateFilterApplied.value = false
|
}
|
|
const confirmDateFilter = () => {
|
// 开区间:只选一头也算有效筛选(后端 startDate / endDate 各自可空)。
|
// 以前两头都必须选,且不管缺哪一头都提示"请选择截止日期":只选了截止日的人会以为
|
// 系统没认下他选的那天,反复点确认也过不去。
|
if (startDateValue.value == 0 && endDateValue.value == 0) {
|
selectDateField('start')
|
uni.showToast({ title: '请选择起始日期或截止日期', icon: 'none' })
|
return
|
}
|
if (startDateValue.value > 0 && endDateValue.value > 0 && startDateValue.value > endDateValue.value) {
|
uni.showToast({ title: '起始日不能晚于截止日', icon: 'none' })
|
return
|
}
|
datePickerVisible.value = false
|
dateFilterText.value = '已选日期'
|
dateFilterApplied.value = true
|
reloadOrders()
|
uni.showToast({ title: '日期区间已应用', icon: 'none' })
|
}
|
|
const openOrder = (orderId: string) => {
|
if (orderId == 'pending' || orderId == 'pending-2') {
|
uni.navigateTo({ url: '/pages/verification-detail/verification-detail?id=' + orderId })
|
return
|
}
|
if (orderId == 'store-pending' || orderId == 'store-pending-2') {
|
uni.navigateTo({ url: '/pages/order-detail/order-detail?id=' + orderId })
|
return
|
}
|
if (orderId == 'verified' || orderId == 'verified-2' || orderId == 'store-verified' || orderId == 'store-verified-2') {
|
uni.navigateTo({ url: '/pages/order-detail/order-detail?status=completed&id=' + orderId })
|
return
|
}
|
uni.showToast({ title: '该演示订单不可查看详情', icon: 'none' })
|
}
|
|
onLoad(() => {
|
if (hasActiveSession()) loadMerchantBusinessDate((value: string) => applyBusinessDate(value), () => {})
|
reloadOrders()
|
})
|
|
onUnload(() => {
|
if (cancelOrderRequest != null) cancelOrderRequest()
|
})
|
|
</script>
|
|
<style>
|
.page { display: flex; flex: 1; flex-direction: column; width: 100%; height: 100vh; position: relative; overflow: hidden; background-image: linear-gradient(180deg, #FFE4C6 0%, #FFF0E1 620rpx, #FFFFFF 980rpx); }
|
.order-scroll { width: 100%; height: calc(100vh - 398rpx); flex: none; background-image: linear-gradient(180deg, #FFECD7 0%, #FFF0E1 220rpx, #FFFFFF 720rpx); }
|
.header { padding: 0 36rpx 22rpx; position: relative; }
|
.status-safe-space { height: 154rpx; }
|
.title-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
|
.page-title { color: #111A2B; font-size: 44rpx; line-height: 60rpx; font-weight: 700; }
|
.demo-control { display: flex; flex-direction: row; align-items: center; }
|
.demo-control-text { color: #A76C47; font-size: 23rpx; line-height: 34rpx; margin-right: 10rpx; font-weight: 600; }
|
.demo-switch { transform: scale(0.8); transform-origin: right center; }
|
.category-tabs { display: flex; flex-direction: row; justify-content: space-between; margin-top: 50rpx; padding: 0 20rpx; }
|
.category-tab { display: flex; height: 52rpx; align-items: flex-start; justify-content: center; color: #8390A4; font-size: 30rpx; line-height: 42rpx; font-weight: 600; position: relative; }
|
.category-tab.active { color: #202B3E; font-weight: 700; }
|
.category-tab.active::after { content: ''; position: absolute; bottom: 0; left: 50%; width: 58rpx; height: 7rpx; margin-left: -29rpx; border-radius: 4rpx; background-color: #FF5A42; animation: category-indicator-in 240ms cubic-bezier(0.22, 0.8, 0.28, 1) both; }
|
.filter-bar { display: flex; flex-direction: row; justify-content: flex-end; margin-top: 26rpx; }
|
.filter-trigger { display: flex; flex-direction: row; align-items: center; min-width: 116rpx; margin-left: 24rpx; }
|
.filter-text { color: #A76C47; font-size: 25rpx; line-height: 34rpx; font-weight: 600; }
|
.chevron { width: 22rpx; height: 22rpx; margin-left: 8rpx; }
|
.order-list { padding: 0 42rpx 52rpx; overflow: visible; }
|
.order-card { display: flex; flex-direction: column; min-height: 338rpx; padding: 30rpx 34rpx 28rpx; margin-top: 22rpx; border-radius: 36rpx; background-color: #FFFFFF; box-sizing: border-box; box-shadow: 0 18rpx 34rpx rgba(70, 65, 62, 0.08); }
|
.store-order-card { min-height: 250rpx; }
|
.stay-order-card { min-height: 250rpx; }
|
.customer-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
|
.customer-info { display: flex; flex-direction: row; align-items: center; }
|
.customer-icon { width: 26rpx; height: 26rpx; margin-right: 12rpx; }
|
.customer-name { color: #697589; font-size: 26rpx; line-height: 36rpx; font-weight: 600; }
|
.customer-phone { color: #9DA8B9; font-size: 24rpx; line-height: 34rpx; margin-left: 12rpx; }
|
.status { font-size: 24rpx; line-height: 34rpx; font-weight: 600; }
|
.pending { color: #FF5A42; }
|
.verified { color: #98A3B4; }
|
.closed { color: #98A3B4; }
|
.order-name { color: #182236; font-size: 31rpx; line-height: 44rpx; font-weight: 700; margin-top: 26rpx; }
|
.price-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-top: 18rpx; }
|
.store-price-row { margin-top: 24rpx; }
|
.stay-title-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-top: 24rpx; }
|
.stay-order-name { color: #182236; font-size: 31rpx; line-height: 42rpx; font-weight: 700; }
|
.price-group { display: flex; flex-direction: row; align-items: baseline; }
|
.price { color: #162035; font-size: 31rpx; line-height: 42rpx; font-weight: 700; }
|
.original-price { color: #B4BDCA; font-size: 24rpx; line-height: 34rpx; margin-left: 20rpx; text-decoration: line-through; }
|
.card-tag { color: #FFFFFF; font-size: 22rpx; line-height: 34rpx; padding: 5rpx 16rpx; border-radius: 24rpx; background-color: #D7AC4A; }
|
.time-text { color: #8995A8; font-size: 25rpx; line-height: 36rpx; margin-top: 14rpx; }
|
.list-feedback { display: flex; min-height: 420rpx; padding: 0 42rpx; justify-content: center; align-items: center; box-sizing: border-box; }
|
.feedback-text { color: #97A1B0; font-size: 28rpx; line-height: 40rpx; }
|
.feedback-action { color: #FF7924; }
|
.load-more-state { display: block; width: 100%; min-height: 60rpx; padding: 8rpx 0 244rpx; box-sizing: border-box; }
|
.load-more-text { display: block; width: 100%; color: #A5AFBE; font-size: 24rpx; line-height: 34rpx; text-align: center; }
|
.load-more-retry { color: #FF7924; }
|
|
.status-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(27, 29, 33, 0.46); z-index: 20; animation: mask-fade-in 180ms ease-out both; }
|
.status-sheet { position: fixed; left: 0; right: 0; bottom: 0; padding: 46rpx 48rpx 52rpx; border-radius: 46rpx 46rpx 0 0; background-color: #FFFFFF; box-sizing: border-box; z-index: 30; animation: sheet-slide-up 260ms cubic-bezier(0.22, 0.8, 0.28, 1) both; }
|
.status-sheet-head { display: flex; flex-direction: row; justify-content: center; align-items: center; position: relative; }
|
.status-sheet-title { color: #2A3444; font-size: 32rpx; line-height: 46rpx; font-weight: 700; }
|
.status-options { margin-top: 38rpx; }
|
.status-option { display: flex; height: 104rpx; padding: 0 30rpx; flex-direction: row; justify-content: space-between; align-items: center; border-radius: 24rpx; box-sizing: border-box; background-color: #F7F8FA; }
|
.status-option + .status-option { margin-top: 18rpx; }
|
.status-option.selected { background-color: #FFF0E6; }
|
.status-option-text { color: #495669; font-size: 30rpx; line-height: 42rpx; font-weight: 600; }
|
.status-option.selected .status-option-text { color: #FF7924; font-weight: 700; }
|
.status-selected-mark { color: #FF7924; font-size: 24rpx; line-height: 34rpx; font-weight: 600; }
|
.status-cancel { display: flex; height: 94rpx; margin-top: 34rpx; justify-content: center; align-items: center; border-radius: 48rpx; background-color: #FFF2E6; }
|
.status-cancel-text { color: #FF8129; font-size: 30rpx; font-weight: 600; }
|
|
.date-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(27, 29, 33, 0.46); z-index: 20; animation: mask-fade-in 180ms ease-out both; }
|
.date-sheet { position: fixed; left: 0; right: 0; bottom: 0; height: 1080rpx; padding: 48rpx 48rpx 58rpx; border-radius: 46rpx 46rpx 0 0; background-color: #FFFFFF; box-sizing: border-box; z-index: 30; animation: sheet-slide-up 260ms cubic-bezier(0.22, 0.8, 0.28, 1) both; }
|
.date-sheet-head { display: flex; flex-direction: row; justify-content: center; align-items: center; position: relative; }
|
.date-sheet-title { color: #2A3444; font-size: 32rpx; line-height: 46rpx; font-weight: 700; }
|
.close-icon { position: absolute; right: 0; width: 32rpx; height: 32rpx; }
|
.date-fields { display: flex; flex-direction: row; margin-top: 58rpx; }
|
.date-field { display: flex; flex: 1; flex-direction: column; align-items: center; padding-bottom: 20rpx; position: relative; }
|
.date-field-label { color: #6E7786; font-size: 28rpx; line-height: 40rpx; font-weight: 600; }
|
.date-field-value { color: #273345; font-size: 30rpx; line-height: 44rpx; margin-top: 14rpx; }
|
.date-field.active .date-field-label, .date-field.active .date-field-value { color: #FF7924; }
|
.date-field.active::after { content: ''; position: absolute; bottom: 0; width: 68rpx; height: 5rpx; border-radius: 3rpx; background-color: #FF7924; }
|
.date-field-value.placeholder { color: #BBC3CE; }
|
.date-picker { width: 100%; height: 430rpx; margin-top: 64rpx; }
|
.picker-item { display: flex; width: 100%; height: 78rpx; justify-content: center; align-items: center; color: #9AA3B0; font-size: 30rpx; line-height: 42rpx; }
|
.picker-item.selected { color: #FF7924; font-size: 36rpx; font-weight: 700; }
|
.date-actions { display: flex; flex-direction: row; margin-top: 42rpx; }
|
.action-button { display: flex; flex: 1; height: 94rpx; justify-content: center; align-items: center; border-radius: 48rpx; }
|
.action-button + .action-button { margin-left: 24rpx; }
|
.reset-button { background-color: #FFF2E6; }
|
.confirm-button { background-image: linear-gradient(90deg, #FF982B 0%, #FF7625 100%); }
|
.reset-text { color: #FF8129; font-size: 30rpx; font-weight: 600; }
|
.confirm-text { color: #FFFFFF; font-size: 30rpx; font-weight: 700; }
|
|
@keyframes mask-fade-in {
|
from { opacity: 0; }
|
to { opacity: 1; }
|
}
|
@keyframes sheet-slide-up {
|
from { opacity: 0; transform: translateY(100%); }
|
to { opacity: 1; transform: translateY(0); }
|
}
|
@keyframes category-indicator-in {
|
from { opacity: 0.2; transform: scaleX(0.25); }
|
to { opacity: 1; transform: scaleX(1); }
|
}
|
</style>
|