diff --git a/src/mod_user/orderDetail/index.tsx b/src/mod_user/orderDetail/index.tsx index cec8780..ec6659b 100644 --- a/src/mod_user/orderDetail/index.tsx +++ b/src/mod_user/orderDetail/index.tsx @@ -2,14 +2,18 @@ import React, { useState } from "react"; import { View, Text, Button, Image } from "@tarojs/components"; import Taro, { useDidShow, useRouter } from "@tarojs/taro"; import dayjs from "dayjs"; -import { delay } from "@/utils"; import orderService, { GameOrderRes, OrderStatus, } from "@/services/orderService"; +import { + payOrder, + delay, + calculateDistance, + getCurrentLocation, +} from "@/utils"; import detailService, { GameData } from "@/services/detailService"; import { withAuth } from "@/components"; -import { calculateDistance, getCurrentLocation } from "@/utils"; import img from "@/config/images"; import { DECLAIMER } from "./config"; import styles from "./index.module.scss"; @@ -290,38 +294,15 @@ const OrderCheck = () => { try { const payment_params = await getPaymentParams(); - - const { - timeStamp, - nonceStr, - package: package_, - signType, - paySign, - } = payment_params; - await Taro.requestPayment({ - timeStamp, - nonceStr, - package: package_, - signType, - paySign, - success: async () => { - Taro.hideLoading(); - Taro.showToast({ - title: "支付成功", - icon: "success", - }); - await delay(1000); - Taro.navigateBack({ - delta: 1, - }); - }, - fail: () => { - Taro.hideLoading(); - Taro.showToast({ - title: "支付失败", - icon: "none", - }); - }, + await payOrder(payment_params); + Taro.hideLoading(); + Taro.showToast({ + title: "支付成功", + icon: "success", + }); + await delay(1000); + Taro.navigateBack({ + delta: 1, }); } catch (error) { Taro.hideLoading(); diff --git a/src/mod_user/orderList/index.module.scss b/src/mod_user/orderList/index.module.scss index a41d76c..e42fc43 100644 --- a/src/mod_user/orderList/index.module.scss +++ b/src/mod_user/orderList/index.module.scss @@ -1,4 +1,4 @@ -@use '~@/scss/images.scss' as img; +@use "~@/scss/images.scss" as img; .container { padding: 12px; @@ -18,6 +18,84 @@ height: 18px; padding: 15px 15px 12px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: space-between; + + .userInfo { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 6px; + + .avatar { + width: 16px; + height: 16px; + } + + .nickName { + display: contents; + .nickNameText { + color: #000; + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 18px; + } + .arrowRight { + width: 8px; + height: 8px; + } + } + } + + .paidInfo { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + + .payTime { + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; + + &.paid { + color: rgba(60, 60, 67, 0.6); + } + + &.pending { + color: #000; + } + } + + .payNum { + font-feature-settings: + "liga" off, + "clig" off; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 600; + line-height: 18px; + &.paid { + color: #000; + } + + &.pending { + color: #ff3b30; + } + } + } } .gameInfo { @@ -28,6 +106,112 @@ height: 28px; padding: 12px 12px 15px; border-top: 1px solid rgba(0, 0, 0, 0.06); + + display: flex; + align-items: center; + justify-content: space-between; + + .extraActions { + } + + .mainActions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + + & > .button { + 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; + + &:last-child { + background: #000; + color: #fff; + } + } + + .cancelOrder { + } + + .payNow { + } + } } } -} \ No newline at end of file +} + +.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); + } + } + } +} diff --git a/src/mod_user/orderList/index.tsx b/src/mod_user/orderList/index.tsx index b72ccc3..5a7049d 100644 --- a/src/mod_user/orderList/index.tsx +++ b/src/mod_user/orderList/index.tsx @@ -1,44 +1,241 @@ -import React, { useState } from 'react' -import { View, Text, Button } from '@tarojs/components' -import Taro, { useDidShow } from '@tarojs/taro' -import { delay } from '@/utils' -import orderService from '@/services/orderService' -import { withAuth } from '@/components' -import styles from './index.module.scss' +import React, { useState } from "react"; +import { View, Text, Button, Image } 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 styles from "./index.module.scss"; +import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; const OrderList = () => { - const [list, setList] = useState([]) + const [list, setList] = useState([]); - useDidShow(async () => { - const res = await orderService.getOrderList() - console.log(res) + useDidShow(() => { + getOrders(); + }); + + async function getOrders() { + const res = await orderService.getOrderList(); + console.log(res); if (res.code === 0) { - setList(res.data.rows) + setList(res.data.rows); } - }) + } - console.log(list, 'list') + async function handlePayNow(gameId) { + try { + const unPaidRes = await orderService.getUnpaidOrder(gameId); + if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) { + await payOrder(unPaidRes.data.payment_params); + getOrders(); + } else { + throw new Error("支付调用失败"); + } + } catch (e) { + console.log(e, 1111); + Taro.showToast({ + title: e.message, + icon: "none", + }); + } + } + + function renderCancelContent(checkOrderInfo) { + const { refund_policy = [] } = checkOrderInfo; + const policyList = [ + { + time: "申请退款时间", + rule: "退款规则", + }, + ...refund_policy.map((item, index) => { + const [, theTime] = item.application_time.split("undefined "); + const theTimeObj = dayjs(theTime); + 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}${index === 0 ? "前" : "后"}`, + rule: item.refund_rule, + }; + }), + ]; + return ( + + + 退款政策 + + {/* 订单信息摘要 */} + + {policyList.map((item, index) => ( + + {item.time} + {item.rule} + + ))} + + + ); + } + + async function handleCancelOrder(item) { + const { order_no, order_status, game_info, amount } = item; + if (order_status === OrderStatus.PENDING) { + Dialog.open("cancelOrder", { + title: "确定取消订单吗?", + content: "取消订单后,您将无法恢复订单。请确认是否继续取消?", + onConfirm: async () => { + try { + const cancelRes = await orderService.cancelUnpaidOrder({ + order_no, + cancel_reason: "用户主动取消", + }); + if (cancelRes.code !== 0) { + throw new Error(cancelRes.message); + } + getOrders(); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }, + onCancel: () => { + Dialog.close("cancelOrder"); + }, + }); + return; + } + const res = await orderService.getCheckOrderInfo(game_info.id); + 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(); + } catch (e) { + Taro.showToast({ + title: e.message, + icon: "error", + }); + } finally { + Dialog.close("cancelOrder"); + } + }, + onCancel: () => { + Dialog.close("cancelOrder"); + }, + }); + } + + function handleViewOrderDetail(orderId) { + Taro.navigateTo({ + url: `/mod_user/orderDetail/index?id=${orderId}`, + }); + } return ( - { - list.map(item => { - return ( - - - {item?.game_info?.title} - - - - + {list.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.FINISHED && + item.cancel_type === CancelType.NONE; + + return ( + + + + + + Light + + {expired ? ( + "" + ) : ( + + + {unPay + ? `请在 ${expiredTime} 前支付` + : dayjs(item.pay_time).format("YYYY-MM-DD HH:mm:ss")} + + + {unPay ? "待支付" : "已支付"} ¥ {item.amount} + + + )} - ) - }) - } + + {item?.game_info?.title} + + + + + {showCancel && ( + + )} + {unPay && !expired && ( + + )} + + + + ); + })} + - ) -} + ); +}; -export default withAuth(OrderList) \ No newline at end of file +export default withAuth(OrderList); diff --git a/src/pages/detail/index.tsx b/src/pages/detail/index.tsx index fb7659b..789085e 100644 --- a/src/pages/detail/index.tsx +++ b/src/pages/detail/index.tsx @@ -38,7 +38,8 @@ function insertDotInTags(tags: string[]) { } function GameTags(props) { - const { detail } = props; + const { userInfo } = props; + const { avatar_url } = userInfo; const tags = [ { name: "🕙 急招", @@ -63,7 +64,7 @@ function GameTags(props) { {/* network image mock */} @@ -1072,7 +1073,7 @@ function Index() { {/* content */} {/* avatar and tags */} - + {/* title */} {detail.title} diff --git a/src/services/orderService.ts b/src/services/orderService.ts index 7272cd0..8b2646f 100644 --- a/src/services/orderService.ts +++ b/src/services/orderService.ts @@ -16,6 +16,12 @@ export enum OrderStatus { FINISHED, } +export enum CancelType { + NONE = 0, // 未取消 + USER, // 用户主动取消 + TIMEOUT, // 超时 +} + export interface PayMentParams { order_id: number; order_no: string; @@ -119,6 +125,42 @@ class OrderService { }, ); } + + // 取消未支付订单 + async cancelUnpaidOrder({ + order_no, + cancel_reason, + }: { + order_no: number; + cancel_reason: string; + }): Promise> { + return httpService.post( + "/payment/cancel_order", + { order_no, cancel_reason }, + { + showLoading: true, + }, + ); + } + + // 申请退款 + async applicateRefund({ + order_no, + refund_reason, + refund_amount, + }: { + order_no: number; + refund_reason: string; + refund_amount: number; + }): Promise> { + return httpService.post( + "/payment/apply_refund", + { order_no, refund_reason, refund_amount }, + { + showLoading: true, + }, + ); + } } // 导出认证服务实例 diff --git a/src/static/order/orderListArrowRight.svg b/src/static/order/orderListArrowRight.svg new file mode 100644 index 0000000..01f71b4 --- /dev/null +++ b/src/static/order/orderListArrowRight.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/utils/index.ts b/src/utils/index.ts index 044bd88..af30b5d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,10 +1,7 @@ -export * from './getNavbarHeight' -export * from './genderUtils' -export * from './locationUtils' -export * from './processImage' -export * from './timeUtils' -export * from './tokenManager' - -export function delay(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)) -} \ No newline at end of file +export * from "./getNavbarHeight"; +export * from "./genderUtils"; +export * from "./locationUtils"; +export * from "./processImage"; +export * from "./timeUtils"; +export * from "./tokenManager"; +export * from "./order.pay"; diff --git a/src/utils/order.pay.ts b/src/utils/order.pay.ts new file mode 100644 index 0000000..060fc7b --- /dev/null +++ b/src/utils/order.pay.ts @@ -0,0 +1,20 @@ +import Taro from "@tarojs/taro"; + +export function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +export async function payOrder(params) { + const { timeStamp, nonceStr, package: _package, signType, paySign } = params; + return new Promise((resolve, reject) => { + Taro.requestPayment({ + timeStamp, + nonceStr, + package: _package, + signType, + paySign, + success: resolve, + fail: reject.bind(null, new Error("支付失败")), + }); + }); +}