434 lines
13 KiB
TypeScript
434 lines
13 KiB
TypeScript
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 "dayjs/locale/zh-cn";
|
||
import classnames from "classnames";
|
||
import orderService, {
|
||
OrderStatus,
|
||
CancelType,
|
||
refundTextMap,
|
||
} from "@/services/orderService";
|
||
import { getStorage, removeStorage, setStorage } from "@/store/storage";
|
||
import { useGlobalStore } from "@/store/global";
|
||
import { handleCustomerService } from "@/services/userService";
|
||
import { withAuth, RefundPopup, GeneralNavbar } from "@/components";
|
||
import { payOrder, generateOrderActions } from "@/utils";
|
||
import emptyContent from "@/static/emptyStatus/publish-empty.png";
|
||
import CustomerIcon from "@/static/order/customer.svg";
|
||
import { insertDotInTags } from "@/game_pages/detail/utils/helper";
|
||
import styles from "./index.module.scss";
|
||
|
||
dayjs.locale("zh-cn");
|
||
|
||
const PAGESIZE = 100;
|
||
|
||
const diffDayMap = new Map([
|
||
[0, "今天"],
|
||
[1, "明天"],
|
||
[2, "后天"],
|
||
]);
|
||
|
||
const DayOfWeekMap = new Map([
|
||
[0, "周日"],
|
||
[1, "周一"],
|
||
[2, "周二"],
|
||
[3, "周三"],
|
||
[4, "周四"],
|
||
[5, "周五"],
|
||
[6, "周六"],
|
||
]);
|
||
|
||
function generateTimeMsg(game_info) {
|
||
const { start_time, end_time } = game_info;
|
||
const startTime = dayjs(start_time);
|
||
const endTime = dayjs(end_time);
|
||
const diffDay = startTime.startOf("day").diff(dayjs().startOf("day"), "day");
|
||
|
||
const dayofWeek = startTime.day();
|
||
const gameLength = `${endTime.diff(startTime, "hour")}小时`;
|
||
return (
|
||
<>
|
||
<Text>
|
||
{diffDay <= 2 && diffDay >= 0
|
||
? diffDayMap.get(diffDay)
|
||
: startTime.format("YYYY-MM-DD")}
|
||
</Text>
|
||
<Text>({DayOfWeekMap.get(dayofWeek)})</Text>
|
||
<Text>{startTime.format("ah")}点</Text>
|
||
<Text>{gameLength}</Text>
|
||
</>
|
||
);
|
||
}
|
||
|
||
const OrderList = () => {
|
||
const [list, setList] = useState<any[][]>([]);
|
||
const [total, setTotal] = useState(0);
|
||
const refundRef = useRef(null);
|
||
|
||
const end = list.length * PAGESIZE >= total;
|
||
|
||
useEffect(() => {
|
||
getOrders(1);
|
||
}, []);
|
||
|
||
useDidShow(() => {
|
||
const targetPage = getStorage("list_reload_page_number");
|
||
if (targetPage) {
|
||
removeStorage("list_reload_page_number");
|
||
getOrders(Number(targetPage));
|
||
}
|
||
});
|
||
|
||
function addPageInfo(arr, page) {
|
||
return arr.map((item) => ({ ...item, page }));
|
||
}
|
||
|
||
// clear 是否清除当前页后面的数据(如果有的话,没有也不影响)
|
||
async function getOrders(page, clear = true) {
|
||
const res = await orderService.getOrderList({ page, pageSize: PAGESIZE });
|
||
if (res.code === 0) {
|
||
setTotal(res.data.count);
|
||
setList((prev) => {
|
||
const newList = [...prev];
|
||
const index = page - 1;
|
||
newList.splice(
|
||
index,
|
||
clear ? newList.length - index : 1,
|
||
addPageInfo(res.data.rows, page)
|
||
);
|
||
return newList;
|
||
});
|
||
}
|
||
}
|
||
|
||
function handleFetchNext() {
|
||
console.log("scroll");
|
||
if (!end) {
|
||
getOrders(list.length + 1);
|
||
}
|
||
}
|
||
|
||
async function handlePayNow(item) {
|
||
try {
|
||
const unPaidRes = await orderService.getUnpaidOrder(item.game_info?.id);
|
||
if (unPaidRes.code === 0 && unPaidRes.data.has_unpaid_order) {
|
||
await payOrder(unPaidRes.data.payment_params);
|
||
getOrders(item.page, false);
|
||
} else {
|
||
throw new Error("支付调用失败");
|
||
}
|
||
} catch (e) {
|
||
console.log(e, 1111);
|
||
Taro.showToast({
|
||
title: e.message,
|
||
icon: "none",
|
||
});
|
||
}
|
||
}
|
||
|
||
function handleViewGame(gameId) {
|
||
if (!gameId) {
|
||
Taro.showToast({ title: "球局未找到", icon: "error" });
|
||
return;
|
||
}
|
||
Taro.navigateTo({
|
||
url: `/game_pages/detail/index?id=${gameId}&from=orderList`,
|
||
});
|
||
}
|
||
|
||
async function handleDeleteOrder(item) {
|
||
const { id: order_id } = item;
|
||
// TODO:删除订单,刷新这一页,然后后面的全清除掉
|
||
const onCancel = () => {
|
||
Dialog.close("cancelOrder");
|
||
};
|
||
const onConfirm = async () => {
|
||
try {
|
||
const deleteRes = await orderService.deleteOrder({
|
||
order_id,
|
||
});
|
||
if (deleteRes.code !== 0) {
|
||
throw new Error(deleteRes.message);
|
||
}
|
||
getOrders(item.page);
|
||
Taro.showToast({
|
||
title: "删除成功",
|
||
icon: "none",
|
||
});
|
||
} catch (e) {
|
||
Taro.showToast({
|
||
title: e.message,
|
||
icon: "error",
|
||
});
|
||
} finally {
|
||
Dialog.close("cancelOrder");
|
||
}
|
||
};
|
||
Dialog.open("cancelOrder", {
|
||
title: "确定删除订单吗?",
|
||
content: (
|
||
<View className={styles.cancelTip}>
|
||
<Text>删除订单后,您将无法恢复订单。请确认是否继续取消?</Text>
|
||
</View>
|
||
),
|
||
footer: (
|
||
<View className={styles.dialogFooter}>
|
||
<Button className={styles.cancel} onClick={onCancel}>
|
||
取消
|
||
</Button>
|
||
<Button className={styles.confirm} type="primary" onClick={onConfirm}>
|
||
确认
|
||
</Button>
|
||
</View>
|
||
),
|
||
onConfirm,
|
||
onCancel,
|
||
});
|
||
}
|
||
|
||
async function handleCancelOrder(item) {
|
||
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);
|
||
}
|
||
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: (
|
||
<View className={styles.cancelTip}>
|
||
<Text>取消订单后,您将无法恢复订单。请确认是否继续取消?</Text>
|
||
</View>
|
||
),
|
||
footer: (
|
||
<View className={styles.dialogFooter}>
|
||
<Button className={styles.cancel} onClick={onCancel}>
|
||
取消
|
||
</Button>
|
||
<Button className={styles.confirm} type="primary" onClick={onConfirm}>
|
||
确认
|
||
</Button>
|
||
</View>
|
||
),
|
||
onConfirm,
|
||
onCancel,
|
||
});
|
||
}
|
||
|
||
function handleQuit(item) {
|
||
if (refundRef.current) {
|
||
refundRef.current.show(item, (result) => {
|
||
if (result) {
|
||
getOrders(item.page);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
function handleViewOrderDetail({ page, id: orderId }) {
|
||
setStorage("list_reload_page_number", page);
|
||
Taro.navigateTo({
|
||
url: `/order_pages/orderDetail/index?id=${orderId}`,
|
||
});
|
||
}
|
||
|
||
const flatList = list.flat();
|
||
|
||
const { statusNavbarHeightInfo } = useGlobalStore();
|
||
const { totalHeight } = statusNavbarHeightInfo;
|
||
|
||
return (
|
||
<View
|
||
className={styles.container}
|
||
style={{ paddingTop: `${totalHeight + 8}px` }}
|
||
>
|
||
<GeneralNavbar
|
||
title="球局订单"
|
||
backgroundColor="transparent"
|
||
titleClassName={styles.titleClassName}
|
||
className={styles.navbar}
|
||
/>
|
||
<ScrollView
|
||
scrollY
|
||
scrollWithAnimation
|
||
lowerThreshold={20}
|
||
onScrollToLower={handleFetchNext}
|
||
enhanced
|
||
showScrollbar={false}
|
||
className={styles.list}
|
||
>
|
||
{/* <View className={styles.bg} /> */}
|
||
{flatList.map((item) => {
|
||
const unPay =
|
||
item.order_status === OrderStatus.PENDING &&
|
||
item.cancel_type === CancelType.NONE;
|
||
const canceled = [CancelType.USER, CancelType.TIMEOUT].includes(
|
||
item.cancel_type
|
||
);
|
||
const { game_info } = item;
|
||
|
||
const {
|
||
skill_level_max,
|
||
skill_level_min,
|
||
play_type,
|
||
participants,
|
||
location_name,
|
||
current_players,
|
||
max_players,
|
||
court_type,
|
||
} = game_info || {};
|
||
|
||
return (
|
||
<View key={item.id} className={styles.orderItem}>
|
||
<View
|
||
className={styles.gameInfo}
|
||
onClick={() => handleViewOrderDetail(item)}
|
||
>
|
||
<View className={styles.gameTitle}>
|
||
<View className={styles.title}>{item?.game_info?.title}</View>
|
||
{!canceled && (
|
||
<View
|
||
className={classnames(
|
||
styles.payNum,
|
||
styles[unPay ? "pending" : "paid"]
|
||
)}
|
||
>
|
||
<Text>
|
||
{unPay
|
||
? "待支付"
|
||
: refundTextMap.get(item.refund_status)}
|
||
</Text>{" "}
|
||
¥ <Text>{item.amount}</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View className={styles.gameTime}>
|
||
{generateTimeMsg(item.game_info || {})}
|
||
</View>
|
||
<View className={styles.address}>
|
||
{insertDotInTags([location_name, court_type, "3.5km"]).map(
|
||
(text, index) => (
|
||
<Text key={index}>{text}</Text>
|
||
)
|
||
)}
|
||
</View>
|
||
<View className={styles.gameOtherInfo}>
|
||
{participants?.length >= 0 ? (
|
||
<View className={styles.avatarCards}>
|
||
{participants.map((participant) => {
|
||
const {
|
||
user: { avatar_url, id },
|
||
} = participant;
|
||
return (
|
||
<Image
|
||
className={styles.avatar}
|
||
mode="aspectFill"
|
||
key={id}
|
||
src={avatar_url}
|
||
/>
|
||
);
|
||
})}
|
||
</View>
|
||
) : (
|
||
""
|
||
)}
|
||
<View className={styles.participantProgress}>
|
||
<Text className={styles.current}>
|
||
报名人数 {current_players}
|
||
</Text>
|
||
<Text>/</Text>
|
||
<Text>{max_players}</Text>
|
||
</View>
|
||
<View className={styles.levelReq}>
|
||
{skill_level_max !== skill_level_min
|
||
? `${skill_level_min || "-"} 至 ${skill_level_max || "-"}`
|
||
: skill_level_min === 1
|
||
? "无要求"
|
||
: `${skill_level_min} 以上`}
|
||
</View>
|
||
<View className={styles.playType}>{play_type}</View>
|
||
</View>
|
||
</View>
|
||
<View className={styles.orderActions}>
|
||
<View className={styles.extraActions}>
|
||
<View
|
||
className={styles.customer}
|
||
onClick={handleCustomerService}
|
||
>
|
||
<Image className={styles.customerIcon} src={CustomerIcon} />
|
||
<Text>客服</Text>
|
||
</View>
|
||
</View>
|
||
<View className={styles.mainActions}>
|
||
{generateOrderActions(
|
||
item,
|
||
{
|
||
handleDeleteOrder,
|
||
handleCancelOrder,
|
||
handleQuit,
|
||
handlePayNow,
|
||
handleViewGame,
|
||
},
|
||
"list"
|
||
)?.map((obj) => (
|
||
<View
|
||
className={classnames(
|
||
styles.button,
|
||
styles[obj.className]
|
||
)}
|
||
>
|
||
<Text className={styles.buttonText}>{obj.text}</Text>
|
||
<Button
|
||
className={styles.transparentButton}
|
||
onClick={obj.action}
|
||
>
|
||
{obj.text}
|
||
</Button>
|
||
</View>
|
||
))}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
);
|
||
})}
|
||
{flatList.length > 0 && end && (
|
||
<View className={styles.endTips}>已经到底了~</View>
|
||
)}
|
||
{flatList.length === 0 && (
|
||
<View className={styles.emptyNotice}>
|
||
<Image mode="widthFix" src={emptyContent} />
|
||
<Text className={styles.emptyTip}>暂时没有订单</Text>
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
<Dialog id="cancelOrder" />
|
||
<RefundPopup ref={refundRef} />
|
||
</View>
|
||
);
|
||
};
|
||
|
||
export default withAuth(OrderList);
|