feat: 订单列表

This commit is contained in:
jqtmviyu 2025-05-13 20:19:48 +08:00
parent dd5010ddc7
commit 2ac3cd88f5
6 changed files with 819 additions and 2 deletions

View File

@ -248,7 +248,7 @@ const onCopy = (id: string) => {
} }
// //
const query = defineProps<{ const query = defineProps<{
id: string id: string // ID
}>() }>()
// //

View File

@ -0,0 +1,359 @@
<template name="skeleton">
<view class="sk-container">
<scroll-view :scroll-y="true" class="orders orderList--orders" :enable-back-to-top="true">
<view class="card orderList--card">
<view class="status orderList--status">
<text class="date orderList--date sk-transparent sk-text-14-2857-751 sk-text"
>2023-04-14 13:14:20</text
>
<text class="sk-transparent sk-text-14-2857-212 sk-text">待发货</text>
</view>
<navigator class="goods orderList--goods" hover-class="none">
<view class="cover orderList--cover">
<image class="image orderList--image sk-image" mode="aspectFit"></image>
</view>
<view class="meta orderList--meta">
<view
class="name orderList--name ellipsis orderList--ellipsis sk-transparent sk-text-14-2857-190 sk-text"
>ins风小碎花泡泡袖衬110-160cm</view
>
<view class="type orderList--type sk-transparent sk-text-22-2222-84 sk-text"
>米底碎花 130</view
>
</view>
</navigator>
<view class="payment orderList--payment">
<text
class="quantity orderList--quantity sk-transparent sk-text-0-0000-734 sk-text"
style="background-position-x: 100%"
>共2件商品</text
>
<text
class="sk-transparent sk-text-0-0000-567 sk-text"
style="background-position-x: 100%"
>实付</text
>
<text class="amount orderList--amount sk-transparent"
><text class="symbol orderList--symbol sk-transparent sk-opacity">¥</text>203.00</text
>
</view>
<view class="action orderList--action">
<navigator
class="button orderList--button secondary orderList--secondary sk-transparent sk-text-14-2857-906 sk-text"
hover-class="none"
>
再次购买
</navigator>
</view>
</view>
<view class="card orderList--card">
<view class="status orderList--status">
<text class="date orderList--date sk-transparent sk-text-14-2857-245 sk-text"
>2023-04-14 13:14:20</text
>
<text class="sk-transparent sk-text-14-2857-180 sk-text">待发货</text>
</view>
<navigator class="goods orderList--goods" hover-class="none">
<view class="cover orderList--cover">
<image class="image orderList--image sk-image" mode="aspectFit"></image>
</view>
<view class="meta orderList--meta">
<view
class="name orderList--name ellipsis orderList--ellipsis sk-transparent sk-text-14-2857-592 sk-text"
>经典品种交响曲智利混酿干白750毫升</view
>
<view class="type orderList--type sk-transparent sk-text-22-2222-288 sk-text"
>礼袋单支/双支均适用</view
>
</view>
</navigator>
<view class="payment orderList--payment">
<text
class="quantity orderList--quantity sk-transparent sk-text-0-0000-744 sk-text"
style="background-position-x: 100%"
>共1件商品</text
>
<text
class="sk-transparent sk-text-0-0000-596 sk-text"
style="background-position-x: 100%"
>实付</text
>
<text class="amount orderList--amount sk-transparent"
><text class="symbol orderList--symbol sk-transparent sk-opacity">¥</text>11.90</text
>
</view>
<view class="action orderList--action">
<navigator
class="button orderList--button secondary orderList--secondary sk-transparent sk-text-14-2857-29 sk-text"
hover-class="none"
>
再次购买
</navigator>
</view>
</view>
</scroll-view>
</view>
</template>
<style>
.sk-transparent {
color: transparent !important;
}
.sk-text-26-6667-284 {
background-image: linear-gradient(
transparent 26.6667%,
#eeeeee 0%,
#eeeeee 73.3333%,
transparent 0%
) !important;
background-size: 100% 60rpx;
position: relative !important;
}
.sk-text {
background-origin: content-box !important;
background-clip: content-box !important;
background-color: transparent !important;
color: transparent !important;
background-repeat: repeat-y !important;
}
.sk-text-26-6667-396 {
background-image: linear-gradient(
transparent 26.6667%,
#eeeeee 0%,
#eeeeee 73.3333%,
transparent 0%
) !important;
background-size: 100% 60rpx;
position: relative !important;
}
.sk-text-26-6667-644 {
background-image: linear-gradient(
transparent 26.6667%,
#eeeeee 0%,
#eeeeee 73.3333%,
transparent 0%
) !important;
background-size: 100% 60rpx;
position: relative !important;
}
.sk-text-26-6667-171 {
background-image: linear-gradient(
transparent 26.6667%,
#eeeeee 0%,
#eeeeee 73.3333%,
transparent 0%
) !important;
background-size: 100% 60rpx;
position: relative !important;
}
.sk-text-26-6667-939 {
background-image: linear-gradient(
transparent 26.6667%,
#eeeeee 0%,
#eeeeee 73.3333%,
transparent 0%
) !important;
background-size: 100% 60rpx;
position: relative !important;
}
.sk-text-14-2857-751 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-14-2857-212 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-14-2857-190 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 36.4rpx;
position: relative !important;
}
.sk-text-22-2222-84 {
background-image: linear-gradient(
transparent 22.2222%,
#eeeeee 0%,
#eeeeee 77.7778%,
transparent 0%
) !important;
background-size: 100% 43.2rpx;
position: relative !important;
}
.sk-text-0-0000-734 {
background-image: linear-gradient(
transparent 0%,
#eeeeee 0%,
#eeeeee 100%,
transparent 0%
) !important;
background-size: 100% 24rpx;
position: relative !important;
}
.sk-text-0-0000-567 {
background-image: linear-gradient(
transparent 0%,
#eeeeee 0%,
#eeeeee 100%,
transparent 0%
) !important;
background-size: 100% 28rpx;
position: relative !important;
}
.sk-opacity {
opacity: 0 !important;
}
.sk-text-14-2857-906 {
background-color: #ebebeb !important;
}
.sk-text-14-2857-245 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-14-2857-180 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-14-2857-592 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 36.4rpx;
position: relative !important;
}
.sk-text-22-2222-288 {
background-image: linear-gradient(
transparent 22.2222%,
#eeeeee 0%,
#eeeeee 77.7778%,
transparent 0%
) !important;
background-size: 100% 43.2rpx;
position: relative !important;
}
.sk-text-0-0000-744 {
background-image: linear-gradient(
transparent 0%,
#eeeeee 0%,
#eeeeee 100%,
transparent 0%
) !important;
background-size: 100% 24rpx;
position: relative !important;
}
.sk-text-0-0000-596 {
background-image: linear-gradient(
transparent 0%,
#eeeeee 0%,
#eeeeee 100%,
transparent 0%
) !important;
background-size: 100% 28rpx;
position: relative !important;
}
.sk-text-14-2857-29 {
background-color: #ebebeb !important;
}
.sk-text-14-2857-33 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-14-2857-709 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 39.2rpx;
position: relative !important;
}
.sk-text-0-0000-544 {
background-image: linear-gradient(
transparent 0%,
#eeeeee 0%,
#eeeeee 100%,
transparent 0%
) !important;
background-size: 100% 28rpx;
position: relative !important;
}
.sk-text-14-2857-253 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 36.4rpx;
position: relative !important;
}
.sk-image {
background: #efefef !important;
}
.sk-pseudo::before,
.sk-pseudo::after {
background: #efefef !important;
background-image: none !important;
color: transparent !important;
border-color: transparent !important;
}
.sk-pseudo-rect::before,
.sk-pseudo-rect::after {
border-radius: 0 !important;
}
.sk-pseudo-circle::before,
.sk-pseudo-circle::after {
border-radius: 50% !important;
}
.sk-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: transparent;
}
.orders .action .secondary {
border-color: #ebebeb !important;
}
</style>

View File

@ -0,0 +1,346 @@
<template>
<OrderListSkeleton v-if="showSkeleton" />
<scroll-view scroll-y class="orders" @scrolltolower="onScrollToLower" v-else>
<view class="card" v-for="item in orderItems" :key="item.id">
<!-- 订单信息 -->
<view class="status">
<text class="date">{{ item.createTime }}</text>
<!-- 订单状态文字 -->
<text>{{ OrderStateList[item.orderState].text }}</text>
<!-- 待评价/已完成/已取消 状态: 展示删除订单 -->
<text v-if="item.orderState >= 4" class="icon-delete" @tap="handleDelete(item)"
>删除订单</text
>
</view>
<!-- 商品信息点击商品跳转到订单详情不是商品详情 -->
<navigator
v-for="sku in item.skus"
:key="sku.id"
class="goods"
:url="`/pagesOrder/detail/detail?id=${item.id}`"
hover-class="none"
>
<view class="cover">
<image class="image" mode="aspectFit" :src="sku.image"></image>
</view>
<view class="meta">
<view class="name ellipsis">{{ sku.name }}</view>
<view class="type">{{ sku.attrsText.replaceAll('', ' ') }}</view>
</view>
</navigator>
<!-- 支付信息 -->
<view class="payment">
<text class="quantity">{{ item.totalNum }}件商品</text>
<text>实付</text>
<text class="amount"> <text class="symbol">¥</text>{{ item.payMoney.toFixed(2) }}</text>
</view>
<!-- 订单操作按钮 -->
<view class="action">
<!-- 待付款状态显示去支付按钮 -->
<template v-if="item.orderState === 1">
<text class="time">支付剩余</text>
<uni-countdown
color="#999999"
splitor-color="#999999"
:show-day="false"
:second="item.countdown"
:show-colon="false"
@timeup="item.orderState = 6"
/>
<view
class="button"
:class="{ primary: item.countdown > 0, disabled: item.countdown <= 0 }"
@tap="handlePay(item)"
>去支付</view
>
</template>
<template v-else>
<navigator
class="button secondary"
:url="`/pagesOrder/create/create?orderId=${item.id}`"
hover-class="none"
>
再次购买
</navigator>
<!-- 待收货状态: 展示确认收货 -->
<view v-if="item.orderState === 3" class="button primary">确认收货</view>
</template>
</view>
</view>
<!-- 底部提示文字 -->
<view class="loading-text" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
{{ isLoading ? '正在加载...' : '没有更多数据~' }}
</view>
</scroll-view>
</template>
<script setup lang="ts">
import { deleteMemberOrderAPI, getMemberOrderListAPI, OrderStateList } from '@/services/order'
import { getMockPayParamsAPI, getWechatPayParamsAPI } from '@/services/pay'
import type { OrderItem, OrderListParams } from '@/types/order'
import { onBeforeMount, ref } from 'vue'
import OrderListSkeleton from './OrderListSkeleton.vue'
//
const { safeAreaInsets } = uni.getSystemInfoSync()
const props = defineProps<{
orderState: number
}>()
//
const queryParams = ref<OrderListParams>({
page: 1,
pageSize: 5,
orderState: props.orderState,
})
//
const pages = ref(1)
//
const showSkeleton = ref(true)
const isLoading = ref(false)
//
const orderItems = ref<OrderItem[]>([])
const getOrderList = async () => {
isLoading.value = true
const res = await getMemberOrderListAPI(queryParams.value)
if (res.code === '1') {
orderItems.value = res.result.items
pages.value = res.result.pages
}
isLoading.value = false
}
//
const isDev = import.meta.env.DEV
const handlePay = async (item: OrderItem) => {
let res = null
if (isDev) {
//
res = await getMockPayParamsAPI({ orderId: item.id })
} else {
//
res = await getWechatPayParamsAPI({ orderId: item.id })
}
if (res.code === '1') {
await uni.showToast({ title: '支付成功', icon: 'success' })
item.orderState = 2
} else {
uni.showToast({ title: res.msg, icon: 'error' })
}
}
//
const handleDelete = async (item: OrderItem) => {
console.log('删除订单')
uni.showModal({
content: '确定删除订单吗?',
success: async ({ confirm }) => {
if (confirm) {
const res = await deleteMemberOrderAPI({ ids: [item.id] })
if (res.code === '1') {
await uni.showToast({ title: '删除订单成功', icon: 'success' })
orderItems.value.splice(orderItems.value.indexOf(item), 1)
}
}
},
})
}
//
const onScrollToLower = () => {
console.log('滚动到底部')
if (queryParams.value.page! < pages.value) {
queryParams.value.page!++
getOrderList()
}
}
onBeforeMount(async () => {
await getOrderList()
showSkeleton.value = false
})
</script>
<style lang="scss">
//
.orders {
.card {
min-height: 100rpx;
padding: 20rpx;
margin: 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #fff;
&:last-child {
padding-bottom: 40rpx;
}
}
.status {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
color: #999;
margin-bottom: 15rpx;
.date {
color: #666;
flex: 1;
}
.primary {
color: #ff9240;
}
.icon-delete {
line-height: 1;
margin-left: 10rpx;
padding-left: 10rpx;
border-left: 1rpx solid #e3e3e3;
}
}
.goods {
display: flex;
margin-bottom: 20rpx;
.cover {
width: 170rpx;
height: 170rpx;
margin-right: 20rpx;
border-radius: 10rpx;
overflow: hidden;
position: relative;
.image {
width: 170rpx;
height: 170rpx;
}
}
.quantity {
position: absolute;
bottom: 0;
right: 0;
line-height: 1;
padding: 6rpx 4rpx 6rpx 8rpx;
font-size: 24rpx;
color: #fff;
border-radius: 10rpx 0 0 0;
background-color: rgba(0, 0, 0, 0.6);
}
.meta {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.name {
height: 80rpx;
font-size: 26rpx;
color: #444;
}
.type {
line-height: 1.8;
padding: 0 15rpx;
margin-top: 10rpx;
font-size: 24rpx;
align-self: flex-start;
border-radius: 4rpx;
color: #888;
background-color: #f7f7f8;
}
.more {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 22rpx;
color: #333;
}
}
.payment {
display: flex;
justify-content: flex-end;
align-items: center;
line-height: 1;
padding: 20rpx 0;
text-align: right;
color: #999;
font-size: 28rpx;
border-bottom: 1rpx solid #eee;
.quantity {
font-size: 24rpx;
margin-right: 16rpx;
}
.amount {
color: #444;
margin-left: 6rpx;
}
.symbol {
font-size: 20rpx;
}
}
.action {
display: flex;
justify-content: flex-end;
align-items: center;
padding-top: 20rpx;
.time {
color: #999;
font-size: 24rpx;
margin-right: 20rpx;
}
.button {
width: 180rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
margin-left: 20rpx;
border-radius: 60rpx;
border: 1rpx solid #ccc;
font-size: 26rpx;
color: #444;
}
.secondary {
color: #27ba9b;
border-color: #27ba9b;
}
.primary {
color: #fff;
background-color: #27ba9b;
border-color: #27ba9b;
}
.disabled {
color: #ccc;
background-color: #f7f7f8;
border-color: #eee;
}
}
.loading-text {
text-align: center;
font-size: 28rpx;
color: #666;
padding: 20rpx 0;
}
}
</style>

View File

@ -0,0 +1,112 @@
<template>
<view class="viewport">
<!-- tabs -->
<view class="tabs">
<text
class="item"
v-for="(item, index) in orderTabs"
:key="item.orderState"
@tap="activeTab = index"
>
{{ item.title }}
</text>
<!-- 游标 -->
<view class="cursor" :style="{ left: activeTab * (100 / orderTabs.length) + '%' }"></view>
</view>
<!-- 滑动容器 -->
<swiper
class="swiper"
:current="activeTab"
@change="
(e) => {
activeTab = e.detail.current
}
"
>
<!-- 滑动项 -->
<swiper-item v-for="item in orderTabs" :key="item.orderState">
<!-- 订单列表 -->
<order-list :type="item.orderState" :orderState="item.orderState" />
</swiper-item>
</swiper>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import orderList from './components/orderList.vue'
// tabs
const orderTabs = ref([
{ orderState: 0, title: '全部' },
{ orderState: 1, title: '待付款' },
{ orderState: 2, title: '待发货' },
{ orderState: 3, title: '待收货' },
{ orderState: 4, title: '待评价' },
])
const query = withDefaults(
defineProps<{
type: string
}>(),
{
type: '0',
},
)
// tab
const activeTab = ref(
orderTabs.value.findIndex((item) => item.orderState.toString() === query.type),
)
</script>
<style lang="scss">
page {
height: 100%;
overflow: hidden;
}
.viewport {
height: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
}
// tabs
.tabs {
display: flex;
justify-content: space-around;
line-height: 60rpx;
margin: 0 10rpx;
background-color: #fff;
box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
position: relative;
z-index: 9;
.item {
flex: 1;
text-align: center;
padding: 20rpx;
font-size: 28rpx;
color: #262626;
}
.cursor {
position: absolute;
left: 0;
bottom: 0;
width: 20%;
height: 6rpx;
padding: 0 50rpx;
background-color: #27ba9b;
/* 过渡效果 */
transition: all 0.4s;
}
}
// swiper
.swiper {
background-color: #f7f7f8;
}
</style>

View File

@ -21,7 +21,7 @@ const interceptorOptions = {
options.url = baseUrl + options.url options.url = baseUrl + options.url
} }
// 2. 超时时间, 默认60s // 2. 超时时间, 默认60s
options.timeout = 10 * 1000 options.timeout = 60 * 1000
// 3. 请求头 // 3. 请求头
options.header = { options.header = {
...options.header, ...options.header,