feat: 分包 除了list
This commit is contained in:
24
src/order_pages/orderDetail/config.ts
Normal file
24
src/order_pages/orderDetail/config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export const DECLAIMER = `
|
||||
GO!仅提供活动发布平台,如在约球活动中发生任何纠纷,请直接与相关方联系,GO!不承担任何责任,但根据需要会依法提供必要的协助。
|
||||
|
||||
发起约球规则
|
||||
1、需至少提前24小时发起活动;
|
||||
2、招募的人数为选择区间,达到最低人数,活动即发起成功;
|
||||
3、活动场地可选择输入(需要在地图上标记位置),选择已订场地的球局如因发起人原因未达成,场地不会自动退订。为避免不必要的损失,如需退订请提前2小时手动申请;
|
||||
4、使用GO!发起活动无须付费预订;
|
||||
5、约球活动结束4天后,队员的报名费自动转入发起人的[微信钱包-余额]中;
|
||||
6、活动自动取消:距约球活动开始时间2小时(时间待讨论),未达到最低招募人数,系统自动取消活动,活动的报名费用全额原路退回;
|
||||
7、活动手动取消:距约球活动开始2小时前可手动取消活动,请至「我的-我的球局」进行取消操作;
|
||||
8、发起人可距约球活动开始2小时前,对该活动进行编辑。
|
||||
① 若无人报名时,可以对约球活动详情进行编辑。「我的球局-约球-点击发起的约球」在最下方点击编辑,可对约球活动名称,招募人数,发起方人数,级别要求,报名费用,联系方式,事项备注进行修改;
|
||||
② 取消约球:可于「我的-我的球局-我发起的」进行取消约球操作;
|
||||
③ 对报名队员进行编辑:在「我的-我的球局-我发起的-点击约球活动-球友」对加入的队员进行管理,若操作不约的队员自动退款并不可再报名加入这次活动。
|
||||
|
||||
参与约球规则
|
||||
1、活动开始前均可报名;若发起人允许,活动已开始未满员也可替补报名
|
||||
2、参与者不提供取消报名方式,可于约球活动开始2小时前联系发起者删除报名;
|
||||
|
||||
异常处理场景说明
|
||||
发起人临时失联/爽约;发起人恶意删除队员,GO!支持全额退款
|
||||
参与者爽约不通知,不可退款但鼓励用户评分机制中反馈,平台将限制其部分功能使用(如发起权限、报名权限等)。
|
||||
`;
|
||||
4
src/order_pages/orderDetail/index.config.ts
Normal file
4
src/order_pages/orderDetail/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: "订单详情",
|
||||
navigationBarBackgroundColor: "#FAFAFA",
|
||||
});
|
||||
434
src/order_pages/orderDetail/index.module.scss
Normal file
434
src/order_pages/orderDetail/index.module.scss
Normal file
@@ -0,0 +1,434 @@
|
||||
@use "~@/scss/images.scss" as img;
|
||||
|
||||
.errorTip {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.gameInfoContainer {
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
.paidInfo {
|
||||
border-bottom: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
padding: 8px 12px;
|
||||
color: #000;
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: -0.23px;
|
||||
}
|
||||
|
||||
.gameStatus {
|
||||
padding: 12px 12px 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.23px;
|
||||
|
||||
.statusText {
|
||||
color: #000;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.gameInfo {
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
&DateWeather {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
// gap: 12px;
|
||||
|
||||
&CalendarDate {
|
||||
/* width: 60%; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
&Calendar {
|
||||
display: flex;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 12px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
background: #fff;
|
||||
|
||||
.month {
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
font-size: 10px;
|
||||
display: flex;
|
||||
padding: 1px auto;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: #ff3b30;
|
||||
}
|
||||
|
||||
.day {
|
||||
display: flex;
|
||||
width: 48px;
|
||||
height: 30px;
|
||||
color: #000;
|
||||
// padding-bottom: 6px;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
// border: 0.5px solid rgba(255, 255, 255, 0.08);
|
||||
// background: rgba(255, 255, 255, 0.25);
|
||||
// background-color: #536272;
|
||||
}
|
||||
}
|
||||
|
||||
&Date {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-evenly;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
color: #fff;
|
||||
|
||||
.date {
|
||||
color: #000;
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 24px; /* 150% */
|
||||
}
|
||||
|
||||
.venueTime {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 166.667% */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&Place {
|
||||
.locationMessage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
&Icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 1px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&Image {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&Text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-evenly;
|
||||
gap: 4px;
|
||||
align-self: stretch;
|
||||
|
||||
&NameDistance {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 24px; /* 150% */
|
||||
|
||||
&Arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&Address {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
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; /* 166.667% */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gameInfoActions {
|
||||
min-height: 12px;
|
||||
border-top: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
}
|
||||
|
||||
.orderSummary {
|
||||
margin-top: 20px;
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.summaryList {
|
||||
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);
|
||||
|
||||
.summaryItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
// height: 24px;
|
||||
|
||||
.title {
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.content {
|
||||
color: #000;
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
|
||||
.location {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.declaimer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding-bottom: 100px;
|
||||
.title {
|
||||
display: flex;
|
||||
padding: 15px 0 0;
|
||||
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;
|
||||
}
|
||||
|
||||
.content {
|
||||
color: rgba(22, 24, 35, 0.6);
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 171.429% */
|
||||
letter-spacing: 0.28px;
|
||||
}
|
||||
}
|
||||
|
||||
.payButton {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
left: 12px;
|
||||
width: 345px;
|
||||
height: 54px;
|
||||
width: calc(100vw - 24px);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
box-shadow: 0 8px 64px 0 rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(16px);
|
||||
font-feature-settings:
|
||||
"liga" off,
|
||||
"clig" off;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
359
src/order_pages/orderDetail/index.tsx
Normal file
359
src/order_pages/orderDetail/index.tsx
Normal file
@@ -0,0 +1,359 @@
|
||||
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}>
|
||||
{Boolean(order_status) && 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: (
|
||||
<View className={styles.location}>
|
||||
<Text>{location}</Text>
|
||||
<Text>{location_name}</Text>
|
||||
</View>
|
||||
),
|
||||
},
|
||||
{
|
||||
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>
|
||||
<View className={styles.content}>{item.content}</View>
|
||||
</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} onClick={handlePay}>
|
||||
{orderDetail.order_status === OrderStatus.PENDING ? "继续" : "确认"}
|
||||
支付
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(OrderCheck);
|
||||
4
src/order_pages/orderList/index.config.ts
Normal file
4
src/order_pages/orderList/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '订单列表',
|
||||
navigationBarBackgroundColor: '#FAFAFA'
|
||||
})
|
||||
217
src/order_pages/orderList/index.module.scss
Normal file
217
src/order_pages/orderList/index.module.scss
Normal file
@@ -0,0 +1,217 @@
|
||||
@use "~@/scss/images.scss" as img;
|
||||
|
||||
.container {
|
||||
padding: 12px;
|
||||
background-color: #fafafa;
|
||||
min-height: 100vh;
|
||||
|
||||
.orderItem {
|
||||
width: 100%;
|
||||
height: 222px;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 4px 36px 0 rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 12px;
|
||||
|
||||
.orderTitle {
|
||||
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 {
|
||||
height: 122px;
|
||||
}
|
||||
|
||||
.orderActions {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
241
src/order_pages/orderList/index.tsx
Normal file
241
src/order_pages/orderList/index.tsx
Normal file
@@ -0,0 +1,241 @@
|
||||
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([]);
|
||||
|
||||
useDidShow(() => {
|
||||
getOrders();
|
||||
});
|
||||
|
||||
async function getOrders() {
|
||||
const res = await orderService.getOrderList();
|
||||
console.log(res);
|
||||
if (res.code === 0) {
|
||||
setList(res.data.rows);
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
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: `/order_pages/orderDetail/index?id=${orderId}`,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<View className={styles.container}>
|
||||
{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 (
|
||||
<View key={item.id} className={styles.orderItem}>
|
||||
<View className={styles.orderTitle}>
|
||||
<View className={styles.userInfo}>
|
||||
<Avatar
|
||||
className={styles.avatar}
|
||||
src="https://img.yzcdn.cn/vant/cat.jpeg"
|
||||
/>
|
||||
<View className={styles.nickName}>
|
||||
<Text className={styles.nickNameText}>Light</Text>
|
||||
<Image
|
||||
className={styles.arrowRight}
|
||||
src={orderListArrowRight}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{expired ? (
|
||||
""
|
||||
) : (
|
||||
<View className={styles.paidInfo}>
|
||||
<Text
|
||||
className={classnames(
|
||||
styles.payTime,
|
||||
styles[unPay ? "pending" : "paid"],
|
||||
)}
|
||||
>
|
||||
{unPay
|
||||
? `请在 ${expiredTime} 前支付`
|
||||
: dayjs(item.pay_time).format("YYYY-MM-DD HH:mm:ss")}
|
||||
</Text>
|
||||
<Text
|
||||
className={classnames(
|
||||
styles.payNum,
|
||||
styles[unPay ? "pending" : "paid"],
|
||||
)}
|
||||
>
|
||||
{unPay ? "待支付" : "已支付"} ¥ {item.amount}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
className={styles.gameInfo}
|
||||
onClick={handleViewOrderDetail.bind(null, item.id)}
|
||||
>
|
||||
{item?.game_info?.title}
|
||||
</View>
|
||||
<View className={styles.orderActions}>
|
||||
<View className={styles.extraActions}></View>
|
||||
<View className={styles.mainActions}>
|
||||
{showCancel && (
|
||||
<Button
|
||||
className={classnames(styles.button, styles.cancelOrder)}
|
||||
onClick={handleCancelOrder.bind(null, item)}
|
||||
>
|
||||
取消订单
|
||||
</Button>
|
||||
)}
|
||||
{unPay && !expired && (
|
||||
<Button
|
||||
className={classnames(styles.button, styles.payNow)}
|
||||
onClick={handlePayNow.bind(null, item.game_info.id)}
|
||||
>
|
||||
立即支付
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
<Dialog id="cancelOrder" />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(OrderList);
|
||||
Reference in New Issue
Block a user