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 "dayjs/locale/zh-cn"; import classnames from "classnames"; import orderService, { CancelType, GameOrderRes, OrderStatus, refundTextMap, } from "@/services/orderService"; import { debounce } from "@tarojs/runtime"; import { payOrder, delay, calculateDistance, getCurrentLocation, getOrderStatus, generateOrderActions, isPhoneNumber, } from "@/utils"; import { getStorage, setStorage } from "@/store/storage"; import { useGlobalStore } from "@/store/global"; import { useOrder } from "@/store/orderStore"; import detailService, { GameData } from "@/services/detailService"; import { withAuth, RefundPopup, GeneralNavbar } from "@/components"; import { OSS_BASE } from "@/config/api"; import img from "@/config/images"; import CustomerIcon from "@/static/order/customer.svg"; import { handleCustomerService } from "@/services/userService"; import { requireLoginWithPhone } from "@/utils/helper"; import { DECLAIMER } from "./config"; import styles from "./index.module.scss"; dayjs.locale("zh-cn"); 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.PENDING) { return {}; } 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, init } = props; const { order_status, refund_status, amount } = orderDetail; const { latitude, longitude, location, location_name, start_time, end_time, weather, title, } = detail || {}; const [{ iconDay, tempMax, tempMin }] = weather || [{}]; const refundRef = useRef(null); const openMap = () => { Taro.openLocation({ latitude, // 纬度(必填) longitude, // 经度(必填) name: location_name, // 位置名(可选) address: location, // 地址详情(可选) scale: 15, // 地图缩放级别(1-28) }); }; const [c_latitude, c_longitude] = currentLocation; const distance = c_latitude + c_longitude === 0 ? 0 : calculateDistance(c_latitude, c_longitude, latitude, longitude) / 1000; const startTime = dayjs(start_time); const endTime = dayjs(end_time); const game_length = Number( (endTime.diff(startTime, "minutes") / 60).toFixed() ); const startMonth = startTime.format("M"); const startDay = startTime.format("D"); const theDayOfWeek = startTime.format("dddd"); 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; 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); } init(); 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) { init(); } }); } } return ( {["refund", "progress", "expired"].includes(orderStatus) && ( {refundTextMap.get(refund_status)} ¥ {amount} )} {["progress", "expired"].includes(orderStatus) && order_status !== OrderStatus.PENDING && ( {gameNotice.title} {gameNotice.content && {gameNotice.content}} )} {!orderDetail.order_id && ( {title} )} {/* Date and Weather */} {/* Calendar and Date time */} {/* Calendar */} {startMonth}月 {startDay} {/* Date time */} {startDate} {gameRange} ({game_length}小时) {/* Weather icon */} {/**/} {/* Weather text and temperature */} {tempMin && tempMax && ( {tempMin}℃ - {tempMax}℃ )} {/* Place */} {/* venue location message */} {/* location icon */} {/* location message */} {/* venue name and distance */} {location_name || "-"} {distance ? ( <> · {distance.toFixed(1)}km ) : null} {/* venue address */} {location || "-"} {/* Action bar */} {orderDetail.order_id && ( {generateOrderActions( orderDetail, { handleDeleteOrder, handleCancelOrder, handleQuit, handlePayNow: () => {}, handleViewGame, }, "detail" )?.map((obj) => ( {obj.text} ))} 客服 )} ); } function handleCopy(msg) { Taro.setClipboardData({ data: msg, }); } function OrderMsg(props) { const { detail, orderDetail, checkOrderInfo } = props; const { start_time, end_time, location, location_name, wechat_contact, price, } = detail; const { order_no, registrant_phone: registrant_phone_from_order } = orderDetail; const { order_info: { registrant_phone: registrant_phone_from_check_order } = {}, } = checkOrderInfo || {}; const registrant_phone = registrant_phone_from_order || registrant_phone_from_check_order; const startTime = dayjs(start_time); const endTime = dayjs(end_time); const startDate = startTime.format("YYYY年M月D日"); const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`; const summary = [ { title: "时间", content: `${startDate} ${gameRange}`, }, { title: "地址", content: ( {location} {location_name} ), }, { title: "报名人电话", content: registrant_phone ? ( { Taro.makePhoneCall({ phoneNumber: registrant_phone }); }} > {registrant_phone} ) : ( "-" ), }, { title: "组织人微信号", content: wechat_contact || "-", }, { title: "组织人电话", content: wechat_contact && isPhoneNumber(wechat_contact) ? ( { Taro.makePhoneCall({ phoneNumber: wechat_contact }); }} > {wechat_contact} ) : ( "-" ), }, { title: "费用", content: `${price} 元 / 人`, }, ...(order_no ? [ { title: "订单号", content: ( {order_no} 复制 ), }, ] : []), ]; return ( 确认订单信息 {/* 订单信息摘要 */} {summary.map((item, index) => ( {item.title} {item.content} ))} ); } function RefundPolicy(props) { const { refund_policy = [] } = props; const current = dayjs(); 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, beforeCurrent: isLast ? true : current.isBefore(theTimeObj), }; }), ]; const targetIndex = policyList.findIndex((item) => item.beforeCurrent); return ( 退款政策 {/* 订单信息摘要 */} {policyList.map((item, index) => ( index && index !== 0 ? styles.pastItem : "", targetIndex === index ? styles.currentItem : "" )} > {targetIndex === index && ( 当前时间段 )} {item.time} {item.rule} ))} ); } function Disclaimer() { return ( 免责声明 {DECLAIMER} ); } const OrderCheck = () => { const { params } = useRouter(); const { id: stringId, gameId: stringGameId } = params; const [id, gameId] = [Number(stringId), Number(stringGameId)]; const [detail, setDetail] = useState({}); const [location, setLocation] = useState([0, 0]); const [checkOrderInfo, setCheckOrderInfo] = useState(); const [orderDetail, setOrderDetail] = useState({}); const { paying, setPaying } = useOrder(); useDidShow(() => { init(); }); async function init() { let gameDetail = {}; if (id) { const res = await orderService.getOrderDetail(id); if (res.code === 0) { setOrderDetail(res.data); gameDetail = res.data.game_info; } } else if (gameId) { const res = await detailService.getDetail(gameId); if (res.code === 0) { gameDetail = res.data; } checkOrder(gameId); } setDetail(gameDetail); const location = await getCurrentLocation(); setLocation([location.latitude, location.longitude]); } async function checkOrder(gid) { const orderRes = await orderService.getCheckOrderInfo(gid); setCheckOrderInfo(orderRes.data); } async function getPaymentParams() { // 检查登录状态和手机号(创建订单前检查) if (!requireLoginWithPhone()) { throw new Error("请先绑定手机号"); } const unPaidRes = await orderService.getUnpaidOrder(detail.id); if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) { return unPaidRes.data.payment_params; } const createOrderRes = await orderService.createOrder(detail.id); if (createOrderRes.code === 0) { return createOrderRes.data.payment_params; } throw new Error("支付调用失败"); } //TODO: get order msg from id const handlePay = debounce(async () => { // 检查登录状态和手机号 if (!requireLoginWithPhone()) { return; // 未登录或未绑定手机号,已跳转到登录页 } setPaying(true); let payment_params = {}; try { payment_params = await getPaymentParams(); if (!id) { setStorage("backFlag", "1"); Taro.redirectTo({ url: `/order_pages/orderDetail/index?id=${payment_params.order_id}`, }); } await payOrder(payment_params); Taro.showToast({ title: "支付成功", icon: "success", }); const backFlag = getStorage("backFlag"); if (backFlag === "1") { setStorage("backFlag", "0"); Taro.navigateBack(); } // Taro.navigateBack({ // delta: 1, // }); } catch (error) { Taro.showToast({ title: error.message, icon: "none", }); } finally { setStorage("backFlag", "0"); init(); setPaying(false); } }, 300); if (!id && !gameId) { return ( 异常订单 ); } const { order_status, cancel_type } = orderDetail; const { statusNavbarHeightInfo } = useGlobalStore(); const { totalHeight } = statusNavbarHeightInfo; return ( {/* Game Date and Address */} {/* Order message */} {/* Refund policy */} {/* Disclaimer */} {(!id || (order_status === OrderStatus.PENDING && cancel_type === CancelType.NONE)) && ( )} ); }; export default withAuth(OrderCheck);