diff --git a/src/components/UploadCover/index.tsx b/src/components/UploadCover/index.tsx index eaf9d51..07b4ff0 100644 --- a/src/components/UploadCover/index.tsx +++ b/src/components/UploadCover/index.tsx @@ -126,7 +126,7 @@ export default function UploadCover(props: UploadCoverProps) { value.map((item) => { return ( - + onDelete(item)} /> ) diff --git a/src/components/UploadCover/upload-source-popup.tsx b/src/components/UploadCover/upload-source-popup.tsx index 6858b44..ddd4004 100644 --- a/src/components/UploadCover/upload-source-popup.tsx +++ b/src/components/UploadCover/upload-source-popup.tsx @@ -128,7 +128,7 @@ export default forwardRef(function UploadImage(props: UploadImageProps, ref) { const isSelected = checkImageSelected(selectedImages, item) return ( handleImageClick(item)}> - + {isSelected ? ( diff --git a/src/game_pages/detail/index.scss b/src/game_pages/detail/index.scss index c8996da..da114ba 100644 --- a/src/game_pages/detail/index.scss +++ b/src/game_pages/detail/index.scss @@ -162,7 +162,6 @@ &-image { width: 28px; height: 28px; - border-radius: 50%; } } @@ -690,9 +689,11 @@ background: rgba(255, 255, 255, 0.16); flex: 0 0 auto; - &-avatar { + .participants-list-item-avatar { width: 60px; height: 60px; + border-radius: 50%; + overflow: hidden; } &-name { @@ -836,6 +837,8 @@ &-avatar { width: 40px; height: 40px; + border-radius: 50%; + object-fit: cover; } &-message { @@ -1012,6 +1015,8 @@ &-avatar { width: 20px; height: 20px; + border-radius: 50%; + object-fit: cover; } &-message { diff --git a/src/game_pages/detail/index.tsx b/src/game_pages/detail/index.tsx index f9981b9..29bc5bf 100644 --- a/src/game_pages/detail/index.tsx +++ b/src/game_pages/detail/index.tsx @@ -38,8 +38,8 @@ function insertDotInTags(tags: string[]) { } function GameTags(props) { - const { userInfo } = props; - const { avatar_url } = userInfo; + const { userInfo, handleViewUserInfo } = props; + const { avatar_url, id } = userInfo; const tags = [ { name: "🕙 急招", @@ -64,7 +64,9 @@ function GameTags(props) { {/* network image mock */} @@ -596,6 +598,7 @@ function VenueInfo(props) { > @@ -661,7 +664,7 @@ function GamePlayAndRequirement(props) { // 参与者 function Participants(props) { - const { detail = {}, handleJoinGame } = props; + const { detail = {}, handleJoinGame, handleViewUserInfo } = props; const participants = detail.participants || []; const { participant_count, @@ -724,9 +727,11 @@ function Participants(props) { participant_user_id === organizer_id ? "组织者" : "参与者"; return ( - {nickname || "未知"} @@ -808,7 +813,7 @@ function genRecommendGames(games, location, avatar) { avatar, applications: max_players, checkedApplications: current_players, - levelRequirements: `NTRP ${genNTRPRequirementText(skill_level_min, skill_level_max)}`, + levelRequirements: skill_level_max !== skill_level_min ? `${skill_level_min || '-'}至${skill_level_max || '-'}` : skill_level_min === 1 ? '无要求' : `${skill_level_min}以上`, playType: play_type, }; }); @@ -819,6 +824,7 @@ function OrganizerInfo(props) { userInfo, currentLocation: location, onUpdateUserInfo = () => {}, + handleViewUserInfo, } = props; const { id, @@ -855,11 +861,11 @@ function OrganizerInfo(props) { } }; - const viewOtherPage = () => { - Taro.navigateTo({ - url: `/user_pages/other/index?userid=${id}`, - }); - }; + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=current` + }) + } return ( @@ -869,7 +875,7 @@ function OrganizerInfo(props) { {/* organizer avatar and name */} - + {nickname} @@ -909,7 +915,7 @@ function OrganizerInfo(props) { {/* recommend games by organizer */} - {}}> + TA的更多活动 {recommendGames.map((game, index) => ( - + {/* game title */} {game.title} @@ -943,9 +949,11 @@ function OrganizerInfo(props) { {/* organizer avatar、applications、level requirements、play type */} - { e.stopPropagation(); handleViewUserInfo(id) }} /> @@ -1049,6 +1057,12 @@ function Index() { } } + function handleViewUserInfo(userId) { + Taro.navigateTo({ + url: `/user_pages/other/index?userid=${userId}` + }) + } + console.log("detail", detail); const backgroundImage = detail?.image_list?.[0] ? { backgroundImage: `url(${detail?.image_list?.[0]})` } @@ -1079,7 +1093,7 @@ function Index() { {/* content */} {/* avatar and tags */} - + {/* title */} {detail.title} @@ -1091,7 +1105,7 @@ function Index() { {/* gameplay requirements */} {/* participants */} - + {/* supplemental notes */} {/* organizer and recommend games by organizer */} @@ -1100,6 +1114,7 @@ function Index() { userInfo={userInfo} currentLocation={currentLocation} onUpdateUserInfo={onUpdateUserInfo} + handleViewUserInfo={handleViewUserInfo} /> {/* sticky bottom action bar */} { - const [, theTime] = item.application_time.split("undefined "); - const theTimeObj = dayjs(theTime); + 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"); + const time = theTimeObj.format("HH:mm"); return { - time: `${year}年${month}月${day}日${time}${index === 0 ? "前" : "后"}`, + time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, rule: item.refund_rule, }; }), diff --git a/src/order_pages/orderList/index.module.scss b/src/order_pages/orderList/index.module.scss index e42fc43..3426f13 100644 --- a/src/order_pages/orderList/index.module.scss +++ b/src/order_pages/orderList/index.module.scss @@ -1,81 +1,158 @@ @use "~@/scss/images.scss" as img; .container { - padding: 12px; + padding: 12px 12px 40px; background-color: #fafafa; - min-height: 100vh; + height: 100vh; + width: 100%; + box-sizing: border-box; + + .list { + height: 100%; + width: 100%; + position: relative; + background-color: #fff; + + // .bg { + // position: absolute; + // left: 0; + // top: 0; + // width: 100%; + // height: 100%; + // background-color: #fafafa; + // z-index: -1; + // } + + .endTips { + height: 40px; + display: flex; + align-items: center; + justify-content: center; + 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: 500; + line-height: 18px; + background-color: #f9f9f9; + } + } .orderItem { width: 100%; - height: 222px; + // 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; + // .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 { + // .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; + + .gameTitle { display: flex; align-items: center; - justify-content: flex-start; - gap: 6px; + justify-content: space-between; + padding: 12px 15px 0; - .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; + .title { + overflow: hidden; + color: #000; + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; font-family: "PingFang SC"; - font-size: 12px; + font-size: 16px; font-style: normal; - font-weight: 400; - line-height: 18px; - - &.paid { - color: rgba(60, 60, 67, 0.6); - } - - &.pending { - color: #000; - } + font-weight: 600; + line-height: 24px; /* 150% */ } .payNum { @@ -96,14 +173,96 @@ } } } - } - .gameInfo { - height: 122px; + .gameTime { + padding: 6px 0 0 15px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + color: rgba(60, 60, 67, 0.60); + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 150% */ + } + + .address { + padding: 6px 0 0 15px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + + color: rgba(60, 60, 67, 0.60); + font-feature-settings: 'liga' off, 'clig' off; + text-overflow: ellipsis; + font-family: "PingFang SC"; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 150% */ + } + + .gameOtherInfo { + padding: 8px 0 12px 15px; + height: 20px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + + .avatarCards { + display: flex; + align-items: center; + justify-content: flex-start; + height: 20px; + + .avatar { + width: 20px; + height: 20px; + border-radius: 50%; + border: 1px solid rgba(0, 0, 0, 0.06); + &+.avatar { + margin-left: -10px; + } + } + } + + .participantProgress, .levelReq, .playType { + display: flex; + height: 20px; + padding: 0px 8px; + align-items: center; + gap: 4px; + border-radius: 999px; + border: 0.5px solid rgba(0, 0, 0, 0.16); + background: #FFF; + color: #000; + font-feature-settings: 'liga' off, 'clig' off; + font-family: "PingFang SC"; + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 20px; /* 181.818% */ + letter-spacing: -0.23px; + } + + .participantProgress { + color: #c4c4c7; + + .current { + color: #000; + } + } + } } .orderActions { - height: 28px; + min-height: 28px; padding: 12px 12px 15px; border-top: 1px solid rgba(0, 0, 0, 0.06); @@ -135,6 +294,9 @@ &:last-child { background: #000; color: #fff; + &.payNow { + background-color: #ff3b30; + } } } @@ -142,6 +304,7 @@ } .payNow { + background-color: #ff3b30; } } } @@ -215,3 +378,47 @@ } } } + +.dialogFooter { + // width: 100%; + width: calc(100% + 1px); + height: 44px; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: flex-end; + position: absolute; + // margin: 0 -24px -24px; + bottom: 0; + left: 0; + border-top: 1px solid rgba(0, 0, 0, 0.06); + border-bottom-left-radius: 16px; + border-bottom-right-radius: 16px; + overflow: hidden; + + & > .cancel, & > .confirm { + padding: 12px 10px; + height: 44px; + width: 50%; + text-align: center; + // border: 0.5px solid rgba(0, 0, 0, 0.06); + color: #000; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: 20px; + + &:last-child { + background: #000; + color: #fff; + } + } + + & > .cancel { + border-radius: 0; + } + + & > .confirm { + border-radius: 0; + } +} diff --git a/src/order_pages/orderList/index.tsx b/src/order_pages/orderList/index.tsx index 070b52b..8f9adcd 100644 --- a/src/order_pages/orderList/index.tsx +++ b/src/order_pages/orderList/index.tsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { View, Text, Button, Image } from "@tarojs/components"; +import React, { useState, useEffect } 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"; @@ -8,29 +8,98 @@ 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"; +// import orderListArrowRight from "@/static/order/orderListArrowRight.svg"; +dayjs.locale("zh-cn"); + +const PAGESIZE = 20; + +// 将·作为连接符插入到标签文本之间 +function insertDotInTags(tags: string[]) { + return tags.join("-·-").split("-"); +} + +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 ( + <> + + {diffDay <= 2 + ? diffDayMap.get(diffDay) + : startTime.format("YYYY-MM-DD")} + + ({DayOfWeekMap.get(dayofWeek)}) + {gameLength} + + ); +} const OrderList = () => { - const [list, setList] = useState([]); + const [list, setList] = useState([]); + const [total, setTotal] = useState(0); - useDidShow(() => { - getOrders(); - }); + const end = list.length * PAGESIZE >= total; - async function getOrders() { - const res = await orderService.getOrderList(); - console.log(res); + useEffect(() => { + getOrders(1); + }, []); + + 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) { - setList(res.data.rows); + 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; + }); } } - async function handlePayNow(gameId) { + function handleFetchNext() { + console.log("scroll"); + if (!end) { + getOrders(list.length + 1); + } + } + + async function handlePayNow(item) { try { - const unPaidRes = await orderService.getUnpaidOrder(gameId); + 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(); + getOrders(item.page, false); } else { throw new Error("支付调用失败"); } @@ -43,6 +112,12 @@ const OrderList = () => { } } + function handleViewGame(gameId) { + Taro.navigateTo({ + url: `/game_pages/detail/index?id=${gameId}&from=orderList`, + }); + } + function renderCancelContent(checkOrderInfo) { const { refund_policy = [] } = checkOrderInfo; const policyList = [ @@ -51,14 +126,18 @@ const OrderList = () => { rule: "退款规则", }, ...refund_policy.map((item, index) => { - const [, theTime] = item.application_time.split("undefined "); - const theTimeObj = dayjs(theTime); + 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}${index === 0 ? "前" : "后"}`, + time: `${year}年${month}月${day}日${time} ${isLast ? "后" : "前"}`, rule: item.refund_rule, }; }), @@ -81,34 +160,104 @@ const 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: "删除订单后,您将无法恢复订单。请确认是否继续取消?", + footer: ( + + + + + ), + onConfirm, + onCancel, + }) + } + async function handleCancelOrder(item) { const { order_no, order_status, game_info, amount } = item; if (order_status === OrderStatus.PENDING) { + 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: "取消订单后,您将无法恢复订单。请确认是否继续取消?", - 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"); - }, + footer: ( + + + + + ), + onConfirm, + onCancel, }); return; } @@ -126,7 +275,11 @@ const OrderList = () => { if (refundRes.code !== 0) { throw new Error(refundRes.message); } - getOrders(); + getOrders(item.page, false); + Taro.showToast({ + title: "退出成功", + icon: "none", + }) } catch (e) { Taro.showToast({ title: e.message, @@ -150,89 +303,149 @@ const OrderList = () => { return ( - {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; + + {/* */} + {list.flat().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.PENDING && + item.cancel_type === CancelType.NONE; + const showQuit = + item.order_status === OrderStatus.PAID && + item.cancel_type === CancelType.NONE; - return ( - - - - - - Light - - - - {expired ? ( - "" - ) : ( - - - {unPay - ? `请在 ${expiredTime} 前支付` - : dayjs(item.pay_time).format("YYYY-MM-DD HH:mm:ss")} - - + + + {item?.game_info?.title} + - {unPay ? "待支付" : "已支付"} ¥ {item.amount} - + {unPay && !expired ? "待支付" : "已支付"} ¥{" "} + {item.amount} + + + + {generateTimeMsg(item.game_info)} + + + {insertDotInTags([location_name, court_type, "3.5km"]).map( + (text, index) => ( + {text} + ) + )} + + + {participants.length >= 0 ? ( + + {/* participants */[{ user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 1 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 2 } }, { user: { avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', id: 3 } }].map((participant) => { + const { + user: { avatar_url, id }, + } = participant; + return ; + })} + + ) : ( + "" + )} + + 报名人数 {current_players} + / + {max_players} + + + {skill_level_max !== skill_level_min + ? `${skill_level_min || "-"} 至 ${skill_level_max || "-"}` + : skill_level_min === 1 + ? "无要求" + : `${skill_level_min} 以上`} + + {play_type} + + + + + + {expired && ( + + )} + {showCancel && ( + + )} + {showQuit && ( + + )} + {unPay && !expired ? ( + + ) : ( + + )} - )} - - - {item?.game_info?.title} - - - - - {showCancel && ( - - )} - {unPay && !expired && ( - - )} - - ); - })} + ); + })} + {end && 已经到底了~} + ); diff --git a/src/services/orderService.ts b/src/services/orderService.ts index 8b2646f..fbf6a05 100644 --- a/src/services/orderService.ts +++ b/src/services/orderService.ts @@ -78,8 +78,8 @@ export interface GameOrderRes { // 发布球局类 class OrderService { // 查询订单列表 - async getOrderList() { - return httpService.post("/user/orders", {}, { showLoading: true }); + async getOrderList(pagination: { page: number, pageSize: number }) { + return httpService.post("/user/orders", pagination, { showLoading: true }); } // 获取订单详情 @@ -161,6 +161,21 @@ class OrderService { }, ); } + + // 删除订单 + async deleteOrder({ + order_id, + }: { + order_id: number; + }): Promise> { + return httpService.post( + "/payment/delete_order", + { order_id }, + { + showLoading: true, + }, + ); + } } // 导出认证服务实例