From a045b395808e04e76573d2e09c5107dc2127f552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=9D=B0?= Date: Tue, 16 Sep 2025 14:34:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=A2=E5=8D=95=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Auth/index.tsx | 19 +- src/components/index.ts | 2 + src/components/refundPopup/index.module.scss | 132 ++++++++ src/components/refundPopup/index.tsx | 139 ++++++++ src/order_pages/orderDetail/index.module.scss | 71 ++++ src/order_pages/orderDetail/index.tsx | 251 +++++++++++++-- src/order_pages/orderList/index.module.scss | 68 ---- src/order_pages/orderList/index.tsx | 302 +++++++----------- src/services/orderService.ts | 6 + src/static/order/orderListClose.svg | 4 + src/utils/index.ts | 2 + src/utils/orderActions.ts | 68 ++++ src/utils/routeUtil.ts | 23 ++ 13 files changed, 799 insertions(+), 288 deletions(-) create mode 100644 src/components/refundPopup/index.module.scss create mode 100644 src/components/refundPopup/index.tsx create mode 100644 src/static/order/orderListClose.svg create mode 100644 src/utils/orderActions.ts create mode 100644 src/utils/routeUtil.ts diff --git a/src/components/Auth/index.tsx b/src/components/Auth/index.tsx index 9044f41..6cbf848 100644 --- a/src/components/Auth/index.tsx +++ b/src/components/Auth/index.tsx @@ -1,26 +1,9 @@ import React, { useEffect, useState } from "react"; import { View } from "@tarojs/components"; import Taro from "@tarojs/taro"; +import { getCurrentFullPath } from '@/utils'; import { check_login_status } from "@/services/loginService"; -export function getCurrentFullPath(): string { - const pages = Taro.getCurrentPages(); - const currentPage = pages.at(-1); - - if (currentPage) { - console.log(currentPage, "currentPage get"); - const route = currentPage.route; - const options = currentPage.options || {}; - - const query = Object.keys(options) - .map((key) => `${key}=${options[key]}`) - .join("&"); - - return query ? `/${route}?${query}` : `/${route}`; - } - return ""; -} - export default function withAuth

( WrappedComponent: React.ComponentType

, ) { diff --git a/src/components/index.ts b/src/components/index.ts index 2918423..abc1c4a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,6 +16,7 @@ import EditModal from "./EditModal/index"; import withAuth from "./Auth"; import { CustomPicker, PopupPicker } from "./Picker"; import NTRPEvaluatePopup from "./NTRPEvaluatePopup"; +import RefundPopup from "./refundPopup"; export { ActivityTypeSwitch, @@ -37,4 +38,5 @@ export { CustomPicker, PopupPicker, NTRPEvaluatePopup, + RefundPopup, }; diff --git a/src/components/refundPopup/index.module.scss b/src/components/refundPopup/index.module.scss new file mode 100644 index 0000000..adbfc5a --- /dev/null +++ b/src/components/refundPopup/index.module.scss @@ -0,0 +1,132 @@ +.refundPolicy { + padding-top: 20px; + // .moduleTitle { + // display: flex; + // padding: 15px 0 8px; + // justify-content: space-between; + // align-items: center; + // align-self: stretch; + // color: #000; + // font-feature-settings: + // "liga" off, + // "clig" off; + // font-family: "PingFang SC"; + // font-size: 14px; + // font-style: normal; + // font-weight: 600; + // line-height: 20px; + // letter-spacing: -0.23px; + // } + + .specTips { + padding-bottom: 20px; + color: rgba(60, 60, 67, 0.60); + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 18px; + } + + .policyList { + border-radius: 12px; + border: 1px solid rgba(0, 0, 0, 0.06); + background: #fff; + box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06); + + .policyItem { + display: flex; + justify-content: space-around; + align-items: center; + color: #000; + text-align: center; + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 20px; + border-top: 1px solid rgba(0, 0, 0, 0.06); + + &:nth-child(1) { + color: #000; + text-align: center; + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 20px; + border: none; + } + + .time, + .rule { + width: 50%; + padding: 10px 12px; + } + + .rule { + border-left: 1px solid rgba(0, 0, 0, 0.06); + } + } + } +} + +.container { + padding: 0 15px 40px; + + .header { + padding: 24px 15px 0; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + + .title { + color: #000; + text-align: center; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + margin-left: auto; + } + + .closeIcon { + margin-left: auto; + width: 20px; + height: 20px; + } + } + + .action { + display: flex; + align-items: center; + justify-content: center; + margin-top: 20px; + padding: 2px 6px; + height: 52px; + border-radius: 16px; + border: 1px solid rgba(0, 0, 0, 0.06); + box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.10); + backdrop-filter: blur(16px); + color: #fff; + background-color: #000; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.23px; + } +} \ No newline at end of file diff --git a/src/components/refundPopup/index.tsx b/src/components/refundPopup/index.tsx new file mode 100644 index 0000000..b7354d9 --- /dev/null +++ b/src/components/refundPopup/index.tsx @@ -0,0 +1,139 @@ +import React, { useState, useRef, forwardRef, useImperativeHandle } from 'react'; +import { View, Text, Button, Image } from '@tarojs/components' +import Taro from '@tarojs/taro'; +import dayjs from 'dayjs' +import { CommonPopup } from '@/components'; +import orderService from '@/services/orderService'; +import styles from './index.module.scss' +import closeIcon from '@/static/order/orderListClose.svg' + +function genRefundNotice (refund_policy) { + if (refund_policy.length === 0) { + return {} + } + const now = dayjs() + const deadlines = refund_policy.map(item => dayjs(item.deadline_formatted)) + let matchPolicyIndex = deadlines.findIndex(d => d.isAfter(now)) + if (matchPolicyIndex === -1) { + matchPolicyIndex = refund_policy.length - 1 + } + const { deadline_formatted, price, refund_rate } = refund_policy[matchPolicyIndex] + if (refund_rate === 1) { + return { refundPrice: price, notice: `本次可全额退款, ¥${price} 将原路退回,请查收` } + } else if (refund_rate === 0) { + return { refundPrice: 0, notice: `当前退出不可退款,后续流程未明确,@麻真瑜` } + } + const refundPrice = price * refund_rate + const leftHours = dayjs(deadline_formatted).diff(dayjs(), 'hour') + return { refundPrice, notice: `距活动开始已不足${leftHours}h,当前退出您需扣除${price - refundPrice}元` } +} + +function renderCancelContent(checkOrderInfo) { + const { refund_policy = [] } = checkOrderInfo; + const policyList = [ + { + time: "申请退款时间", + rule: "退款规则", + }, + ...refund_policy.map((item) => { + return { + time: item.application_time, + rule: item.refund_rule, + }; + }), + ]; + const { notice } = genRefundNotice(refund_policy) + return ( + + {/* + 退款政策 + */} + {{notice}} + {/* 订单信息摘要 */} + + {policyList.map((item, index) => ( + + {item.time} + {item.rule} + + ))} + + + ); +} + +export type RefundRef = { + show: (item: any, callback: (result: boolean) => void) => void +} + +export default forwardRef(function RefundPopup(_props, ref) { + const [visible, setVisible] = useState(false) + const [checkOrderInfo, setCheckOrderInfo] = useState({}) + const [orderData, setOrderData] = useState({}) + const onDown = useRef<((result: boolean) => void) | null>(null) + + useImperativeHandle(ref, () => ({ + show: onShow, + })) + + async function onShow (orderItem, onFinish: (result: boolean) => void) { + const { + game_info, + } = orderItem + onDown.current = onFinish + setOrderData(orderItem) + const res = await orderService.getCheckOrderInfo(game_info.id); + setCheckOrderInfo(res.data); + setVisible(true) + } + + function onClose () { + setVisible(false) + onDown.current?.(false) + } + + async function handleConfirmQuit () { + const { order_no, amount } = orderData + try { + const refundRes = await orderService.applicateRefund({ + order_no, + refund_amount: amount, + refund_reason: "用户主动退款", + }); + if (refundRes.code !== 0) { + throw new Error(refundRes.message); + } + Taro.showToast({ + title: "退出成功", + icon: "none", + }) + onDown.current?.(true) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + onClose() + } + } + return ( + + + + 退出活动 + + + {renderCancelContent(checkOrderInfo)} + + + + ) +}) \ No newline at end of file diff --git a/src/order_pages/orderDetail/index.module.scss b/src/order_pages/orderDetail/index.module.scss index d7aed40..3c518c1 100644 --- a/src/order_pages/orderDetail/index.module.scss +++ b/src/order_pages/orderDetail/index.module.scss @@ -229,7 +229,34 @@ .gameInfoActions { min-height: 12px; + padding: 0 12px; border-top: 0.5px solid rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: flex-start; + gap: 10px; + + & > .button { + margin: 12px 0; + padding: 4px 10px; + height: 28px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 12px; + font-style: normal; + font-weight: 600; + line-height: 20px; + letter-spacing: -0.23px; + + &:first-child { + background: #000; + color: #fff; + &.payNow { + background-color: #ff3b30; + } + } + } } } @@ -432,3 +459,47 @@ font-weight: 600; line-height: normal; } + +.dialogFooter { + // width: 100%; + width: calc(100% + 1px); + height: 44px; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: flex-end; + position: absolute; + // margin: 0 -24px -24px; + bottom: 0; + left: 0; + border-top: 1px solid rgba(0, 0, 0, 0.06); + border-bottom-left-radius: 16px; + border-bottom-right-radius: 16px; + overflow: hidden; + + & > .cancel, & > .confirm { + padding: 12px 10px; + height: 44px; + width: 50%; + text-align: center; + // border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &:last-child { + background: #000; + color: #fff; + } + } + + & > .cancel { + border-radius: 0; + } + + & > .confirm { + border-radius: 0; + } +} diff --git a/src/order_pages/orderDetail/index.tsx b/src/order_pages/orderDetail/index.tsx index 922e08f..0bfacfa 100644 --- a/src/order_pages/orderDetail/index.tsx +++ b/src/order_pages/orderDetail/index.tsx @@ -1,31 +1,80 @@ -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import { View, Text, Button, Image } from "@tarojs/components"; +import { Dialog } from "@nutui/nutui-react-taro"; import Taro, { useDidShow, useRouter } from "@tarojs/taro"; import dayjs from "dayjs"; +import classnames from "classnames"; import orderService, { + CancelType, GameOrderRes, OrderStatus, + RefundStatus, } from "@/services/orderService"; import { payOrder, delay, calculateDistance, getCurrentLocation, + getOrderStatus, + generateOrderActions, + reloadPage, } from "@/utils"; import detailService, { GameData } from "@/services/detailService"; -import { withAuth } from "@/components"; +import { withAuth, RefundPopup } from "@/components"; import img from "@/config/images"; import { DECLAIMER } from "./config"; import styles from "./index.module.scss"; dayjs.locale("zh-cn"); +const refundTextMap = new Map([ + [RefundStatus.NONE, "已支付"], + [RefundStatus.PENDING, "退款中"], + [RefundStatus.SUCCESS, "已退款"], +]); + +const gameNoticeMap = new Map([ + [ + "pending", + { title: "球局暂未开始", content: "球局开始前2小时,我们将通过短信通知你" }, + ], + [ + "pendinging", + { + title: "球局即将开始,请按时抵达球局", + content: "球局开始前2小时,我们将通过短信通知你", + }, + ], + ["progress", { title: "球局已开始", content: "友谊第一,比赛第二" }], + ["finish", { title: "球局已结束", content: "" }], +]); + +function genGameNotice(order_status, start_time) { + const startTime = dayjs(start_time); + let key = ""; + if (order_status === OrderStatus.FINISHED) { + key = "finish"; + } + const leftHour = startTime.diff(dayjs(), "hour"); + const start = startTime.isBefore(dayjs()); + if (start) { + key = "progress"; + } else if (leftHour > 2) { + key = "pending"; + } else if (leftHour < 2) { + key = "pendinging"; + } + return gameNoticeMap.get(key) || {}; +} + function GameInfo(props) { const { detail, currentLocation, orderDetail } = props; - const { order_status } = orderDetail; + const { order_status, refund_status } = orderDetail; const { latitude, longitude, location, location_name, start_time, end_time } = detail || {}; + const refundRef = useRef(null); + const openMap = () => { Taro.openLocation({ latitude, // 纬度(必填) @@ -52,14 +101,129 @@ function GameInfo(props) { const startDate = `${startMonth}月${startDay}日 ${theDayOfWeek}`; const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`; + const orderStatus = getOrderStatus(orderDetail); + + const gameNotice = genGameNotice(order_status, start_time); + + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=orderList`, + }); + } + + async function handleDeleteOrder(item) { + const { order_id } = item; + // TODO:删除订单,刷新这一页,然后后面的全清除掉 + const onCancel = () => { + Dialog.close("detailCancelOrder"); + }; + const onConfirm = async () => { + try { + const deleteRes = await orderService.deleteOrder({ + order_id, + }); + if (deleteRes.code !== 0) { + throw new Error(deleteRes.message); + } + Taro.showToast({ + title: "删除成功", + icon: "none", + }); + delay(2000); + Taro.redirectTo({ url: "/order_pages/orderList/index" }); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("detailCancelOrder"); + } + }; + Dialog.open("detailCancelOrder", { + title: "确定删除订单吗?", + content: "删除订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }); + } + + async function handleCancelOrder(item) { + const { order_no } = item; + const onCancel = () => { + Dialog.close("detailCancelOrder"); + }; + const onConfirm = async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); + } + reloadPage(); + Taro.showToast({ + title: "取消成功", + icon: "none", + }); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("detailCancelOrder"); + } + }; + Dialog.open("detailCancelOrder", { + title: "确定取消订单吗?", + content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }); + } + + function handleQuit(item) { + if (refundRef.current) { + refundRef.current.show(item, (result) => { + if (result) { + reloadPage(); + } + }); + } + } + return ( - {Boolean(order_status) && order_status !== OrderStatus.PENDING && ( + {["progress", "expired"].includes(orderStatus) && ( <> - 已支付 ¥ 90 + + {refundTextMap.get(refund_status)} ¥ 90 + - 球局暂未开始 - 球局开始前2小时,我们将通过短信通知你 + {gameNotice.title} + {gameNotice.content && {gameNotice.content}} )} @@ -122,13 +286,36 @@ function GameInfo(props) { {/* Action bar */} - + + {orderDetail.order_id + ? generateOrderActions( + orderDetail, + { + handleDeleteOrder, + handleCancelOrder, + handleQuit, + handlePayNow: () => {}, + handleViewGame, + }, + "detail" + )?.map((obj) => ( + + )) + : ""} + +

+ ); } function OrderMsg(props) { - const { detail, checkOrderInfo } = props; + const { detail, orderDetail, checkOrderInfo } = props; const { start_time, end_time, @@ -137,7 +324,8 @@ function OrderMsg(props) { wechat_contact, price, } = detail; - const { order_info: { registrant_nickname, registrant_phone } = {} } = checkOrderInfo; + const { order_no } = orderDetail; + const { order_info: { registrant_phone } = {} } = checkOrderInfo; const startTime = dayjs(start_time); const endTime = dayjs(end_time); const startYear = startTime.format("YYYY"); @@ -159,18 +347,30 @@ function OrderMsg(props) { ), }, - { - title: "报名人昵称", - content: registrant_nickname, - }, { title: "报名人电话", content: registrant_phone, }, + { + title: "组织人微信号", + content: wechat_contact, + }, + { + title: "组织人电话", + content: wechat_contact, + }, { title: "费用", content: `${price} 元 / 人`, }, + ...(order_no + ? [ + { + title: "订单号", + content: order_no, + }, + ] + : []), ]; return ( @@ -199,8 +399,12 @@ function RefundPolicy(props) { rule: "退款规则", }, ...refund_policy.map((item, index) => { - const isLast = index === refund_policy.length - 1 - const theTimeObj = dayjs(isLast ? refund_policy.at(-2).deadline_formatted : item.deadline_formatted); + const isLast = index === refund_policy.length - 1; + const theTimeObj = dayjs( + isLast + ? refund_policy.at(-2).deadline_formatted + : item.deadline_formatted + ); const year = theTimeObj.format("YYYY"); const month = theTimeObj.format("M"); const day = theTimeObj.format("D"); @@ -332,6 +536,9 @@ const OrderCheck = () => { ); } + + const { order_status, cancel_type } = orderDetail; + return ( {/* Game Date and Address */} @@ -341,14 +548,20 @@ const OrderCheck = () => { currentLocation={location} /> {/* Order message */} - + {/* Refund policy */} {/* Disclaimer */} - {(!id || orderDetail.order_status === OrderStatus.PENDING) && ( + {(!id || + (order_status === OrderStatus.PENDING && + cancel_type === CancelType.NONE)) && ( )} diff --git a/src/order_pages/orderList/index.module.scss b/src/order_pages/orderList/index.module.scss index 3426f13..b7474e2 100644 --- a/src/order_pages/orderList/index.module.scss +++ b/src/order_pages/orderList/index.module.scss @@ -311,74 +311,6 @@ } } -.refundPolicy { - .moduleTitle { - display: flex; - padding: 15px 0 8px; - justify-content: space-between; - align-items: center; - align-self: stretch; - color: #000; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: 20px; - letter-spacing: -0.23px; - } - - .policyList { - border-radius: 12px; - border: 1px solid rgba(0, 0, 0, 0.06); - background: #fff; - box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06); - - .policyItem { - display: flex; - justify-content: space-around; - align-items: center; - color: #000; - text-align: center; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 20px; - border-top: 1px solid rgba(0, 0, 0, 0.06); - - &:nth-child(1) { - color: #000; - text-align: center; - font-feature-settings: - "liga" off, - "clig" off; - font-family: "PingFang SC"; - font-size: 14px; - font-style: normal; - font-weight: 600; - line-height: 20px; - border: none; - } - - .time, - .rule { - width: 50%; - padding: 10px 12px; - } - - .rule { - border-left: 1px solid rgba(0, 0, 0, 0.06); - } - } - } -} - .dialogFooter { // width: 100%; width: calc(100% + 1px); diff --git a/src/order_pages/orderList/index.tsx b/src/order_pages/orderList/index.tsx index 8f9adcd..fbc749a 100644 --- a/src/order_pages/orderList/index.tsx +++ b/src/order_pages/orderList/index.tsx @@ -1,14 +1,14 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import { View, Text, Button, Image, ScrollView } from "@tarojs/components"; import Taro, { useDidShow } from "@tarojs/taro"; import { Avatar, Dialog } from "@nutui/nutui-react-taro"; import dayjs from "dayjs"; import classnames from "classnames"; import orderService, { OrderStatus, CancelType } from "@/services/orderService"; -import { withAuth } from "@/components"; -import { payOrder } from "@/utils"; +import { withAuth, RefundPopup } from "@/components"; +import { payOrder, generateOrderActions } from "@/utils"; import styles from "./index.module.scss"; -// import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; + dayjs.locale("zh-cn"); const PAGESIZE = 20; @@ -45,11 +45,12 @@ function generateTimeMsg(game_info) { return ( <> - {diffDay <= 2 + {diffDay <= 2 && diffDay >= 0 ? diffDayMap.get(diffDay) : startTime.format("YYYY-MM-DD")} ({DayOfWeekMap.get(dayofWeek)}) + {startTime.format('ah')}点 {gameLength} ); @@ -58,6 +59,7 @@ function generateTimeMsg(game_info) { const OrderList = () => { const [list, setList] = useState([]); const [total, setTotal] = useState(0); + const refundRef = useRef(null) const end = list.length * PAGESIZE >= total; @@ -118,48 +120,6 @@ const OrderList = () => { }); } - function renderCancelContent(checkOrderInfo) { - const { refund_policy = [] } = checkOrderInfo; - const policyList = [ - { - time: "申请退款时间", - rule: "退款规则", - }, - ...refund_policy.map((item, index) => { - const isLast = index === refund_policy.length - 1; - const theTimeObj = dayjs( - isLast - ? refund_policy.at(-2).deadline_formatted - : item.deadline_formatted - ); - const year = theTimeObj.format("YYYY"); - const month = theTimeObj.format("M"); - const day = theTimeObj.format("D"); - const time = theTimeObj.format("HH:MM"); - return { - time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, - rule: item.refund_rule, - }; - }), - ]; - return ( - - - 退款政策 - - {/* 订单信息摘要 */} - - {policyList.map((item, index) => ( - - {item.time} - {item.rule} - - ))} - - - ); - } - async function handleDeleteOrder(item) { const { id: order_id } = item // TODO:删除订单,刷新这一页,然后后面的全清除掉 @@ -211,90 +171,65 @@ const OrderList = () => { } async function handleCancelOrder(item) { - const { order_no, order_status, game_info, amount } = item; - if (order_status === OrderStatus.PENDING) { - const onCancel = () => { - Dialog.close("cancelOrder"); - }; - const onConfirm = async () => { - try { - const cancelRes = await orderService.cancelUnpaidOrder({ - order_no, - cancel_reason: "用户主动取消", - }); - if (cancelRes.code !== 0) { - throw new Error(cancelRes.message); - } - getOrders(item.page, false); - Taro.showToast({ - title: "取消成功", - icon: "none", - }) - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); + const { order_no } = item; + const onCancel = () => { + Dialog.close("cancelOrder"); + }; + const onConfirm = async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); } - }; - Dialog.open("cancelOrder", { - title: "确定取消订单吗?", - content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", - footer: ( - - - - - ), - onConfirm, - onCancel, - }); - return; - } - const res = await orderService.getCheckOrderInfo(game_info.id); + getOrders(item.page, false); + Taro.showToast({ + title: "取消成功", + icon: "none", + }) + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }; Dialog.open("cancelOrder", { title: "确定取消订单吗?", - content: renderCancelContent(res.data), - onConfirm: async () => { - try { - const refundRes = await orderService.applicateRefund({ - order_no, - refund_amount: amount, - refund_reason: "用户主动退款", - }); - if (refundRes.code !== 0) { - throw new Error(refundRes.message); - } - getOrders(item.page, false); - Taro.showToast({ - title: "退出成功", - icon: "none", - }) - } catch (e) { - Taro.showToast({ - title: e.message, - icon: "error", - }); - } finally { - Dialog.close("cancelOrder"); - } - }, - onCancel: () => { - Dialog.close("cancelOrder"); - }, + content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, }); } + function handleQuit (item) { + if (refundRef.current) { + refundRef.current.show(item, (result) => { + if (result) { + getOrders(item.page) + } + }) + } + } + function handleViewOrderDetail(orderId) { Taro.navigateTo({ url: `/order_pages/orderDetail/index?id=${orderId}`, @@ -314,19 +249,7 @@ const OrderList = () => { > {/* */} {list.flat().map((item) => { - const unPay = item.order_status === OrderStatus.PENDING; - const expired = - item.order_status === OrderStatus.FINISHED || - [CancelType.TIMEOUT, CancelType.USER].includes(item.cancel_type); - // const expiredTime = dayjs(item.expire_time).isSame(dayjs(), "day") - // ? dayjs(item.expire_time).format("HH:mm:ss") - // : dayjs(item.expire_time).format("YYYY-MM-DD HH:mm:ss"); - const showCancel = - item.order_status === OrderStatus.PENDING && - item.cancel_type === CancelType.NONE; - const showQuit = - item.order_status === OrderStatus.PAID && - item.cancel_type === CancelType.NONE; + const unPay = item.order_status === OrderStatus.PENDING && item.cancel_type === CancelType.NONE; const { game_info: { @@ -352,10 +275,10 @@ const OrderList = () => { - {unPay && !expired ? "待支付" : "已支付"} ¥{" "} + {unPay ? "待支付" : "已支付"} ¥{" "} {item.amount} @@ -372,18 +295,48 @@ const OrderList = () => { {participants.length >= 0 ? ( - {/* participants */[{ user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 1 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 2 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 3 } }].map((participant) => { - const { - user: { avatar_url, id }, - } = participant; - return ; - })} + { + /* participants */ [ + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 1, + }, + }, + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 2, + }, + }, + { + user: { + avatar_url: "https://img.yzcdn.cn/vant/cat.jpeg", + id: 3, + }, + }, + ].map((participant) => { + const { + user: { avatar_url, id }, + } = participant; + return ( + + ); + }) + } ) : ( "" )} - 报名人数 {current_players} + + 报名人数 {current_players} + / {max_players} @@ -400,45 +353,27 @@ const OrderList = () => { - {expired && ( + {generateOrderActions( + item, + { + handleDeleteOrder, + handleCancelOrder, + handleQuit, + handlePayNow, + handleViewGame, + }, + "list" + )?.map((obj) => ( - )} - {showCancel && ( - - )} - {showQuit && ( - - )} - {unPay && !expired ? ( - - ) : ( - - )} + ))} @@ -447,6 +382,7 @@ const OrderList = () => { {end && 已经到底了~} + ); }; diff --git a/src/services/orderService.ts b/src/services/orderService.ts index fbf6a05..787267e 100644 --- a/src/services/orderService.ts +++ b/src/services/orderService.ts @@ -22,6 +22,12 @@ export enum CancelType { TIMEOUT, // 超时 } +export enum RefundStatus { + NONE = 0, // 无退款 + PENDING, // 退款中 + SUCCESS, // 已退款 +} + export interface PayMentParams { order_id: number; order_no: string; diff --git a/src/static/order/orderListClose.svg b/src/static/order/orderListClose.svg new file mode 100644 index 0000000..5cde120 --- /dev/null +++ b/src/static/order/orderListClose.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/utils/index.ts b/src/utils/index.ts index af30b5d..08ea333 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,3 +5,5 @@ export * from "./processImage"; export * from "./timeUtils"; export * from "./tokenManager"; export * from "./order.pay"; +export * from './orderActions'; +export * from './routeUtil'; diff --git a/src/utils/orderActions.ts b/src/utils/orderActions.ts new file mode 100644 index 0000000..49f4e4b --- /dev/null +++ b/src/utils/orderActions.ts @@ -0,0 +1,68 @@ +import { OrderStatus, CancelType } from "@/services/orderService"; + +export function getOrderStatus(orderData) { + const { order_status, cancel_type } = orderData + const unPay = order_status === OrderStatus.PENDING && cancel_type === CancelType.NONE; + const expired = + order_status === OrderStatus.FINISHED || + [CancelType.TIMEOUT, CancelType.USER].includes(cancel_type); + + return unPay ? 'unpay' : expired ? 'expired' : 'progress' +} + +// scene: list、detail +export function generateOrderActions(orderData, actions, scene) { + + + const { handleDeleteOrder, handleCancelOrder, handleQuit, handlePayNow, handleViewGame } = actions + + const deleteOrder = { + text: '删除订单', + className: 'cancelOrder', + action: handleDeleteOrder.bind(null, orderData), + } + + const cancelOrder = { + text: '取消订单', + className: 'cancelOrder', + action: handleCancelOrder.bind(null, orderData), + } + + const quitGame = { + text: '退出活动', + className: 'cancelOrder', + action: handleQuit.bind(null, orderData), + } + + const payNow = { + text: '立即支付', + className: 'payNow', + action: handlePayNow.bind(null, orderData), + } + + const gameDetail = { + text: '球局详情', + className: 'gameDetail', + action: handleViewGame.bind(null, orderData.game_info.id), + } + + const key = getOrderStatus(orderData) + + if (scene === 'list') { + const actionMap = new Map([ + ['expired', [deleteOrder, gameDetail]], + ['progress', [quitGame, gameDetail]], + ['unpay', [cancelOrder, payNow]] + ]) + return actionMap.get(key) + } + + if (scene === 'detail') { + const actionMap = new Map([ + ['expired', [gameDetail, deleteOrder]], + ['progress', [gameDetail, quitGame]], + ['unpay', [cancelOrder]] + ]) + return actionMap.get(key) + } +} diff --git a/src/utils/routeUtil.ts b/src/utils/routeUtil.ts new file mode 100644 index 0000000..178655e --- /dev/null +++ b/src/utils/routeUtil.ts @@ -0,0 +1,23 @@ +import Taro from "@tarojs/taro"; + +export function getCurrentFullPath(): string { + const pages = Taro.getCurrentPages(); + const currentPage = pages.at(-1); + + if (currentPage) { + console.log(currentPage, "currentPage get"); + const route = currentPage.route; + const options = currentPage.options || {}; + + const query = Object.keys(options) + .map((key) => `${key}=${options[key]}`) + .join("&"); + + return query ? `/${route}?${query}` : `/${route}`; + } + return ""; +} + +export function reloadPage() { + Taro.reLaunch({ url: getCurrentFullPath() }) +}