Files
miniprogram-1/miniprogram/pages/order/order.ts
2025-12-23 17:33:05 +08:00

173 lines
6.1 KiB
TypeScript

import apiManager from '../../utils/api'
import logger from '../../utils/logger'
Page({
data: {
orders: [] as Array<any>,
page: 1,
size: 20,
total: 0,
hasMore: true,
isLoading: false,
refundDialogVisible: false,
refundOrderId: '',
refundAmountCents: 0,
selectedReasons: [] as Array<string>,
reasonText: '',
reasonMax: 200
},
onLoad() {
this.loadOrders(1)
},
onPullDownRefresh() {
this.setData({ page: 1 })
this.loadOrders(1).finally(() => wx.stopPullDownRefresh())
},
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
const next = this.data.page + 1
this.loadOrders(next)
}
},
async loadOrders(page: number) {
this.setData({ isLoading: page === 1 })
try {
const res = await apiManager.listOrders(page, this.data.size)
const items = (res.items || []).map((it: any) => ({
...it,
displayTime: this.formatTime(it.created_time),
displayAmount: this.formatAmount(it.amount_cents),
refund_display: this.formatRefundStatusText(it.refund_status),
refund_class: this.formatRefundStatusClass(it.refund_status)
}))
logger.info(items)
if (page === 1) {
this.setData({ orders: items, page, total: res.total || items.length, hasMore: page * this.data.size < (res.total || items.length), isLoading: false })
} else {
const merged = [...this.data.orders, ...items]
this.setData({ orders: merged, page, total: res.total || merged.length, hasMore: page * this.data.size < (res.total || merged.length), isLoading: false })
}
} catch (e) {
this.setData({ isLoading: false })
wx.showToast({ title: '加载失败', icon: 'none' })
}
},
formatTime(t: string) {
try {
const d = new Date(t)
const y = d.getFullYear()
const m = `${d.getMonth() + 1}`.padStart(2, '0')
const dd = `${d.getDate()}`.padStart(2, '0')
const hh = `${d.getHours()}`.padStart(2, '0')
const mm = `${d.getMinutes()}`.padStart(2, '0')
return `${y}-${m}-${dd} ${hh}:${mm}`
} catch (_) {
return t
}
},
formatAmount(cents: number) {
if (!cents && cents !== 0) return ''
return (cents / 100).toFixed(2)
},
formatRefundStatusText(s: string | null | undefined) {
const v = String(s || '').trim().toUpperCase()
if (!v) return '未申请退款'
if (v === 'REFUND') return '已退款'
if (v === 'FAIL') return '退款失败'
if (v === 'CLOSED') return '退款已关闭'
if (v === 'ABNORMAL') return '退款异常'
return s as string
},
formatRefundStatusClass(s: string | null | undefined) {
const v = String(s || '').trim().toUpperCase()
if (v === 'REFUND') return 'status-success'
if (v === 'FAIL') return 'status-failed'
if (v === 'CLOSED') return 'status-closed'
if (v === 'ABNORMAL') return 'status-abnormal'
return 'status-default'
},
async onRefundTap(e: any) {
const id = e.currentTarget.dataset.id
try {
const detail = await apiManager.getOrderDetail(id)
const updated = this.data.orders.map((it: any) => it.id === id ? {
...it,
refund_status: detail.refund_status || it.refund_status,
refund_display: this.formatRefundStatusText(detail.refund_status || it.refund_status),
refund_class: this.formatRefundStatusClass(detail.refund_status || it.refund_status),
amount_cents: detail.amount_cents ?? it.amount_cents,
displayAmount: this.formatAmount(detail.amount_cents ?? it.amount_cents),
refundable_amount_cents: detail.refundable_amount_cents ?? it.refundable_amount_cents,
displayRefundable: this.formatAmount(detail.refundable_amount_cents ?? 0),
can_refund: detail.can_refund ?? it.can_refund
} : it)
this.setData({ orders: updated })
if (detail.refund_status) {
wx.showToast({ title: `退款状态:${detail.refund_status}`, icon: 'none' })
return
}
if (detail.can_refund) {
this.setData({ refundOrderId: id, refundAmountCents: Number(detail.refundable_amount_cents || 0), selectedReasons: [], reasonText: '', refundDialogVisible: true })
} else {
wx.showToast({ title: '当前订单不可退款', icon: 'none' })
}
} catch (_) {
wx.showToast({ title: '获取订单详情失败', icon: 'none' })
}
},
onReasonGroupChange(e: any) {
this.setData({ selectedReasons: e.detail.value || [] })
},
onReasonTextInput(e: any) {
const v = e.detail.value || ''
const max = this.data.reasonMax
this.setData({ reasonText: v.length > max ? v.slice(0, max) : v })
},
closeRefundDialog() {
this.setData({ refundDialogVisible: false })
},
async confirmRefundDialog() {
const sel = this.data.selectedReasons
const txt = (this.data.reasonText || '').trim()
if ((!sel || sel.length === 0) && !txt) {
wx.showToast({ title: '请至少选择或填写一项理由', icon: 'none' })
return
}
const reason = [...sel, txt].filter(Boolean).join('|')
try {
const r = await apiManager.refund(this.data.refundOrderId, reason, this.data.refundAmountCents)
wx.showToast({ title: '已提交退款申请', icon: 'none' })
this.setData({ refundDialogVisible: false })
const outRefundNo = r && (r.out_refund_no || (r.data && r.data.out_refund_no))
if (outRefundNo) {
setTimeout(async () => {
try {
const s = await apiManager.getRefundStatus(outRefundNo)
const status = s && (s.status || '')
const updated = this.data.orders.map((it: any) => it.id === this.data.refundOrderId ? { ...it, refund_status: status, refund_display: this.formatRefundStatusText(status), refund_class: this.formatRefundStatusClass(status), can_refund: false } : it)
this.setData({ orders: updated })
wx.showToast({ title: status ? `退款状态:${status}` : '查询成功', icon: 'none' })
} catch (_) {
wx.showToast({ title: '查询失败', icon: 'none' })
}
}, 1000)
}
} catch (e) {
wx.showToast({ title: '提交失败', icon: 'none' })
}
}
})