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);