feat(订单): 添加退款功能及相关页面

- 新增退款申请功能,用户可申请订单内所有商品退款
- 更新订单状态显示,增加“已退款”和“退款中”状态
- 新增退款去向页面,展示退款信息
- 更新依赖版本至v1.0.13
- 提升用户体验,优化多个页面的文本提示和样式
- 更新应用版本至2.0.49
This commit is contained in:
2026-05-09 11:12:34 +08:00
parent a38187c13f
commit 6274ac6cd6
15 changed files with 339 additions and 49 deletions

View File

@@ -5,6 +5,7 @@ if (process.env.NODE_ENV === 'development') {
// baseUrl = "https://testapi.nuttyreading.com/"; // 线上测试
baseUrl = "https://api.nuttyreading.com/"; //线上正式'
// baseUrl = "http://192.168.110.100:9200/pb/"; //张川川
// baseUrl = "http://192.168.110.131:9200/pb/"; // 王亚男
} else if (process.env.NODE_ENV === 'production') {
// 生产环境11
baseUrl = "https://api.nuttyreading.com/"; //线上正式

View File

@@ -92,6 +92,9 @@ Vue.component('common-order-submit', commonOrderSubmit);
import CommonCourseVideo from 'edu-core/components/course-video'
Vue.component('CommonCourseVideo', CommonCourseVideo);
import CommonRefundDestination from 'edu-core/components/order/refund-destination.vue'
Vue.component('common-refund-destination', CommonRefundDestination);
import commonCoupon from '@/pages/component/commonComponents/coupon/index.vue'
Vue.component('common-coupon', commonCoupon);
import commonGoodsList from '@/pages/component/commonComponents/goodsList.vue'

View File

@@ -12,8 +12,8 @@
"src" : "图片路径"
}
],
"versionName" : "2.0.48",
"versionCode" : 2048,
"versionName" : "2.0.49",
"versionCode" : 2049,
"sassImplementationName" : "node-sass",
"app-plus" : {
"nvueCompiler" : "uni-app",

16
package-lock.json generated
View File

@@ -11,7 +11,7 @@
"dependencies": {
"animate.css": "^4.1.1",
"e-peanut": "file:",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.12",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13",
"element-plus": "^2.9.6",
"epubjs": "^0.3.93",
"jquery": "^2.2.4",
@@ -435,8 +435,8 @@
"link": true
},
"node_modules/edu-core": {
"version": "1.0.12",
"resolved": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#ea1dca213de69ac5a01a44a352ab33024edd2577",
"version": "1.0.13",
"resolved": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#b5ece8b8abfeba98428f6e2191efae3312b5036f",
"license": "ISC"
},
"node_modules/element-plus": {
@@ -4173,7 +4173,7 @@
"requires": {
"animate.css": "^4.1.1",
"e-peanut": "file:",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.12",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13",
"element-plus": "^2.9.6",
"epubjs": "^0.3.93",
"jquery": "^2.2.4",
@@ -4494,8 +4494,8 @@
}
},
"edu-core": {
"version": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#ea1dca213de69ac5a01a44a352ab33024edd2577",
"from": "edu-core@git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.12"
"version": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#b5ece8b8abfeba98428f6e2191efae3312b5036f",
"from": "edu-core@git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13"
},
"element-plus": {
"version": "2.11.5",
@@ -7256,8 +7256,8 @@
}
},
"edu-core": {
"version": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#ea1dca213de69ac5a01a44a352ab33024edd2577",
"from": "edu-core@git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.12"
"version": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#b5ece8b8abfeba98428f6e2191efae3312b5036f",
"from": "edu-core@git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13"
},
"element-plus": {
"version": "2.11.5",

View File

@@ -7,7 +7,7 @@
"dependencies": {
"animate.css": "^4.1.1",
"e-peanut": "file:",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.12",
"edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13",
"element-plus": "^2.9.6",
"epubjs": "^0.3.93",
"jquery": "^2.2.4",

View File

@@ -170,6 +170,18 @@
}
}
},
{
"path": "pages/bookShop/refundDestination",
"style": {
"navigationBarTitleText": "钱款去向",
"enablePullDownRefresh": false,
"app-plus": {
"bounce": "none",
"titleNView": false,
"popGesture": "none"
}
}
},
{
"path": "pages/hufen/hufen",
"style": {

View File

@@ -90,6 +90,16 @@
v-if="orderContet.orderStatus == 5"
>已超时</text
>
<text
class="orderState orderState6"
v-if="orderContet.orderStatus == 6"
>已退款</text
>
<text
class="orderState orderState7"
v-if="orderContet.orderStatus == 7"
>退款中</text
>
</view>
<view
class="orderContent"
@@ -775,6 +785,8 @@ export default {
this.OverOrder();
} else if (data.text == "查看物流") {
this.seeExpressDetail(this.orderContet);
} else if (data.text == "申请退款") {
this.confirmApplyRefund();
}
},
async clickIcon(data) {
@@ -783,6 +795,53 @@ export default {
this.kefu();
}
},
confirmApplyRefund() {
uni.showModal({
title: "提示",
content: "确认申请订单内所有商品退款吗?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
this.submitOrderRefund();
}
},
});
},
submitOrderRefund() {
if (!this.orderContet || !this.orderContet.orderId) return;
this.$http
.request({
url: "book/buyOrder/refundOrder",
method: "POST",
data: {
order_id: this.orderContet.orderId,
},
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (res.code == 0) {
uni.showToast({
icon: "none",
title: "退款成功",
});
this.getOrderList();
return;
}
uni.showToast({
icon: "none",
title: (res && (res.msg || res.errMsg)) || "退款失败",
});
})
.catch((e) => {
uni.showToast({
icon: "none",
title: (e && (e.msg || e.errMsg)) || "退款失败",
});
});
},
// 复制到剪切板
copyData(data) {
uni.setClipboardData({
@@ -1188,10 +1247,19 @@ export default {
text: "继续付款",
});
}
if (this.orderContet.orderStatus == 0) {
// medicine注释取消订单按钮
// if (this.orderContet.orderStatus == 0) {
// this.customButton.push({
// width: "160rpx",
// text: "取消订单",
// color: "#333",
// backgroundColor: "#f0f0f0",
// });
// }
if (this.orderContet.refundableStatus === true) {
this.customButton.push({
width: "160rpx",
text: "取消订单",
text: "申请退款",
color: "#333",
backgroundColor: "#f0f0f0",
});
@@ -1214,6 +1282,10 @@ export default {
this.titleStat = "待收到";
} else if (this.orderContet.orderStatus == 3) {
this.titleStat = "已完成";
} else if (this.orderContet.orderStatus == 6) {
this.titleStat = "已退款";
} else if (this.orderContet.orderStatus == 7) {
this.titleStat = "退款中";
}
if (
this.orderContet.orderStatus >= 2 &&
@@ -1256,10 +1328,28 @@ export default {
},
});
},
checkPayTimeout(payItem) {
const createTime = payItem && payItem.createTime;
if (!createTime) return true;
const createdAt = new Date(String(createTime).replace(/-/g, "/")).getTime();
if (!createdAt) return true;
const expired = Date.now() - createdAt > 10 * 60 * 1000;
if (expired) {
uni.showModal({
title: "提示",
content: "订单已超时,不能继续支付,请重新下单",
confirmText: "知道了",
showCancel: false,
});
return false;
}
return true;
},
// 支付
goPay(payItem) {
console.log(payItem, "订单数据");
if (!this.checkPayTimeout(payItem)) return;
if (payItem.paymentMethod == 2) {
console.log("阿里支付");
setPay(
@@ -1403,6 +1493,12 @@ export default {
.orderState5 {
background-color: #787878;
}
.orderState6 {
background-color: #f56c6c;
}
.orderState7 {
background-color: #f56c6c;
}
.guoqi {
font-size: 28rpx;
align-items: center;

View File

@@ -12,7 +12,7 @@
</z-nav-bar>
<view class="cateList flexbox">
<common-sticky
itemStyle="width:20%; height: 68rpx;font-size:24rpx;"
itemStyle="width:auto; padding: 0 20rpx; height: 68rpx;font-size:22rpx;"
:list="ordersTabs"
label="name"
:currentCateIndex="currentCateIndex"
@@ -111,6 +111,16 @@
v-show="slotProps.row.orderStatus == 5"
>已过期</text
>
<text
class="orderstatus"
v-show="slotProps.row.orderStatus == 6"
>已退款</text
>
<text
class="orderstatus"
v-show="slotProps.row.orderStatus == 7"
>退款中</text
>
</view>
</view>
<view v-if="slotProps.row.orderType == 'trainingClass'" class="bookinfolist">
@@ -392,17 +402,12 @@
</view>
<view
class="operation_box boxShadow"
v-if="slotProps.row.isShowMore == true"
class="btns flexbox"
:class="{ 'btns-no-more': !hasMoreActions(slotProps.row) }"
style="margin-top: 10rpx"
>
<view
v-if="slotProps.row.orderStatus == 0"
@click.native.stop="canceOrder(slotProps.row)"
>取消订单</view
>
</view>
<view class="btns flexbox" style="margin-top: 10rpx">
<view
v-if="hasMoreActions(slotProps.row)"
class="left"
style="color: #c0c4cc"
@click.native.stop="
@@ -442,6 +447,21 @@
v-if="slotProps.row.orderStatus == 3"
>申请售后</view
>
<view
class="orderstatusbtn"
v-if="
slotProps.row.orderStatus == 6 ||
slotProps.row.orderStatus == 7
"
@click.stop="goRefundDestination(slotProps.row)"
>钱款去向</view
>
<view
class="orderstatusbtn"
v-if="slotProps.row.refundableStatus === true"
@click.stop="confirmApplyRefund(slotProps.row)"
>申请退款</view
>
<!-- <view
class="orderstatusbtn"
v-if="item.orderStatus == 3 && userRecordid == null"
@@ -615,12 +635,7 @@ export default {
come: "2",
isShowTab: false,
isLoadingHide: false,
moreList: [
{
name: "取消订单",
key: "false",
},
],
moreList: [],
currentCateIndex: 0,
pagination: {
// 请求参数
@@ -671,6 +686,16 @@ export default {
value: 3,
badge: {},
},
{
name: "已退款",
value: 6,
badge: {},
},
{
name: "退款中",
value: 7,
badge: {},
},
],
selectOrderInfo: {},
ordersListTab: 1,
@@ -742,15 +767,26 @@ export default {
},
selectClick(index) {
console.log("index at line 609:", index);
if (index.key == "false") {
if (index.key == "cancelOrder") {
this.isShowMore = false;
this.canceOrder(this.selectOrderInfo);
}
},
openMore(row, index) {
const actions = this.getMoreActions(row);
if (!actions.length) return;
this.moreList = actions;
this.selectOrderInfo = row;
this.isShowMore = true;
},
hasMoreActions(row) {
return this.getMoreActions(row).length > 0;
},
getMoreActions(row) {
const actions = [];
// 取消订单功能已屏蔽,更多操作为空时不展示“更多”按钮
return actions;
},
handleCopy(value, title) {
this.$commonJS.handleCopy(value, title);
},
@@ -813,6 +849,60 @@ export default {
val.orderSn,
});
},
goRefundDestination(row) {
uni.navigateTo({
url: "/pages/bookShop/refundDestination?orderId=" + row.orderId,
});
},
confirmApplyRefund(row) {
uni.showModal({
title: "提示",
content: "确认申请订单内所有商品退款吗?",
confirmText: "确认",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
this.submitOrderRefund(row);
}
},
});
},
submitOrderRefund(orderRow) {
if (!orderRow || !orderRow.orderId) return;
this.$http
.request({
url: "book/buyOrder/refundOrder",
method: "POST",
data: {
orderId: orderRow.orderId,
},
header: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (res.code == 0) {
uni.showToast({
icon: "none",
title: "退款成功",
});
this.pagination.page = 1;
this.newList = [];
this.getBookList(this.ordersListTab, false);
return;
}
uni.showToast({
icon: "none",
title: (res && (res.msg || res.errMsg)) || "退款失败",
});
})
.catch((e) => {
uni.showToast({
icon: "none",
title: (e && (e.msg || e.errMsg)) || "退款失败",
});
});
},
getBookList(flag, refreshflag) {
this.isLoadingHide = false;
var that = this;
@@ -843,6 +933,8 @@ export default {
// * 3已完成
// * 4: 交易失败
// * 5: 已过期
// * 6: 已退款
// * 7: 退款中
console.log("res at line 757:", res);
that.map = res.data;
@@ -872,7 +964,7 @@ export default {
var params = {
userId: this.userInfo.id,
come: this.come,
orderStatus: flag == -1 ? "" : flag, //传null为全部订单状态 0-未付款 1-待发出 2-待收到 3-交易成功 4-交易失败 5-过期
orderStatus: flag == -1 ? "" : flag, //传null为全部订单状态 0-未付款 1-待发出 2-待收到 3-交易成功 4-交易失败 5-过期 6-已退款 7-退款中
...this.pagination,
// limit: 10,
// page: this.newestpage,
@@ -908,9 +1000,27 @@ export default {
console.log(e);
});
},
checkPayTimeout(payItem) {
const createTime = payItem && payItem.createTime;
if (!createTime) return true;
const createdAt = new Date(String(createTime).replace(/-/g, "/")).getTime();
if (!createdAt) return true;
const expired = Date.now() - createdAt > 10 * 60 * 1000;
if (expired) {
uni.showModal({
title: "提示",
content: "订单已超时,不能继续支付,请重新下单",
confirmText: "知道了",
showCancel: false,
});
return false;
}
return true;
},
// 支付
goPay(payItem) {
console.log(payItem, "订单数据");
if (!this.checkPayTimeout(payItem)) return;
if (payItem.paymentMethod == 2) {
console.log("阿里支付");
setPay(
@@ -1542,6 +1652,15 @@ export default {
}
}
}
.btns.btns-no-more {
justify-content: flex-end;
.right {
width: auto;
max-width: 100%;
}
}
}
.mb30 {
@@ -1584,7 +1703,7 @@ export default {
}
/deep/.u-tabs__wrapper__nav__item {
padding: 0 !important;
// padding: 0 !important;
}
.commonDetailPage {

View File

@@ -0,0 +1,27 @@
<template>
<view class="page-wrap">
<public-module></public-module>
<common-refund-destination :order-id="orderId" :http="$http" />
</view>
</template>
<script>
export default {
data() {
return {
orderId: '',
}
},
onLoad(options) {
if (options && options.orderId != null) {
this.orderId = options.orderId
}
},
}
</script>
<style scoped>
.page-wrap {
min-height: 100vh;
}
</style>

View File

@@ -249,9 +249,9 @@ export default {
gotoDetail(v) {
this.$emit("hancleClick", v);
},
onHandleClickBuy() {
onHandleClickBuy(e) {
this.$emit("selectGoodsData", this.selectGoodsData);
this.$emit("onHandleClickBuy");
this.$emit("onHandleClickBuy", e);
},
},
onBackPress() {
@@ -430,4 +430,8 @@ export default {
font-weight: 700;
}
}
.title_list {
margin-bottom: 20rpx;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<!-- <scroll-view class="scroll-view_H cateList" scroll-x="true" scroll-left="0"> -->
<view class="" style="background-color: #fff; width: 100%">
<view class="" style="background-color: #fff; width: 100%; padding: 0 20rpx;">
<u-tabs
lineWidth="30"
lineColor="#258feb"

View File

@@ -790,8 +790,13 @@ export default {
this.$refs.commonSelectGoods.open();
},
//点击下单按钮
onHandleClickBuy() {
if(this.buttonType==0){ //如果是加入购物车
onHandleClickBuy(e) {
if (e && e.content && e.content.text === "购物车") {
this.onHandleClick();
return;
}
const clickType = typeof (e && e.index) === "number" ? e.index : this.buttonType;
if(clickType==0){ //如果是加入购物车
console.log('剩余', this.selectGoodsData.productStock)
if(this.selectGoodsData.productStock==0){
uni.showToast({

View File

@@ -71,17 +71,19 @@
>
<template slot="labelSlot" slot-scope="slotProps">
<view class="label_content AC_List">
<view class="left">
<view class="title" v-if="slotProps.row.productName">{{ slotProps.row.productName }}</view>
<view class="title" v-else>{{ slotProps.row.orderType }}</view>
</view>
<view
:class="`right ${
slotProps.row.changeAmount > 0 ? 'Hot' : ''
}`"
>
<text v-if="slotProps.row.changeAmount > 0">+</text>
<text>{{ slotProps.row.changeAmount }}</text> </view>
<view style=" display: flex; align-items: center; justify-content: space-between;">
<view class="left">
<view class="title">{{ slotProps.row.productName || slotProps.row.orderType}}</view>
</view>
<view
:class="`right ${
slotProps.row.changeAmount > 0 ? 'Hot' : ''
}`"
>
<text v-if="slotProps.row.changeAmount > 0">+</text>
<text>{{ slotProps.row.changeAmount }}</text>
</view>
</view>
<view class="AC_mark" v-if="slotProps.row.remark">{{slotProps.row.remark}}</view>
<view class="AC_note" v-if="slotProps.row.note&&slotProps.row.note!='null'">说明{{slotProps.row.note}}</view>
<view class="AC_time">{{ slotProps.row.createTime }}</view>
@@ -339,6 +341,12 @@ export default {
.AC_List {
overflow: hidden;
.title,
.AC_note,
.AC_mark {
word-break: break-word;
overflow-wrap: break-word;
}
.left {
width: calc(100% - 120rpx) !important;
font-weight: 700;
@@ -355,6 +363,19 @@ export default {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
> view:first-child {
.left {
flex: 1;
min-width: 0;
width: auto !important;
float: none;
}
.right {
flex-shrink: 0;
float: none;
width: auto !important;
}
}
.AC_title {

View File

@@ -389,4 +389,6 @@ button::after {
}
}
uni-text {
white-space: normal;
}

Binary file not shown.