first commit

This commit is contained in:
2026-09-11 01:45:04 +08:00
commit 0a8d60d87f
145 changed files with 40978 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"usingComponents": {
"navigation-bar": "/components/navigation-bar/navigation-bar",
"empty-state": "/components/empty-state/empty-state"
}
}
+127
View File
@@ -0,0 +1,127 @@
import { request } from '../../utils/request'
import { PagedResult, OrderItem } from '../../types/models'
import { fenToYuan, formatDateTime, formatCountdown } from '../../utils/format'
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_TYPE: Record<number, string> = { 1: 'orange', 2: 'green', 3: 'gray', 4: 'gray', 5: 'blue' }
Page({
data: {
filters: STATUS_FILTERS,
activeFilter: 0,
list: [] as OrderItem[],
loading: true,
page: 1,
pageSize: 20,
hasMore: true,
},
onShow() {
this.refresh()
},
refresh() {
this.setData({ page: 1, hasMore: true, loading: this.data.list.length === 0 })
this.fetchList(1)
},
onReachBottom() {
if (!this.data.hasMore || this.data.loading) return
this.fetchList(this.data.page + 1)
},
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
request<PagedResult<OrderItem>>({ url: '/order', data: params })
.then((res) => {
const now = Math.floor(Date.now() / 1000)
const list = res.list.map((it) => ({
...it,
_statusText: STATUS_TEXT[it.order_status] || '',
_statusType: STATUS_TYPE[it.order_status] || 'gray',
_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 = page === 1 ? list : this.data.list.concat(list)
this.setData({
list: merged,
hasMore: merged.length < res.total,
loading: false,
page,
})
})
.catch(() => this.setData({ loading: 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
wx.showModal({
title: '取消订单',
content: '确认取消该订单?',
success: (res) => {
if (!res.confirm) return
request({ url: `/order/${orderNo}/cancel`, method: 'POST' })
.then(() => {
wx.showToast({ title: '已取消', icon: 'success' })
this.refresh()
})
.catch(() => {})
},
})
},
onPay() {
wx.showToast({ title: '请返回商城重新下单支付', icon: 'none' })
},
onRefund(e: WechatMiniprogram.TouchEvent) {
const orderNo = e.currentTarget.dataset.no as string
wx.showModal({
title: '申请退款',
editable: true,
placeholderText: '请填写退款原因',
success: (res) => {
if (!res.confirm) return
const reason = (res.content || '').trim()
if (!reason) {
wx.showToast({ title: '请填写退款原因', icon: 'none' })
return
}
request({ url: '/order/refund', method: 'POST', data: { order_no: orderNo, reason } })
.then(() => {
wx.showToast({ title: '退款申请已提交,等待审核', icon: 'none' })
this.refresh()
})
.catch(() => {})
},
})
},
onGoMall() {
wx.navigateTo({ url: '/pages/mall/mall' })
},
})
+55
View File
@@ -0,0 +1,55 @@
<view class="page page-flex">
<navigation-bar title="我的订单" />
<scroll-view scroll-y class="scroll-body" bindscrolltolower="onReachBottom">
<!-- 筛选 -->
<scroll-view scroll-x class="filter-scroll" show-scrollbar="{{false}}">
<view class="filter-row">
<view
wx:for="{{filters}}"
wx:key="name"
class="filter-item {{activeFilter === index ? 'active' : ''}}"
data-index="{{index}}"
bindtap="onFilterTap"
>{{item.name}}</view>
</view>
</scroll-view>
<empty-state
wx:if="{{!loading && !list.length}}"
icon="icon-doc"
title="还没有订单"
desc=""
buttonText="去看看额度包"
bind:action="onGoMall"
/>
<!-- 订单列表 -->
<view class="list">
<view class="order-card card" wx:for="{{list}}" wx:key="order_no">
<view class="order-top">
<text class="order-no">订单 {{item._orderTail}}</text>
<text wx:if="{{item._payChannelText}}" class="tag tag-gray">{{item._payChannelText}}</text>
<text class="tag tag-{{item._statusType}}">{{item._statusText}}</text>
</view>
<view class="order-body">
<view class="order-goods">{{item.goods_name}}</view>
<view class="order-benefit" wx:if="{{item._benefit}}">{{item._benefit}}</view>
<view class="order-bottom">
<text class="order-amount">实付 <text class="num">{{item._amount}}</text></text>
<view class="order-actions">
<block wx:if="{{item.order_status === 1}}">
<text wx:if="{{item._countdown}}" class="countdown">剩余 {{item._countdown}}</text>
<view wx:if="{{item._canCancel}}" class="action-btn outline" data-no="{{item.order_no}}" bindtap="onCancel">取消订单</view>
<view class="action-btn primary" data-no="{{item.order_no}}" bindtap="onPay">去支付</view>
</block>
<block wx:elif="{{item.order_status === 2}}">
<view class="action-btn outline" data-no="{{item.order_no}}" bindtap="onRefund">申请退款</view>
</block>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
+122
View File
@@ -0,0 +1,122 @@
.page-flex {
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.scroll-body {
flex: 1;
height: 0;
}
.filter-scroll {
background: #fff;
white-space: nowrap;
}
.filter-row {
display: inline-flex;
padding: 20rpx 24rpx;
}
.filter-item {
flex-shrink: 0;
padding: 0 28rpx;
height: 56rpx;
line-height: 56rpx;
border-radius: 28rpx;
font-size: 24rpx;
color: #3C3C43;
background: #F2F2F7;
margin-right: 16rpx;
}
.filter-item.active {
background: #007AFF;
color: #fff;
}
.list {
padding: 24rpx;
}
.order-card {
margin-bottom: 20rpx;
padding: 28rpx;
}
.order-top {
display: flex;
align-items: center;
justify-content: space-between;
}
.order-no {
font-size: 24rpx;
color: #8E8E93;
}
.order-body {
margin-top: 20rpx;
}
.order-goods {
font-size: 30rpx;
font-weight: 600;
color: #000;
}
.order-benefit {
font-size: 24rpx;
color: #8E8E93;
margin-top: 6rpx;
}
.order-bottom {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24rpx;
}
.order-amount {
font-size: 26rpx;
color: #3C3C43;
}
.order-amount .num {
font-size: 32rpx;
font-weight: 600;
color: #000;
}
.order-actions {
display: flex;
align-items: center;
gap: 16rpx;
}
.countdown {
font-size: 24rpx;
color: #8E8E93;
}
.action-btn {
height: 56rpx;
padding: 0 28rpx;
border-radius: 28rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.action-btn.outline {
border: 1rpx solid #C7C7CC;
color: #3C3C43;
}
.action-btn.primary {
background: #007AFF;
color: #fff;
}