Files
mini-programs/src/mod_user/orderDetail/index.tsx
2025-09-11 22:51:25 +08:00

354 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 orderService, {
GameOrderRes,
OrderStatus,
} from "@/services/orderService";
import {
payOrder,
delay,
calculateDistance,
getCurrentLocation,
} from "@/utils";
import detailService, { GameData } from "@/services/detailService";
import { withAuth } from "@/components";
import img from "@/config/images";
import { DECLAIMER } from "./config";
import styles from "./index.module.scss";
dayjs.locale("zh-cn");
function GameInfo(props) {
const { detail, currentLocation, orderDetail } = props;
const { order_status } = orderDetail;
const { latitude, longitude, location, location_name, start_time, end_time } =
detail || {};
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 = endTime.diff(startTime, "minutes") / 60;
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")}`;
return (
<View className={styles.gameInfoContainer}>
{order_status !== OrderStatus.PENDING && (
<>
<View className={styles.paidInfo}> ¥ 90</View>
<View className={styles.gameStatus}>
<Text className={styles.statusText}></Text>
<Text>2</Text>
</View>
</>
)}
<View className={styles.gameInfo}>
{/* Date and Weather */}
<View className={styles.gameInfoDateWeather}>
{/* Calendar and Date time */}
<View className={styles.gameInfoDateWeatherCalendarDate}>
{/* Calendar */}
<View className={styles.gameInfoDateWeatherCalendarDateCalendar}>
<View className={styles.month}>{startMonth}</View>
<View className={styles.day}>{startDay}</View>
</View>
{/* Date time */}
<View className={styles.gameInfoDateWeatherCalendarDateDate}>
<View className={styles.date}>{startDate}</View>
<View className={styles.venueTime}>
{gameRange} {game_length}
</View>
</View>
</View>
</View>
{/* Place */}
<View className={styles.gameInfoPlace}>
{/* venue location message */}
<View className={styles.locationMessage}>
{/* location icon */}
<View className={styles.locationMessageIcon}>
<Image
className={styles.locationMessageIconImage}
src="https://bimwe.oss-cn-shanghai.aliyuncs.com/front/ball/images/43aab7e9-061e-4e3b-88c6-61c19b660b22.png"
/>
</View>
{/* location message */}
<View className={styles.locationMessageText}>
{/* venue name and distance */}
<View
className={styles.locationMessageTextNameDistance}
onClick={openMap}
>
<Text>{location_name || "-"}</Text>
{distance ? (
<>
<Text>·</Text>
<Text>{distance.toFixed(1)}km</Text>
</>
) : null}
<Image
className={styles.locationMessageTextNameDistanceArrow}
src={img.ICON_DETAIL_ARROW_RIGHT}
/>
</View>
{/* venue address */}
<View className={styles.locationMessageTextAddress}>
<Text>{location || "-"}</Text>
</View>
</View>
</View>
</View>
</View>
{/* Action bar */}
<View className={styles.gameInfoActions}></View>
</View>
);
}
function OrderMsg(props) {
const { detail, checkOrderInfo } = props;
const {
start_time,
end_time,
location,
location_name,
wechat_contact,
price,
} = detail;
const { order_info: { registrant_nickname } = {} } = checkOrderInfo;
const startTime = dayjs(start_time);
const endTime = dayjs(end_time);
const startYear = startTime.format("YYYY");
const startMonth = startTime.format("M");
const startDay = startTime.format("D");
const startDate = `${startYear}${startMonth}${startDay}`;
const gameRange = `${startTime.format("HH:mm")} - ${endTime.format("HH:mm")}`;
const summary = [
{
title: "时间",
content: `${startDate} ${gameRange}`,
},
{
title: "地址",
content: `${location} ${location_name}`,
},
{
title: "组织者昵称",
content: registrant_nickname,
},
{
title: "组织者电话",
content: wechat_contact,
},
{
title: "费用",
content: `${price} 元 / 人`,
},
];
return (
<View className={styles.orderSummary}>
<View className={styles.moduleTitle}>
<Text></Text>
</View>
{/* 订单信息摘要 */}
<View className={styles.summaryList}>
{summary.map((item, index) => (
<View key={index} className={styles.summaryItem}>
<Text className={styles.title}>{item.title}</Text>
<Text className={styles.content}>{item.content}</Text>
</View>
))}
</View>
</View>
);
}
function RefundPolicy(props) {
const { checkOrderInfo } = props;
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 (
<View className={styles.refundPolicy}>
<View className={styles.moduleTitle}>
<Text>退</Text>
</View>
{/* 订单信息摘要 */}
<View className={styles.policyList}>
{policyList.map((item, index) => (
<View key={index} className={styles.policyItem}>
<View className={styles.time}>{item.time}</View>
<View className={styles.rule}>{item.rule}</View>
</View>
))}
</View>
</View>
);
}
function Disclaimer() {
return (
<View className={styles.declaimer}>
<Text className={styles.title}></Text>
<Text className={styles.content}>{DECLAIMER}</Text>
</View>
);
}
const OrderCheck = () => {
const { params } = useRouter();
const { id: stringId, gameId: stringGameId } = params;
const [id, gameId] = [Number(stringId), Number(stringGameId)];
const [detail, setDetail] = useState<GameData | {}>({});
const [location, setLocation] = useState<number[]>([0, 0]);
const [checkOrderInfo, setCheckOrderInfo] = useState<GameOrderRes | {}>({});
const [orderDetail, setOrderDetail] = useState({});
useDidShow(async () => {
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;
}
}
if (gameDetail.id) {
setDetail(gameDetail);
onInit(gameDetail.id);
}
});
async function checkOrder(gid) {
const orderRes = await orderService.getCheckOrderInfo(gid);
setCheckOrderInfo(orderRes.data);
}
async function onInit(gid) {
checkOrder(gid);
const location = await getCurrentLocation();
setLocation([location.latitude, location.longitude]);
}
async function getPaymentParams() {
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 = async () => {
Taro.showLoading({
title: "支付中...",
mask: true,
});
try {
const payment_params = await getPaymentParams();
await payOrder(payment_params);
Taro.hideLoading();
Taro.showToast({
title: "支付成功",
icon: "success",
});
await delay(1000);
Taro.navigateBack({
delta: 1,
});
} catch (error) {
Taro.hideLoading();
Taro.showToast({
title: error.message,
icon: "none",
});
}
};
if (!id && !gameId) {
return (
<View className={styles.errorTip}>
<Text></Text>
<Button
type="warn"
onClick={() => {
Taro.redirectTo({ url: "/pages/list/index" });
}}
>
</Button>
</View>
);
}
return (
<View className={styles.container}>
{/* Game Date and Address */}
<GameInfo
detail={detail}
orderDetail={orderDetail}
currentLocation={location}
/>
{/* Order message */}
<OrderMsg detail={detail} checkOrderInfo={checkOrderInfo} />
{/* Refund policy */}
<RefundPolicy checkOrderInfo={checkOrderInfo} />
{/* Disclaimer */}
<Disclaimer />
{(!id || orderDetail.order_status === OrderStatus.PENDING) && (
<Button className={styles.payButton} type="primary" onClick={handlePay}>
{orderDetail.order_status === OrderStatus.PENDING ? "继续" : ""}
</Button>
)}
</View>
);
};
export default withAuth(OrderCheck);