Files
jjx-wap/pages/shop/order.vue
T
b2894lxlx c8e651b7a6 fix: 支付去掉货到付款,样式穿透改为 ::v-deep
支付单改为上传凭证和附件,尾款或全款时提示交易已完结。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 14:20:52 +08:00

785 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template> <!-- 订单列表 -->
<view class="wrap">
<u-sticky offset-top="0" customNavHeight="0">
<uititle title="订单管理"></uititle>
</u-sticky>
<u-subsection :list="subsectionList" :current="subsectionActive" activeColor="#A92924"
@change="sectionChange" fontSize="16"></u-subsection>
<view class="tab-box">
<view class="item" :class="{active: activeName === 'all'}" @click="handTab('all')">全部</view>
<view class="item" :class="{active: activeName === 0}" @click="handTab(0)">待付款</view>
<view class="item" :class="{active: activeName === 1}" @click="handTab(1)">待发货</view>
<view class="item" :class="{active: activeName === 2}" @click="handTab(2)">待收货</view>
<view class="item" :class="{active: activeName === 5}" @click="handTab(5)">待退款</view>
<view class="item" :class="{active: activeName === 4}" @click="handTab(4)">已完成</view>
<view class="item" :class="{active: activeName === 6}" @click="handTab(6)">退款成功</view>
<view class="item" :class="{active: activeName === 7}" @click="handTab(7)">退款失败</view>
</view>
<view class="p-20 flex justify-start items-center">
<view class="mr-15">
<u-icon name="arrow-down" label-pos="left" :label="typeStr" @click="showTypeFilter = true"/>
</view>
<u-search v-model="keyword" placeholder="请输入订单号" @blur="query(1)" @custom="query(1)"
@clear="query(1)"/>
</view>
<view class="list-box">
<!-- 1 -->
<view class="item bg-white" v-for="(item, index) in tableData.rows" :key="index">
<view class="state">
<view class="no">{{ item.orderNo }}</view>
<view class="bage" v-if="item.signMethod !== 1">{{
orderStatusList[item.orderStatus]
}}
</view>
</view>
<view class="mt-15 text-25 text-gray flex justify-start items-center">
<text>{{ orderTypeList[item.type] }}</text>
<text class="text-25 text-gray" v-if="item.type === 1">({{
item.subType === 0 ? '企业' : '产品'
}})
</text>
</view>
<text class=" text-25 text-main" v-if="item.type === 3">(过期时间:{{
item.upgradeExpiredAt
}})
</text>
<text class=" text-25 text-main" v-if="item.type === 8 && item.spot && item.spot.topExpiredAt">过期时间:{{
dayjs(item.spot.topExpiredAt).format('YYYY-MM-DD HH:mm:ss')
}}({{
item.goods[0].goodsId === '-1' ? '全国' : provincesData.find(province => parseInt(province.code) === parseInt(item.goods[0].goodsId)).name
}})
</text>
<view class="mt-15 text-25 text-gray">{{ item._add_time_str }}</view>
<text class="mt-15 text-25 text-main" v-if="item.type > 3 && item.payList">
最新支付状态:{{ item.buyerStatusStr }}
</text>
<text class="mt-15 text-25 text-main" v-if="item.type < 4 && item.orderStatus === 7">
退款拒绝原因:{{ item.refundRefuseReason }}
</text>
<template v-if="item.type === 0">
<view class="p-20 mb-20 flex justify-start items-start" v-for="(goods, goodsIndex) in item.goods"
:key="goodsIndex">
<u-image :src="getGoodsImage(goods)" width="150rpx" height="150rpx" v-if="getGoodsImage(goods)"/>
<view class="flex flex-col justify-start items-start ml-20 text-25">
<text class="font-bold mb-15 text-28">{{ goods.goodsName }}</text>
<text class="text-gray" v-if="item.type === 0 && goods.sku">{{ goods.sku.label }}</text>
<text class=" font-bold text-main" v-if="item.type === 0 && goods.sku">¥{{
goods.sku.values
}}
</text>
<text class=" font-bold flex-1" v-else>¥{{ goods.goodsPrice }}</text>
<text class="">x{{ goods.num }}</text>
</view>
<text class="text-35 font-bold flex-1 text-main" v-if="item.type === 0 && goods.sku">¥{{
parseFloat(goods.sku.values) * goods.num
}}
</text>
<text class="text-35 font-bold flex-1 text-main" v-else>¥{{
parseFloat(goods.goodsPrice) * goods.num
}}
</text>
</view>
</template>
<view class="tool" v-if="item.type < 4 || (item.type > 6 && item.type !== 14)">
<view class="price">
<text class="mr-10" v-if="item.orderStatus !== 10">总金额:</text>
<text v-else class="red mr-10">待报价:</text>
<template v-if="item.orderStatus !== 10">
<text
:class="[parseFloat(item.offAmount) !== parseFloat(item.amount) ? 'text-line-through text-gray': 'red']">
¥{{ item.amount ? parseFloat(item.amount).toFixed(2) : '' }}
</text>
<text class="red"
v-if="parseFloat(item.offAmount) !== parseFloat(item.amount)">
¥{{ item.offAmount }}
</text>
</template>
</view>
<view class="status">
<view class="icon">
<img v-if="item.orderStatus === 0" src="/static/images/order/toBePaid.png"
alt=""/>
</view>
</view>
</view>
<view class="operate">
<template v-if="(item.type > 3 && item.type < 7) || [14, 15].includes(item.type)">
<view class="mx-15">
<u-button plain size="mini" class="btn-buy pointer" @click="showOtherUser(item)">
联系交易方
</u-button>
</view>
<template v-if="item.type > 3 && item.orderStatus < 3 && item.isSeller">
<template
v-if="!item.payList || !['尾款', '货到付款'].includes(item.payList.type) || (['尾款', '货到付款'].includes(item.payList.type) && item.payList.buyerRefuse)">
<view class="mx-15">
<u-button type="primary" size="mini" class="pointer"
@click="makePayList(item)">
创建支付单
</u-button>
</view>
</template>
</template>
<template v-if="item.type > 3 && item.payStatus > 0">
<view class="mx-15">
<u-button type="warning" size="mini" class="pointer"
@click="showPayList(item)">
查看支付单
</u-button>
</view>
</template>
<template v-if="item.contractId">
<view class="mx-15">
<u-button plain size="mini" class="btn-buy pointer"
@click="viewContract(item.contractId)">
查看合同
</u-button>
</view>
</template>
</template>
<template v-if="[0, 1, 2, 3, 7, 8, 9].includes(item.type)">
<view class="mx-15">
<u-button plain size="mini" type="primary"
v-if="[0, 1].includes(item.type)"
@click="showGoods(item)">商品列表
</u-button>
</view>
<template v-if="item.orderStatus === 0">
<view class="mx-15">
<u-button plain size="mini" type="primary" class="btn-close pointer"
@click="navTo(`/pages/shop/pay?id=${item._id}`)"
>去支付
</u-button>
</view>
<view class="mx-15">
<u-button plain size="mini" class="pointer" type="info"
@click="cancel(item)"
>取消订单
</u-button>
</view>
<template v-if="item.orderStatus === 4">
<view class="mx-15">
<u-button plain size="mini"
@click="showServicePhone()">客服电话
</u-button>
</view>
<view class="mx-15">
<u-button plain size="mini"
@click="showServiceQR()">客服二维码
</u-button>
</view>
</template>
</template>
<template v-if="item.orderStatus >= 2 && item.type === 0">
<view class="mx-15">
<u-button plain size="mini" class="btn-buy pointer"
@click="showExpressInfo(item)">
物流信息
</u-button>
</view>
<template v-if="item.isBuyer && item.orderStatus === 2">
<view class="mx-15">
<u-button size="mini" type="success"
@click="confirmExpress(item._id)">
确认收货
</u-button>
</view>
</template>
</template>
<view class="mx-15">
<u-button size="mini" type="success" v-if="item.orderStatus === 11 && item.isSeller"
@click.native="changeOrderStatus(item._id, 3)">已完成
</u-button>
</view>
<view class="mx-15">
<u-button size="mini" type="success" v-if="item.orderStatus === 3 && item.isBuyer"
@click.native="changeOrderStatus(item._id, 4)">确认完成
</u-button>
</view>
</template>
<view v-if="item.orderStatus === 1 && item.isSeller && item.type === 0">
<u-button plain size="mini" type="primary" @click="setExpress(item)">发货</u-button>
</view>
<view class="mx-15"
v-if="([1, 2, 4, 7, 8, 9].includes(item.orderStatus)
&& ((item.type > 3 && item.hasPay && item.isBuyer)
|| (item.type < 3 && [1, 2, 3, 4, 7, 8, 9].includes(item.orderStatus) && item.isBuyer)
))">
<u-button plain size="mini" class="btn-close pointer"
@click="showRefund(item)">申请退款
</u-button>
</view>
</view>
</view>
</view>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="100%">
<make-pay v-if="dialogVisible && clickedInfo && dialogType === 'create'" :info="clickedInfo"
@close="dialogVisible = false;query(1)" :pay-type-list="payTypeList"/>
<pay-list v-else-if="dialogVisible && dialogType === 'pay-list'" :pay-list="payList"
@refresh="payRefresh" @close="dialogVisible = false"/>
<goods v-if="dialogVisible && dialogType === 'goods-list'" :goods="clickedInfo.goods" :order="clickedInfo"
:selfProduct="clickedInfo.selfProduct"/>
<set-express v-if="dialogVisible && dialogType === 'set-express'" :id="clickedInfo._id" :info="clickedInfo"
@close="dialogVisible = false;query(1)"/>
<set-refund v-if="dialogVisible && dialogType === 'set-refund'" :id="clickedInfo._id"
@close="dialogVisible = false;query(1)"/>
<express-info v-if="dialogVisible && dialogType === 'express-info'" :info="clickedInfo"
@close="dialogVisible = false;"/>
</el-dialog>
<contact-modal ref="contactModal"/>
<u-picker :show="showTypeFilter" :columns="[subsectionActive === 0 ? sellerTypeFilter : otherTypeFilter]" keyName="label"
@cancel="showTypeFilter = false" @close="showTypeFilter = false" closeOnClickOverlay
@confirm="confirmType">
<template #more>
<text @click="reset">重置</text>
</template>
</u-picker>
</view>
</template>
<script>
import uititle from "@/components/common/title.vue";
import MakePay from "@/pages/shop/components/makePay.vue";
import PayList from "@/pages/shop/components/payList.vue";
import Goods from "./components/goods.vue";
import SetExpress from "@/pages/shop/components/setExpress.vue";
import SetRefund from "@/pages/shop/components/setRefund.vue";
import ExpressInfo from "@/pages/shop/components/expressInfo.vue";
import dayjs from "dayjs";
import provinces from '@/util/address/provinces.json'
import citys from '@/util/address/citys.json'
import areas from '@/util/address/areas.json'
import ContactModal from "@/components/ContactModal.vue";
const CREATE_PAY_LIST_CONFIRM_CONTENT = `1、您已充分知悉、了解并认可本订单及合同的全部内容以及各方的权利义务;
2、请您按照订单或合同约定创建支付单,否则将存在被驳回的风险。若申请被驳回,请及时沟通处理,完成整改后再次提交。`
export default {
components: {ContactModal, ExpressInfo, SetRefund, SetExpress, Goods, PayList, MakePay, uititle},
data() {
return {
provincesData: provinces,
isLoadMore: false, //默认关闭,请求列表数据超过一屏之后开启
subsectionList: ['我卖出的', '我购买的', '线下交易'],
subsectionActive: 0,
dialogVisible: false,
dialogType: '',
dialogTitle: '',
clickedInfo: null,
activeName: 'all',
value: '',
options: [{
value: '自主交易',
label: '自主交易'
}],
pageIndex: 1,
menuIndex: '0',
formData: {
status: null,
tradeMethod: null
},
tableData: {
total: 0,
rows: []
},
pageSize: 500,
orderType: null,
orderTypeList: [
'商城订单',
'金久信自营增值服务',
'保证金',
'会员升级',
'询价订单',
'加工订单',
'旧货订单',
'报价次数',
'现货广告',
'旧货加急',
'旧货次数',
'金久信学堂下载次数',
'合同下载',
'金久信签约宝次数',
'金久信签约宝',
'金久信自营询价',
],
orderStatusList: [
'待支付',
'待发货',
'待收货',
'待确认',
'已完成',
'退款审核中',
'退款成功',
'退款拒绝',
'',
'',
'待报价',
'已派单待完成',
],
payList: [],
keyword: '',
orderPayList: [],
payInfo: {},
payTypeList: ['预付款', '进度款', '尾款', '全款'],
topExpiredAt: '',
typeStr: '全部类型',
showTypeFilter: false,
sellerTypeFilter: [
{
label: '商城订单',
value: 0
},
{
label: '询价订单',
value: 4
},
{
label: '加工订单',
value: 5
},
{
label: '旧货交易订单',
value: 6
},
],
otherTypeFilter: [
{
label: '商城订单',
value: 0
},
{
label: '询价订单',
value: 4
},
{
label: '加工订单',
value: 5
},
{
label: '旧货交易订单',
value: 6
},
],
};
},
methods: {
dayjs,
getGoodsImage(goods) {
if (!goods) return ''
if (goods.sku && goods.sku.image) return goods.sku.image
if (goods.goods && goods.goods.cover) return goods.goods.cover
return ''
},
sectionChange(index) {
this.subsectionActive = index
this.query()
},
handTab(index) {
this.activeName = index
this.query()
},
async query(pageIndex = 1, resetFilter = false, activeName = null) {
if (resetFilter) {
this.keyword = null
this.orderType = null
}
if (activeName) this.activeName = activeName
this.tableData = await vk.callFunction({
url: 'client/order/kh/getList',
title: '请求中...',
data: {
pageIndex,
pageSize: this.pageSize,
keyword: this.keyword,
isSeller: this.subsectionActive === 0 ? 1 : 0,
type: this.orderType,
signMethod: this.subsectionActive === 2 ? 1 : null,
orderStatus: this.activeName === 'all ' ? null : parseInt(this.activeName),
},
});
this.tableData.rows.map(item => {
item.buyerStatusStr = ''
if (item.type > 3 && item.payList) {
item.buyerStatusStr = `${item.payList.type}-`
if (item.payList.buyerRefuse) {
item.buyerStatusStr += `买家已拒绝:${item.payList.refuseReason}。`
if (item.isSeller) item.buyerStatusStr += '请等待卖家重新创建支付单'
else item.buyerStatusStr += '请重新创建支付单'
} else {
if (item.payList.hasPaid) {
if (item.payList.sellerConfirm) item.buyerStatusStr += '卖家已确认收款'
else item.buyerStatusStr += '买家已支付,待卖家确认收款'
} else item.buyerStatusStr += '买家未支付'
}
}
return item
})
},
reset() {
this.orderType = null
this.typeStr = '全部类型'
this.keyword = null
this.showTypeFilter = false
this.query(1)
},
contact(item) {
try {
const mobile = item.isBuyer ? item.seller.mobile : item.buyer.mobile
uni.showModal({
title: '',
content: `交易方联系方式:${mobile}`,
showCancel: false
})
} catch (e) {
uni.showModal({
title: '',
content: `交易方未留下联系方式`,
showCancel: false
})
}
},
async makePayList(item) {
const confirmed = await new Promise(resolve => {
uni.showModal({
title: '创建支付单', content: CREATE_PAY_LIST_CONFIRM_CONTENT,
confirmText: '我已知晓', cancelText: '取消', showCancel: true,
success: res => resolve(res.confirm), fail: () => resolve(false)
})
})
if (!confirmed) return
const {rows} = await vk.callFunction({
url: 'client/order/kh/orderPayList',
title: '请求中...',
data: {
id: item._id
},
});
if (rows.length > 0 && !rows[rows.length - 1].buyerRefuse && !rows[rows.length - 1].sellerConfirm) {
return this.$message.error('还有未确认的支付单,无法继续创建。请到支付单中查看并确认收款')
}
this.orderPayList = rows
this.clickedInfo = item
this.dialogType = 'create'
this.dialogTitle = '创建支付单'
this.dialogVisible = true
},
async showPayList(item) {
this.clickedInfo = item
await this.getPayList()
this.dialogVisible = true
this.dialogTitle = '查看支付单'
this.dialogType = 'pay-list'
},
async getPayList() {
const {rows} = await vk.callFunction({
url: 'client/order/kh/orderPayList',
title: '请求中...',
data: {id: this.clickedInfo._id}
})
this.payList = rows
},
viewContract(contractId) {
vk.callFunction({
url: 'client/contract/kh/viewContract',
title: '请求中...',
data: {
contractId
},
success: (res) => {
// window.open(res.data)
window.location.href = res.data
}
});
},
navTo(url) {
vk.navigateTo({
url: url
})
},
async buyerRefuse(index) {
this.payList[index].buyerRefuse = true
await vk.callFunction({
url: 'client/order/kh/payRefuse',
title: '请求中...',
data: {
id: this.payList[index]._id
}
})
},
showGoods(item) {
this.clickedInfo = item;
this.dialogTitle = '商品列表'
this.dialogType = 'goods-list';
this.dialogVisible = true
},
showRefund(item) {
this.clickedInfo = item;
this.dialogTitle = '退款申请'
this.dialogType = 'set-refund';
this.dialogVisible = true
},
setExpress(item) {
this.clickedInfo = item;
this.dialogTitle = '发货'
this.dialogType = 'set-express';
this.dialogVisible = true
},
showExpressInfo(item) {
this.clickedInfo = item;
this.dialogTitle = '发货详情'
this.dialogType = 'express-info';
this.dialogVisible = true
},
confirmExpress(id) {
uni.showModal({
title: '提示',
content: '只确认数量和金额,质保按双方合同约定内容执行',
success: async res => {
if (res.confirm) {
await vk.callFunction({
url: 'client/order/kh/changeOrderStatus',
title: '请求中...',
data: {
id,
orderStatus: 4
}
})
this.activeName = '4'
await this.query(1)
}
}
})
},
payRefresh(menuIndex) {
this.getPayList()
this.query(1, null, menuIndex)
},
async cancel({_id}) {
uni.showModal({
title: '提示',
content: '确认取消订单吗?',
success: async res => {
if (res.confirm) {
await vk.callFunction({
url: 'client/order/kh/changeOrderStatus',
title: '请求中...',
data: {
id: _id,
orderStatus: -1
}
})
await this.query(1)
}
}
})
},
async showServicePhone() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: '657d8fca55b337cfadbe1063'
}
})
uni.showModal({
title: '平台客服',
content: res.content,
showCancel: false
})
},
async showServiceQR() {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {
id: '657d9025e0ec19b5040feb40'
}
})
const h = this.$createElement;
this.$confirm(`<image src="${res.content}" style="width: 100px;height: 100px;"/>`, '平台客服二维码', {
cancelButtonText: '关闭',
showConfirmButton: false,
dangerouslyUseHTMLString: true
})
},
async changeOrderStatus(id, orderStatus) {
await vk.callFunction({
url: 'client/order/kh/changeOrderStatus',
data: {
id,
orderStatus
}
})
await this.query(1)
},
showOtherUser(item) {
const mine = item.isBuyer ? item.buyer : item.seller
const otherUser = item.isBuyer ? item.seller : item.buyer
const mineName = mine.accountType === 2 ? mine.company.companyName : mine.person.personName
const otherName = otherUser.accountType === 2 ? otherUser.company.companyName : otherUser.person.personName
this.$refs.contactModal.open(otherUser.businessCard.companyMobile, mine.businessCard.companyMobile, otherName, mineName)
},
confirmType({value}) {
this.orderType = value[0].value
this.typeStr = value[0].label
this.query(1)
this.showTypeFilter = false
}
},
onLoad(option) {
if (option.menuIndex) this.subsectionActive = parseInt(option.menuIndex)
if (option.keyword) this.keyword = option.keyword
this.query()
},
/*上拉加载更多*/
onReachBottom() {
// uni.showToast({title: '加载更多……', icon: 'none'})
// /*防止重复请求*/
// if (!this.isLoadMore) {
// this.isLoadMore = true
// }
}
}
</script>
<style lang="scss">
::v-deep .u-button--mini {
padding: 0 1px !important;
}
.tab-box {
display: flex;
justify-content: space-between;
margin: 0 30rpx;
border-bottom: 1px solid #e5e5e5;
font-size: 26rpx;
line-height: 88rpx;
color: #2d2d2d;
overflow-x: scroll;
.active {
position: relative;
color: #A92924;
}
.active::after {
position: absolute;
left: 50%;
bottom: -3px;
z-index: 9;
margin-left: -34rpx;
display: block;
content: '';
width: 68rpx;
height: 4px;
border-radius: 2px;
background-color: #A92924;
}
.item {
flex-shrink: 0;
margin-right: 20rpx;
}
}
.list-box {
padding: 10rpx 30rpx;
.item {
margin-top: 30rpx;
padding: 16rpx 30rpx 10rpx;
// border: 1px solid #e3e3e3;
border-radius: 20rpx;
box-shadow: 0 0 3px 3px #e3e3e3;
.info-box {
display: flex;
padding: 16rpx 0;
.img {
width: 180rpx;
height: 106rpx;
border-radius: 10rpx;
overflow: hidden;
}
.info {
flex: 1;
flex-wrap: wrap;
padding-left: 20rpx;
.title {
width: 100%;
height: 36rpx;
font-size: 28rpx;
line-height: 36rpx;
color: #2a2a2a;
overflow: hidden;
}
.type {
font-size: 24rpx;
line-height: 32rpx;
color: #A92924;
}
.code {
font-size: 24rpx;
line-height: 32rpx;
color: #d7d7d7;
}
}
}
.state {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
line-height: 30rpx;
font-weight: bold;
.no {
color: #222222;
}
.bage {
font-size: 25rpx;
color: #ff9600;
}
}
.operate {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 20rpx 0;
font-size: 24rpx;
line-height: 48rpx;
color: #2a2a2a;
.link {
margin-left: 20rpx;
// width: 156rpx;
height: 48rpx;
padding: 0 12rpx;
text-align: center;
border-radius: 12rpx;
}
.link-1 {
color: #FFFFFF;
border: 1px solid #A92924;
background-color: #A92924;
}
.link-2 {
color: #999999;
border: 1px solid #999999;
background-color: #FFFFFF;
}
}
}
}
</style>