234 lines
7.4 KiB
TypeScript
234 lines
7.4 KiB
TypeScript
import { request } from '../../utils/request'
|
|
import { PagedResult, OrderItem } from '../../types/models'
|
|
import { fenToYuan, formatDateTime, formatCountdown } from '../../utils/format'
|
|
import { normalizePaged, mergePaged, hasMorePaged } from '../../utils/paged'
|
|
import { navScroll } from '../../utils/nav'
|
|
import { logger } from '../../utils/logger'
|
|
|
|
// 日志模块名
|
|
const LOG = 'order'
|
|
|
|
const STATUS_FILTERS = [
|
|
{ name: '全部', value: 0 },
|
|
{ name: '待支付', value: 1 },
|
|
{ name: '已支付', value: 2 },
|
|
{ name: '已取消', value: 3 },
|
|
{ name: '已过期', value: 4 },
|
|
{ name: '已退款', value: 5 },
|
|
]
|
|
|
|
const STATUS_TEXT: Record<number, string> = { 1: '待支付', 2: '已支付', 3: '已取消', 4: '已过期', 5: '已退款' }
|
|
// 零彩色状态语义:待支付 = 实心黑;已支付/已退款 = 描边;已取消/已过期 = 降透明
|
|
const STATUS_CLS: Record<number, string> = {
|
|
1: 'pill--ink',
|
|
2: 'pill--line',
|
|
3: 'pill--dim',
|
|
4: 'pill--dim',
|
|
5: 'pill--line pill--dim',
|
|
}
|
|
|
|
Page({
|
|
data: {
|
|
filters: STATUS_FILTERS,
|
|
activeFilter: 0,
|
|
list: [] as OrderItem[],
|
|
loading: true,
|
|
loadingMore: false,
|
|
page: 1,
|
|
pageSize: 20,
|
|
hasMore: true,
|
|
// 滚动收缩:由 utils/nav.ts 的共享 handler 驱动
|
|
navScrolled: false,
|
|
// 取消订单确认
|
|
cancelVisible: false,
|
|
cancelOrderNo: '',
|
|
cancelButtons: [
|
|
{ text: '再想想', value: '' },
|
|
{ text: '确认取消', value: 'ok', type: 'main' },
|
|
],
|
|
// 退款申请(抽屉 + 原因输入)
|
|
refundVisible: false,
|
|
refundOrderNo: '',
|
|
refundReason: '',
|
|
refundSubmitting: false,
|
|
},
|
|
|
|
// scroll-view 滚动 → 导航收缩为玻璃小标题
|
|
onNavScroll: navScroll,
|
|
|
|
// 抽屉内容点击不穿透到遮罩
|
|
noop() {},
|
|
|
|
onShow() {
|
|
this.refresh()
|
|
this.startCountdown()
|
|
},
|
|
|
|
onHide() {
|
|
this.stopCountdown()
|
|
},
|
|
|
|
onUnload() {
|
|
this.stopCountdown()
|
|
},
|
|
|
|
refresh() {
|
|
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
|
|
this.fetchList(1)
|
|
},
|
|
|
|
onReachBottom() {
|
|
if (!this.data.hasMore || this.data.loading || this.data.loadingMore) return
|
|
this.fetchList(this.data.page + 1)
|
|
},
|
|
|
|
// 待支付倒计时需要每秒走动,仅在有倒计时项时才 setData
|
|
startCountdown() {
|
|
this.stopCountdown()
|
|
;(this as any).tickTimer = setInterval(() => this.tickCountdown(), 1000)
|
|
},
|
|
|
|
stopCountdown() {
|
|
const timer = (this as any).tickTimer
|
|
if (timer) {
|
|
clearInterval(timer)
|
|
;(this as any).tickTimer = null
|
|
}
|
|
},
|
|
|
|
tickCountdown() {
|
|
const list = this.data.list
|
|
if (!list.length) return
|
|
const now = Math.floor(Date.now() / 1000)
|
|
let changed = false
|
|
const next = list.map((it: any) => {
|
|
if (it.order_status !== 1 || !it.expire_time) return it
|
|
const left = it.expire_time - now
|
|
const text = left > 0 ? formatCountdown(left) : ''
|
|
if (text === it._countdown) return it
|
|
changed = true
|
|
return { ...it, _countdown: text }
|
|
})
|
|
if (changed) this.setData({ list: next })
|
|
},
|
|
|
|
fetchList(page: number) {
|
|
const params: Record<string, any> = { page, page_size: this.data.pageSize }
|
|
const filter = this.data.activeFilter
|
|
if (filter > 0) params.status = filter
|
|
|
|
// 请求序号:切换筛选时旧响应可能后到,序号不匹配直接丢弃
|
|
const seq = ((this as any).reqSeq || 0) + 1
|
|
;(this as any).reqSeq = seq
|
|
if (page > 1) this.setData({ loadingMore: true })
|
|
|
|
request<PagedResult<OrderItem>>({ url: '/order', data: params })
|
|
.then((res) => {
|
|
if ((this as any).reqSeq !== seq) return
|
|
const { list: raw, total } = normalizePaged<OrderItem>(res)
|
|
logger.debug(LOG, '订单加载成功, 筛选=', filter, 'page=', page, '条数=', raw && raw.length)
|
|
const now = Math.floor(Date.now() / 1000)
|
|
const list = raw.map((it) => ({
|
|
...it,
|
|
_statusText: STATUS_TEXT[it.order_status] || '',
|
|
_statusCls: STATUS_CLS[it.order_status] || 'pill--line',
|
|
_amount: fenToYuan(it.pay_amount),
|
|
_time: formatDateTime(it.created_at),
|
|
_orderTail: `…${it.order_no.slice(-4)}`,
|
|
_countdown: it.order_status === 1 && it.expire_time > now ? formatCountdown(it.expire_time - now) : '',
|
|
_benefit: it.member_days > 0 ? `${it.member_days} 天会员` : '',
|
|
_canCancel: it.order_status === 1 && it.pay_channel !== 2,
|
|
_payChannelText: it.pay_channel === 2 ? '虚拟支付' : '',
|
|
}))
|
|
const merged = mergePaged(this.data.list, list, page === 1)
|
|
this.setData({
|
|
list: merged,
|
|
hasMore: hasMorePaged(merged, total, this.data.pageSize),
|
|
loading: false,
|
|
loadingMore: false,
|
|
page,
|
|
})
|
|
})
|
|
.catch((e: any) => {
|
|
if ((this as any).reqSeq !== seq) return
|
|
logger.warn(LOG, '订单加载失败, page=', page, 'code=', e && e.code)
|
|
this.setData({ loading: false, loadingMore: false })
|
|
})
|
|
},
|
|
|
|
onFilterTap(e: WechatMiniprogram.TouchEvent) {
|
|
const index = Number(e.currentTarget.dataset.index)
|
|
if (index === this.data.activeFilter) return
|
|
this.setData({ activeFilter: index, list: [], loading: true })
|
|
this.refresh()
|
|
},
|
|
|
|
onCancel(e: WechatMiniprogram.TouchEvent) {
|
|
const orderNo = e.currentTarget.dataset.no as string
|
|
this.setData({ cancelVisible: true, cancelOrderNo: orderNo })
|
|
},
|
|
|
|
onCancelCancel() {
|
|
this.setData({ cancelVisible: false })
|
|
},
|
|
|
|
onCancelConfirm(ev: WechatMiniprogram.CustomEvent) {
|
|
const orderNo = this.data.cancelOrderNo
|
|
this.setData({ cancelVisible: false })
|
|
if (ev.detail.value !== 'ok' || !orderNo) return
|
|
request({ url: `/order/${orderNo}/cancel`, method: 'POST' })
|
|
.then(() => {
|
|
logger.info(LOG, '取消订单成功, orderNo=', orderNo)
|
|
wx.showToast({ title: '已取消', icon: 'success' })
|
|
this.refresh()
|
|
})
|
|
.catch((err: any) => logger.warn(LOG, '取消订单失败, orderNo=', orderNo, 'code=', err && err.code))
|
|
},
|
|
|
|
onPay() {
|
|
wx.showToast({ title: '请返回商城重新下单支付', icon: 'none' })
|
|
},
|
|
|
|
onRefund(e: WechatMiniprogram.TouchEvent) {
|
|
const orderNo = e.currentTarget.dataset.no as string
|
|
this.setData({ refundVisible: true, refundOrderNo: orderNo, refundReason: '' })
|
|
},
|
|
|
|
onRefundClose() {
|
|
this.setData({ refundVisible: false })
|
|
},
|
|
|
|
onRefundInput(e: WechatMiniprogram.Input) {
|
|
this.setData({ refundReason: e.detail.value })
|
|
},
|
|
|
|
onRefundSubmit() {
|
|
if (this.data.refundSubmitting) return
|
|
const orderNo = this.data.refundOrderNo
|
|
const reason = (this.data.refundReason || '').trim()
|
|
if (!reason) {
|
|
wx.showToast({ title: '请填写退款原因', icon: 'none' })
|
|
return
|
|
}
|
|
this.setData({ refundSubmitting: true })
|
|
request({ url: '/order/refund', method: 'POST', data: { order_no: orderNo, reason } })
|
|
.then(() => {
|
|
logger.info(LOG, '退款申请提交成功, orderNo=', orderNo)
|
|
wx.showToast({ title: '退款申请已提交,等待审核', icon: 'none' })
|
|
this.setData({ refundVisible: false, refundSubmitting: false })
|
|
this.refresh()
|
|
})
|
|
.catch((err: any) => {
|
|
logger.warn(LOG, '退款申请失败, orderNo=', orderNo, 'code=', err && err.code)
|
|
this.setData({ refundSubmitting: false })
|
|
})
|
|
},
|
|
|
|
onGoMall() {
|
|
wx.navigateTo({
|
|
url: '/pages/mall/mall',
|
|
fail: (e) => logger.warn(LOG, 'navigateTo 商城失败, err=', e && e.errMsg),
|
|
})
|
|
},
|
|
})
|