mall_client/zyhs3_uniapp/pages/order/index.vue

641 lines
21 KiB
Vue
Raw Permalink Normal View History

2026-03-13 07:50:35 +00:00
<template>
<view>
<v-tabs
ref="tabs"
v-if="isDone"
v-model="current"
:tabs="tabs"
field="name"
height="90rpx"
fontSize="30rpx"
:fixed="true"
topOffset="var(--window-top)"
:scroll="true"
@change="changeTab">
</v-tabs>
<view class="list">
<view class="list-item" v-for="(item, i) in list" :key="i" @tap="goDetails(item)">
<view class="flex flex-between store">
<view class="flex flex-align-center">
<image :src="filterImg(item.storeInfo.logoImage)" mode="" class="logo"></image>
<text class="font34 color-b3">{{item.storeInfo.name}}</text>
<!-- <text class="iconfont color-b9 font36">&#xe770;</text> -->
</view>
<text class="color-purple font28" v-if="item.status === 1">待付款</text>
<text class="color-purple font28" v-if="item.status === 2">未支付完成</text>
<text class="color-purple font28" v-if="item.status === 3">{{deliveryStatus(item)}}</text>
<text class="color-purple font28" v-if="item.status === 4">待评价</text>
<text class="color-b9 font28" v-if="item.status === 5">已完成</text>
<text class="color-b9 font28" v-if="item.status === 10">已退款</text>
<text class="color-b9 font28" v-if="item.status === 11">已关闭</text>
</view>
<view class="goods flex">
<view class="goods-img">
<image :src="filterImg(item.skuImage || item.goodsImage)" mode="" class="img"></image>
<text class="tips">{{item.orderType | filterType}}</text>
</view>
<view class="flex flex-column" style="width: 100%;">
<block v-if="item.combOrderType === 1">
<text class="line font28 color-b3">{{item.goodsName + ' - ' + item.periodNo + '期'}}</text>
<text class="spec">{{item.skuName}}</text>
<view class="flex flex-between" style="width: 100%;"></view>
</block>
<block v-else>
<text class="line font28 color-b3">{{item.goodsName}}</text>
<text class="spec">{{item.skuName}}</text>
<view class="flex flex-between" style="width: 100%;">
<view class="flex">
<text class="color-red font30">¥{{item.unitPrice}}</text>
<text class="color-red font30" v-if="item.payIntegralAmount">绿色积分{{item.payIntegralAmount}}</text>
</view>
<text class="color-b9 font26">x{{item.number}}</text>
</view>
</block>
</view>
</view>
<!-- 子订单渲染父订单为待发货和已收货时展示 -->
<view
class="child-orders"
v-if="isShipTab() && item.combOrderType === 2 && item.childOrderList && item.childOrderList.length"
@tap.stop>
<view class="child-orders__title" @tap.stop="toggleChildOpen(item)">
<view>
<text class="font30 color-b3">分期子订单</text>
<text class="font24 color-b9">{{ item.childOrderList.length }}</text>
</view>
<text class="font26 color-b9 toggle">{{ isChildOpen(item) ? '收起' : '展开' }}</text>
</view>
<scroll-view scroll-y class="child-orders__scroller" v-if="isChildOpen(item)">
<view class="child-orders__content">
<block v-if="item.combOrderType === 2 && item.status === 4 && item.deliveryLogisticsStatus === 3"></block>
<block v-else>
<view class="child-orders__steps">
<u-steps direction="column" :activeColor="'#e5e5e5'" :inactiveColor="'#e5e5e5'">
<u-steps-item v-for="(sub, subIndex) in item.childOrderList" :key="sub.id || subIndex">
<template #icon>
<view class="custom-step-icon">
<u-icon :name="childStepIcon(sub)" :color="childStepColor(sub)" size="18"></u-icon>
</view>
</template>
</u-steps-item>
</u-steps>
</view>
</block>
<view class="child-orders__list">
<view
class="child-card"
v-for="(sub, subIndex) in item.childOrderList"
:key="sub.id || subIndex">
<block v-if="sub.status === 3 && sub.deliveryMethod === 1 && sub.deliveryLogisticsStatus === 1">
<view class="flex child-card__header">
<text class="badge">{{ sub.periodNo }}</text>
<view class="flex flex-align-center">
<text class="font26 color-b9" style="margin-left: 10rpx;margin-right: 10rpx;">预计发货</text>
<!-- <text class="font24 color-b3">{{ expectedShipRange(item, sub) }}</text> -->
<text class="font24 color-b3">{{ sub.expectedDeliveryTime | formatYearMonth }}</text>
</view>
</view>
<view class="flex child-card__body">
<text class="font26 color-b9">收货人信息</text>
<text class="font26 consignee" @tap.stop="openSenderForm(sub)">查看</text>
<text class="font26 consignee" @tap.stop="openAddressEdit(item, sub)">修改</text>
<!-- <text class="font24" :class="(sub.senderName||sub.senderMobile||sub.senderAddress)?'color-b3':'color-b9'">{{ (sub.senderName||sub.senderMobile||sub.senderAddress) ? '已设置' : '未设置' }}</text> -->
</view>
</block>
<block v-if="sub.status === 3 && sub.deliveryMethod === 1 && sub.deliveryLogisticsStatus === 2">
<view class="flex flex-between child-card__header">
<view class="flex flex-align-center">
<text class="badge">{{ sub.periodNo }}</text>
<text class="font26 color-b3 f-b">已发货</text>
</view>
<view class="flex flex-align-center" @tap.stop="goDetails(sub)">
<text class="font26 color-b9">订单详情</text>
<text class="iconfont color-b9 font36">&#xe770;</text>
</view>
</view>
<view class="child-card__body">
<text class="font26 color-b9" style="margin-left: 10rpx;margin-right: 10rpx;">发货时间</text>
<text class="font26 color-b3">{{ sub.deliveryLogisticsDeliverTime }}</text>
</view>
</block>
<block v-if="sub.status === 4">
<view class="flex flex-between child-card__header">
<view class="flex flex-align-center">
<text class="badge">{{ sub.periodNo }}</text>
<text class="font26 color-b3 f-b">已收货</text>
</view>
<view class="flex flex-align-center" @tap.stop="goDetails(sub)">
<text class="font26 color-b9">订单详情</text>
<text class="iconfont color-b9 font36">&#xe770;</text>
</view>
</view>
<view class="child-card__body">
<text class="font26 color-b9" style="margin-left: 10rpx;margin-right: 10rpx;">收货时间</text>
<text class="font26 color-b3">{{ sub.confirmReceiptTime }}</text>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
</view>
<block v-if="item.combOrderType === 1">
</block>
<block v-else>
<view class="flex flex-align-end flex-space-end total font30">
<text class="color-b9 font28">总价¥{{item.totalPrice}}</text>
<text class="color-b9" v-if="item.couponStatus === 2">优惠¥{{item.couponAmount}},</text>
<text class="color-b3">
<text v-if="item.payIntegralAmount">绿色积分{{item.payIntegralAmount}}</text>
</text>
</view>
</block>
<view class="btns">
<text class="btn base" v-if="item.status === 1 || item.status === 2" @tap.stop="payOrder(item)">立即支付</text>
<text class="btn base" v-if="item.status === 4" @tap.stop="toEvaluate(item.id)">立即评价</text>
<text class="btn base" v-if="item.deliveryLogisticsOrderNumber" @tap.stop="handlerExpress(item)">查看物流</text>
<text class="btn base" v-if="item.status === 3 && judgeReceiv(item)" @tap.stop="confimDelivery(item.id, 'getStatusNum')">确认收货</text>
<text class="btn base" v-if="item.status === 12 && judgeReceiv(item)" @tap.stop="confimUse(item.id, 'getStatusNum')">确认使用</text>
<text class="btn base" v-if="item.status === 5 && item.qvyu == 5 && item.status != 11" @tap.stop="handlerSign(item.id)">签署合同</text>
<!-- <block v-if="item.orderType !== 3 && item.status === 3 && judgeRefund(item)">
<block v-if="item.orderType !== 3 && item.status === 3 && judgeRefund(item)">
<text class="btn base" @tap.stop="onRefund(item)">申请退款</text>
</block> -->
<!-- <text class="btn base" v-if="item.status === 1 || item.status === 5 || item.status === 10 || item.status === 11" @tap.stop="delOrder(item.id, false)">删除订单</text> -->
<text class="btn base" v-if="item.status === 1 || item.status === 11" @tap.stop="delOrder(item.id, false)">删除订单</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
<!-- 收货人信息查看弹层u-popup + 表单样式 -->
<u-popup :show="senderPopupShow" mode="bottom" @close="senderPopupShow = false" :round="15">
<view class="sender-popup">
<view class="flex flex-center">
<text class="font32 color-b3">收货人信息</text>
</view>
<view class="sp-form">
<view class="sp-row">
<view class="sp-label">联系人</view>
<view class="sp-value">{{ senderForm.fullName || '—' }}</view>
</view>
<view class="sp-row">
<view class="sp-label">手机号</view>
<view class="sp-value">{{ senderForm.mobile || '—' }}</view>
</view>
<view class="sp-row">
<view class="sp-label">地区</view>
<view class="sp-value">{{ senderForm.areaText || '—' }}</view>
</view>
<view class="sp-row">
<view class="sp-label">详细地址</view>
<view class="sp-value">{{ senderForm.addressDetails || '—' }}</view>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import url from "@/common/http/url.js"
import mix from "./mix.js"
import vTabs from "@/components/v-tabs/v-tabs.vue"
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import { handlerSign } from '@/utils/common.js';
import publics from "@/common/utils/public.js"
export default{
components: { vTabs, uniLoadMore },
mixins: [mix],
data(){
return {
isDone: false,
loadingType: "loading",
current: 0,
tabs: [
{name: "全部", status: 1},
{name: "待付款", status: 2},
{name: "待发货", status: 3},
{name: "待收货", status: 4},
{name: "待使用", status: 6},
{name: "待评价", status: 5},
// {name: "退款", status: 6}
],
list: [],
childOpenMap: {}, // 记录每个父订单的折叠状态
// 收货人信息查看
senderForm:{
fullName: "",
mobile: "",
areaText: "",
addressDetails: ""
},
senderPopupShow: false,
reShow: true
}
},
filters: {
filterType(val){
switch(val){
case 1:
return "普通";
case 2:
return "精品";
case 3:
return "进货";
case 11:
return "新人";
case 12:
return "秒杀";
case 13:
return "特价";
case 14:
return "拼团";
case 15:
return "积分";
default:
return "其他";
}
},
formatYearMonth(val){
try{
if(!val) return ''
let source = val
if (typeof source === 'string'){
// 提取日期部分并兼容 iOS 解析
source = source.split('T')[0].replace(/-/g, '/')
}
const d = new Date(source)
if (isNaN(d.getTime())) return ''
const y = d.getFullYear()
const m = d.getMonth() + 1
return `${y}${m}`
}catch(e){
return ''
}
}
},
onPullDownRefresh() {
this.getStatusNum()
},
onReachBottom(){
this.loadData();
},
onLoad(option) {
let status = option.status || 1
if (option.status) {
this.tabs.map((v, i) =>{
if (parseInt(option.status) === v.status) {
this.current = i
}
})
}
},
onShow() {
// 详情页操作后返回触发全量刷新(含数量与列表)
try{
const need = uni.getStorageSync('ORDER_LIST_NEED_REFRESH')
if (need) {
uni.removeStorageSync('ORDER_LIST_NEED_REFRESH')
this.getStatusNum()
return
}
}catch(e){}
if (this.reShow) {
this.getStatusNum()
}
this.reShow = false;
},
methods:{
// 子订单步骤条:根据状态返回图标
childStepIcon(sub){
const s = Number(sub.status)
if(s === 4) return 'checkmark'
if(s === 3 && sub.deliveryMethod === 1 && sub.deliveryLogisticsStatus === 2) return 'car'
return 'clock'
},
// 子订单步骤条:根据状态返回颜色
childStepColor(sub){
const s = Number(sub.status)
if(s === 4) return '#5ac725'
if(s === 3 && sub.deliveryMethod === 1 && sub.deliveryLogisticsStatus === 2) return '#3c3cff'
return '#F8D247'
},
isShipTab(){
return this.tabs[this.current] && (this.tabs[this.current].status === 3 || this.tabs[this.current].status === 5)
},
handlerSign (id) {
handlerSign(id)
},
handlerExpress (item) {
this.$navigateTo(`/pages/order/express/express?com=${item.deliveryLogisticsCompanyCode}&num=${item.deliveryLogisticsOrderNumber}&name=${item.deliveryLogisticsCompanyName}&phone=${item.userMobile}`)
},
loadData(){
this.loadingType = "loading"
let params = {
pageNum: this.list.length,
type: this.tabs[this.current].status
}
this.$http("GET", url.order.orderList, params).then(res =>{
this.list = this.list.concat(res.data)
this.loadingType = res.data < 12 ? 'nomore' : 'more';
uni.stopPullDownRefresh();
})
},
getStatusNum(){
this.tabs = [
{name: "全部", status: 1},
{name: "待付款", status: 2},
{name: "待发货", status: 3},
{name: "待收货", status: 4},
{name: "待使用", status: 7},
{name: "待评价", status: 5},
// {name: "退款", status: 6}
];
this.$http("GET", url.order.getOrderAmount).then(res =>{
if (res.data.whole) {
this.tabs[0].name = `全部(${res.data.whole})`
}
if (res.data.unpaid){
this.tabs[1].name = `待付款(${res.data.unpaid})`
}
if (res.data.ongoing){
this.tabs[2].name = `待发货(${res.data.ongoing})`
}
if (res.data.waitReceived){
this.tabs[3].name = `待收货(${res.data.waitReceived})`
}
if (res.data.notUsed){
this.tabs[4].name = `待使用(${res.data.notUsed})`
}
if (res.data.evaluated){
this.tabs[5].name = `待评价(${res.data.evaluated})`
}
this.isDone = true
this.$nextTick(() => {
this.$refs.tabs && this.$refs.tabs.getTabItemWidth && this.$refs.tabs.getTabItemWidth()
this.list = []
this.loadData();
})
})
},
// 取消订单
cancelOrder(){
let _this = this;
this.$showModal("是否确定取消该笔订单?", null, res=>{
if (res.confirm) {
_this.$http("POST", url.order.closeOrder, {orderId: item.id}).then(res =>{
_this.$msg(res.data)
_this.list = []
_this.loadData()
})
}
})
},
// 订单详情
goDetails(item){
this.$navigateTo('details?id=' + item.id)
},
changeTab(i) {
this.current = i
this.list = []
this.loadData()
},
// 折叠/展开子订单
toggleChildOpen(parent){
const key = parent.id || `${parent.storeInfo && parent.storeInfo.id || 'store'}-${parent.createTime}`
this.$set(this.childOpenMap, key, !this.childOpenMap[key])
},
isChildOpen(parent){
const key = parent.id || `${parent.storeInfo && parent.storeInfo.id || 'store'}-${parent.createTime}`
return !!this.childOpenMap[key]
},
// 预计发货时间范围([付款+(n-1)月, 付款+n月]
expectedShipRange(parentItem, childItem){
try{
let base = parentItem.rmbPayTime || parentItem.payTime || parentItem.createTime
if(!base) return '时间未知'
let payDate = this.parseDate(base)
let start = this.addMonths(new Date(payDate), (childItem.periodNo || 1) - 1)
let end = this.addMonths(new Date(payDate), (childItem.periodNo || 1))
return `${this.formatDate(start)} ~ ${this.formatDate(end)}`
}catch(e){
return '时间未知'
}
},
addMonths(date, months){
let d = new Date(date)
let day = d.getDate()
d.setMonth(d.getMonth() + months)
if (d.getDate() !== day){
// 处理月底溢出
d.setDate(0)
}
return d
},
parseDate(str){
// 兼容 iOS将 YYYY-MM-DD 替换为 YYYY/MM/DD
if(typeof str === 'string'){
return str.replace(/-/g, '/').slice(0, 19)
}
return str
},
formatDate(d){
const pad = n => n < 10 ? '0'+n : ''+n
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`
},
openSenderForm(childItem){
this.$http("GET", url.order.getOrderInfoById, {orderId: childItem.id}).then(({code, data}) =>{
if (code == 200) {
this.senderForm.fullName = data.userFullName || ''
this.senderForm.mobile = data.userMobile || ''
// 地区名称拼接,参考地址编辑页地区选择
let areaText = ''
try{
if (data.userThreeAdcode) {
const arr = publics.getLngAndLatByCode(data.userThreeAdcode) || []
arr.forEach(v => { areaText = areaText + (v.name || '') })
}
}catch(e){ areaText = '' }
this.senderForm.areaText = areaText
this.senderForm.addressDetails = data.userAddressDetails || ''
this.senderPopupShow = true
}
})
},
// 修改收货人信息:跳转至地址列表,逻辑与订单详情一致
openAddressEdit(parentItem, childItem){
if(!childItem || !childItem.id){
return
}
this.$navigateTo(`/pages/user/address/index?orderId=${childItem.id}&&editAddr=true`)
this.reShow = false;
}
}
}
</script>
<style scoped lang="scss">
.list{
margin: 20rpx;
&-item{
border-radius: 20rpx;
margin-bottom: 20rpx;
padding: 20rpx;
background-color: white;
.store{
.logo{
width: 50rpx;
height: 50rpx;
display: block;
margin-right: 20rpx;
border-radius: 8rpx;
}
}
.goods{
margin-top: 20rpx;
&-img{
position: relative;
margin-right: 20rpx;
width: 140upx;
height: 140upx;
overflow: hidden;
border-radius: 10upx;
.img{
flex-shrink: 0;
display: block;
width: 140upx;
height: 140upx;
}
.tips{
color: #CA0400;
background-color: white;
font-size: 24rpx;
position: absolute;
right: -32rpx;
top: -16rpx;
width: 100rpx;
text-align: center;
height: 60rpx;
line-height: 80rpx;
transform: rotate(45deg);
}
}
.spec{
background-color: #F8F8F8;
color: #bcbbbd;
font-size: 24rpx;
padding: 10rpx;
margin-top: 4rpx;
border-radius: 8rpx;
align-self: flex-start;
}
}
.child-orders{
margin-top: 16rpx;
padding: 16rpx;
background: #FAFAFA;
border-radius: 12rpx;
&__title{
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 12rpx;
}
&__scroller{
max-height: 375rpx;
overflow: hidden;
}
&__content{
display: flex;
flex-direction: row;
}
&__steps{
width: 56rpx;
padding-right: 10rpx;
display: flex;
justify-content: flex-start;
}
&__list{
flex: 1;
.child-card{
background: #FFFFFF;
border-radius: 10rpx;
padding: 16rpx;
margin-bottom: 12rpx;
&__header{ display: flex; align-items: center;}
&__body{ margin-top: 10rpx; }
.badge{
background: #F8D247;
color: #3a2397;
border-radius: 8rpx;
padding: 4rpx 10rpx;
font-size: 22rpx;
margin-right: 10rpx;
}
.consignee {
color: #333333;
border: 1rpx solid #999999;
padding: 0 24rpx;
border-radius: 20rpx;
margin-right: 15rpx;
}
}
}
.btn.mini{
margin-left: 10rpx;
width: auto;
padding: 0 20rpx;
}
}
::v-deep .u-steps-item__wrapper {
background-color: #FAFAFA;
}
.total{
margin: 20rpx 0;
}
.btns{
display: flex;
justify-content: flex-end;
.btn{
margin-left: 10rpx;
width: 160rpx;
height: 60rpx;
text-align: center;
line-height: 60rpx;
display: inline-block;
border-radius: 50rpx;
color: #666666;
border: 2rpx solid #e9eae9;
font-size: 26rpx;
}
.base{
color: #F8D247;
border: 2rpx solid #F8D247;
}
}
}
}
.sender-popup{
padding: 20rpx;
}
.sender-actions{
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
}
.sp-form{ margin-top: 20rpx; }
.sp-row{ height: 80rpx; display: flex; align-items: center; border-bottom: 1upx solid #EEEEEE; }
.sp-label{ width: 25%; display: flex; align-items: center; font-size: 32rpx; color: #676769; }
.sp-value{ flex: 1; font-size: 30rpx; color: #676769; }
</style>