zhejiushizhuce 2 rokov pred
rodič
commit
ec71a059bf

+ 1 - 1
src/assets/singlesvg/succss.svg

@@ -1,5 +1,5 @@
 <svg width="55" height="55" viewBox="0 0 55 55" fill="none" xmlns="http://www.w3.org/2000/svg" class="s-ion-icon">
-<rect width="55" height="55" rx="27.5" fill="#27D48B"></rect>
+<rect width="55" height="55" rx="27.5" fill="#3BC117"></rect>
 <rect x="41.2139" y="18.03" width="3" height="30" rx="1.5" transform="rotate(45 41.2139 18.03)" fill="white"></rect>
 <rect width="3" height="14.5074" rx="1.5" transform="matrix(-0.707107 0.707107 0.707107 0.707107 14 29)" fill="white"></rect>
 </svg>

+ 5 - 0
src/assets/singlesvg/succss_old.svg

@@ -0,0 +1,5 @@
+<svg width="55" height="55" viewBox="0 0 55 55" fill="none" xmlns="http://www.w3.org/2000/svg" class="s-ion-icon">
+<rect width="55" height="55" rx="27.5" fill="#27D48B"></rect>
+<rect x="41.2139" y="18.03" width="3" height="30" rx="1.5" transform="rotate(45 41.2139 18.03)" fill="white"></rect>
+<rect width="3" height="14.5074" rx="1.5" transform="matrix(-0.707107 0.707107 0.707107 0.707107 14 29)" fill="white"></rect>
+</svg>

+ 1 - 0
src/constants/routes.js

@@ -28,6 +28,7 @@ const ROUTE_PATH={
     MESSAGE:"#/message",
     MESSAGE_DETAIL:"#/messageDetail",
     RECHARGE_HISTORY:"#/rechargeHistory",
+    WITHDRAW_REACORD:"#/withdrawRecord",
 }
 
 export {

+ 13 - 0
src/db/routes.js

@@ -243,6 +243,19 @@ export const routes=[
         lpath:"#/signin",//登录path
         subs:[]
     },
+    {
+        path: "#/withdrawRecord",
+        type: 1,
+        side: 0,
+        block:1,//块排序
+        title: "Withdrawal Record",
+        icon:"",
+        fill:";;",//icon 颜色 默认 选中 本身颜色
+        component:"withdrawRecord",
+        login:1,//0,1 检不检查登录
+        lpath:"#/signin",//登录path
+        subs:[]
+    },
     {
         path: "#/customer",
         type: 1,

+ 35 - 2
src/features/paySlice.js

@@ -151,7 +151,8 @@ export const Pay=createSlice({
             GetAccountsPayInfo:REQ_STATUS.NONE,
             PaymentOrder:REQ_STATUS.NONE,
             ChangePayPass:REQ_STATUS.NONE,
-            PayOrderList:REQ_STATUS.NONE
+            PayOrderList:REQ_STATUS.NONE,
+            WithdrawRecoard:REQ_STATUS.NONE
         },
         config:[],
         payconfig:null,
@@ -161,7 +162,30 @@ export const Pay=createSlice({
         WithDrawTax:4,
         withdrawBaseInfo:null,
         withdrawInfo:null,
-        payOrderList:null
+        payOrderList:null,
+        withdrawRecord:[
+            {
+                CreateDate:1716706224,
+                OrderID:"BR12345678901",
+                State:2,
+                ExchangeScore:10000,
+                Fee:1000
+            },
+            {
+                CreateDate:1716706224,
+                OrderID:"BR12345678901",
+                State:2,
+                ExchangeScore:10000,
+                Fee:1000
+            },
+            {
+                CreateDate:1716706224,
+                OrderID:"BR12345678901",
+                State:3,
+                ExchangeScore:10000,
+                Fee:1000
+            }
+        ]
     },
     reducers: {
         updatePayFinish:((state, action) => {
@@ -250,6 +274,15 @@ export const getPayOrderStatus=(state)=>{
 export const getPayOrderList=(state)=>{
     return state.pay.payOrderList;
 }
+export const getWithdrawRecordStatus=(state)=>{
+    return state.pay.status.WithdrawRecoard;
+}
+export const getWithdrawRecord=(state)=>{
+    return state.pay.withdrawRecord;
+}
+export const getWithdrawTax=(state)=>{
+    return state.pay.WithDrawTax;
+}
 export default Pay.reducer;
 
 export const reqPayConfig=(dispatch,userInfo)=>{

+ 25 - 0
src/locales/languages/en_US.json

@@ -286,6 +286,31 @@
         "err_not_same": "The password input is inconsistent, please re-enter it."
       },
       "pass_box_title": "Enter the withdrawal password"
+    },
+    "record": {
+      "title": "Withdrawal Record",
+      "time": "Request Time",
+      "orderId": "Order ID",
+      "amount": "Amount",
+      "exchage": "Exchange Score",
+      "fee": "Fee",
+      "state": "Status",
+
+      "wait": "Waiting",
+      "fail": "Failure",
+      "success": "Complete",
+      "processing": "Processing",
+      "detail_title": "Record Details"
+    },
+    "help": {
+      "title": "Withdrawal Rules",
+      "rule_1": "1. The minimum/maximum amount which you can withdraw every time is {0}/ {1};",
+      "rule_2": "2. You can extract the amount depends on the total amount that you lose and win;",
+      "rule_3": "3. The maximum number of withdrawal applications per day: {0} times;",
+      "rule_4": "4. We will deal with withdrawal requests from 9 AM to 6 PM, everyMonday to Friday, except holidays;",
+      "rule_5": "5. You can probably receive the amount in 2-12 hours;",
+      "rule_6": "6. The unsuccessful withdrawal amount will be refunded directly, You can check the reason in Mail box;",
+      "rule_tips": "PS:{0}% of the withdrawal amount will be deducted as payment gateway fee;"
     }
   },
   "customer": {

+ 10 - 0
src/pages/WithdrawRecord.jsx

@@ -0,0 +1,10 @@
+import WWithdrawRecord from "@widgets/WWithdraw/WWithdrawRecord/WWithdrawRecord";
+
+const WithdrawRecord = ({...props}) => {
+    return (
+        <>
+            <WWithdrawRecord {...props}/>
+        </>
+    )
+}
+export default WithdrawRecord;

+ 3 - 1
src/pages/index.jsx

@@ -20,6 +20,7 @@ const CashWheel=lazy(()=>import("@pages/CashWheel"));
 const Mail=lazy(()=>import("@pages/Mail"));
 const MailDetail=lazy(()=>import("@pages/MailDetail"));
 const RechargeHistory=lazy(()=>import("@pages/RechargeHistory"));
+const WithdrawRecord=lazy(()=>import("@pages/WithdrawRecord"))
 export default {
     ModulePage:ModulePage,
     Home:Home,
@@ -38,7 +39,8 @@ export default {
     cashwheel:CashWheel,
     mail:Mail,
     mailDetail:MailDetail,
-    rechargeHistory:RechargeHistory
+    rechargeHistory:RechargeHistory,
+    withdrawRecord:WithdrawRecord
 }
 export const Header={
     deposit: DepositHeader,

+ 208 - 0
src/widgets/WWithdraw/WWithdrawRecord/WWithdrawRecord.jsx

@@ -0,0 +1,208 @@
+import styles from './styles.module.scss';
+import {useSelector} from "react-redux";
+import {getWithdrawRecord, getWithdrawRecordStatus} from "@features/paySlice";
+import React, {useEffect, useState} from "react";
+import {REQ_STATUS} from "@api/config";
+import SmallLoading from "@components/SmallLoading";
+import Spring from "@components/Spring";
+import {formatCash, formatDate} from "@utils/helpers";
+import {CopyToClipboard} from "react-copy-to-clipboard";
+import {toast} from "react-toastify";
+import SvgIcon from "@components/SvgIcon";
+import Popup from "@components/Popup";
+import {ReactComponent as SuccssLogo} from '@assets/singlesvg/succss.svg';
+import {ReactComponent as PendingLogo} from '@assets/singlesvg/pending.svg';
+import {ReactComponent as FailedLogo} from '@assets/singlesvg/failed.svg';
+const i18n=global.i18n;
+const stateType = {
+    ERROR: -1,                  // 订单错误
+    CHECKING: 1,                // 审核中
+    SUCCESS: 2,                 // 提现成功
+    FAIL: 3,                    // 提现失败
+    RECALL: 4,                  // 订单撤回
+    PROCESSING: 5,              // 处理中
+    TTP_ERROR: 6,               // 三方订单异常
+};
+const WWithdrawRecord=({close})=>{
+    const status=useSelector(getWithdrawRecordStatus);
+    const recordList=useSelector(getWithdrawRecord);
+    const [showDetails,setShowDetails]=useState(false);
+    const [detailInfo,setDetailInfo]=useState(null);
+    useEffect(()=>{
+
+    },[])
+    const getStateTxt=(state)=>{
+        switch(state) {
+            case stateType.CHECKING: {
+                return i18n.t("withdraw.record.wait");
+            }
+            case stateType.SUCCESS: {
+                return i18n.t("withdraw.record.success");
+            }
+            case stateType.PROCESSING: {
+                return i18n.t("withdraw.record.processing");
+            }
+            case stateType.ERROR:
+            case stateType.FAIL:
+            case stateType.TTP_ERROR:
+            case stateType.RECALL: {
+                return i18n.t("withdraw.record.fail");
+            }
+            default: return "error";
+        }
+    }
+    const getStateCls=(state)=>{
+        switch(state) {
+            case stateType.PROCESSING:
+            case stateType.CHECKING: {
+                return styles.status_pending;
+            }
+            case stateType.SUCCESS: {
+                return styles.status_success;
+            }
+            case stateType.ERROR:
+            case stateType.FAIL:
+            case stateType.RECALL: {
+                return styles.status_failed;
+            }
+            default: return styles.status_pending;
+        }
+    }
+    const getStateLogo=(state)=>{
+        switch(state) {
+            case stateType.PROCESSING:
+            case stateType.CHECKING: {
+                return PendingLogo;
+            }
+            case stateType.SUCCESS: {
+                return SuccssLogo;
+            }
+            case stateType.ERROR:
+            case stateType.FAIL:
+            case stateType.RECALL: {
+                return FailedLogo;
+            }
+            default: return PendingLogo;
+        }
+    }
+    const renderContent=()=>{
+        if (status===REQ_STATUS.LOADING)return (<SmallLoading></SmallLoading>);
+        if (recordList==null)return (<></>);
+        return (
+            <Spring type="slideUp" className={styles.content}>
+                {
+                    recordList.map((item,index)=>{
+                        let amount=Number(item.ExchangeScore)+Number(item.Fee);
+                        let amount_str=global.DOLLAR+formatCash(amount);
+                        let time_str=formatDate(item.CreateDate*1000);
+                        let statusTxt=getStateTxt(item.State);
+                        let status_cls=getStateCls(item.State);
+                        return (
+                            <button key={index} className={styles.item_con} onClick={()=>{
+                                setDetailInfo(item);
+                                setShowDetails(true);
+                            }}>
+                                <div className={styles.item}>
+                                    <div className={styles.title}>
+                                        {amount_str}
+                                    </div>
+                                </div>
+                                <div className={styles.item}>
+                                    <div>{time_str}</div>
+                                </div>
+                                <div className={styles.item}>
+                                    <CopyToClipboard text={item.OrderID} onCopy={() => {
+                                        toast.success(i18n.t("copy_success"));
+                                    }}>
+                                        <div className={styles.payment_sn_con} onClick={(event) => {
+                                            event.stopPropagation();
+                                        }}>
+                                            {item.OrderID}
+                                            <SvgIcon className={styles.logo} herf={"icon_Copy"}/>
+                                        </div>
+                                    </CopyToClipboard>
+                                    <div className={status_cls}>
+                                        {statusTxt}
+                                    </div>
+                                </div>
+                            </button>
+                        )
+                    })
+                }
+            </Spring>
+        )
+    }
+    const renderDetails=()=>{
+        let item=detailInfo;
+        if (item==null)return (<></>);
+        let ExchangeScore=Number(item.ExchangeScore);
+        let Fee=Number(item.Fee);
+        let amount=ExchangeScore+Fee;
+        let amount_str=global.DOLLAR+formatCash(amount);
+        let status=parseInt(item.State);
+        let statusTxt=getStateTxt(status);
+        let status_cls=getStateCls(status);
+        let Logo=getStateLogo(status);
+        return (
+            <Popup onClose={()=>{
+                setShowDetails(false)
+            }} open={showDetails}>
+                <div className={styles.detail}>
+                    <div className={styles.top}>
+                        <div>{i18n.t("withdraw.record.detail_title")}</div>
+                        <div className={styles.status}>
+                            <Logo className={styles.logo}></Logo>
+                            <div className={status_cls}>
+                                {statusTxt}
+                            </div>
+                        </div>
+                        <div>{amount_str}</div>
+                    </div>
+                    <div className={styles.line}></div>
+                    <div className={styles.bottom}>
+                        <div className={styles.item}>
+                            <div
+                                className={styles.label}>{i18n.t("withdraw.record.exchage")}</div>
+                            <div>{global.DOLLAR + formatCash(ExchangeScore)}</div>
+                        </div>
+                        <div className={styles.item}>
+                            <div
+                                className={styles.label}>{i18n.t("withdraw.record.fee")}</div>
+                            <div>{global.DOLLAR + formatCash(Fee)}</div>
+                        </div>
+                        <div className={styles.item}>
+                            <div
+                                className={styles.label}>{i18n.t("withdraw.record.time")}</div>
+                            <div>{formatDate(item.CreateDate*1000)}</div>
+                        </div>
+                        <div className={styles.item}>
+                            <div
+                                className={styles.label}>{i18n.t("withdraw.record.orderId")}</div>
+                            <CopyToClipboard text={item.OrderID} onCopy={() => {
+                                toast.success(i18n.t("copy_success"));
+                            }}>
+                                <div className={styles.number}>
+                                    {item.OrderID}
+                                    <SvgIcon className={styles.logo} herf={"icon_Copy"}/>
+                                </div>
+                            </CopyToClipboard>
+                        </div>
+                    </div>
+                    <div className={styles.line}></div>
+                    <button className={"button-green"} onClick={() => {
+                        setShowDetails(false);
+                    }}>
+                        {i18n.t("deposit.HistoryListPanel.details.btn_confirm")}
+                    </button>
+                </div>
+            </Popup>
+        )
+    }
+    return (
+        <div className={styles.container}>
+            {renderContent()}
+            {renderDetails()}
+        </div>
+    )
+}
+export default WWithdrawRecord;

+ 140 - 0
src/widgets/WWithdraw/WWithdrawRecord/styles.module.scss

@@ -0,0 +1,140 @@
+.container{
+  width: 100%;
+  height: 100%;
+  background-color: var(--widget-bg);
+  padding: 1rem 1rem;
+}
+.title_con{
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 1rem;
+  color: var(--header);
+  font-size: 0.9rem;
+}
+.content{
+  display: flex;
+  flex-direction: column;
+  gap: 0.8rem;
+  & .item_con{
+    display: flex;
+    flex-direction: column;
+    padding: 1rem;
+    background-color: var(--card-bg);
+    border-radius: var(--border-radius);
+    gap: 0.2rem;
+    & .item{
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+      color: var(--text);
+      width: 100%;
+      & .pay_type_con{
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        gap: 0.2rem;
+        & .logo{
+          width: 1.4rem;
+          height: 1.4rem;
+          fill: var(--primary-color);
+        }
+      }
+      & .title{
+        font-size: 1rem;
+        color: var(--header);
+      }
+      & .payment_sn_con{
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        gap: 0.5rem;
+        user-select: text;
+        & .logo{
+          width: 1.2rem;
+          height: 1.2rem;
+          fill: var(--text);
+        }
+      }
+      & .status{
+        &_success{
+          color: var(--primary-color);
+        }
+        &_failed{
+          color: var(--red);
+        }
+        &_pending{
+          color: var(--coin-yellow);
+        }
+      }
+    }
+  }
+}
+.detail{
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+  width: 100%;
+  & .top{
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    font-size: 1rem;
+    color: var(--header);
+    justify-content: center;
+    align-items: center;
+    gap: 1rem;
+    & .status{
+      display: flex;
+      flex-direction: column;
+      font-size: 0.875rem;
+      align-items: center;
+      justify-content: center;
+      gap: 0.5rem;
+      & .logo{
+        width: 3.5rem;
+        height: 3.5rem;
+      }
+      &_success{
+        color: var(--primary-color);
+      }
+      &_failed{
+        color: var(--red);
+      }
+      &_pending{
+        color: var(--coin-yellow);
+      }
+    }
+  }
+  & .line{
+    border-top: 1px dashed var(--card-line);
+  }
+  & .bottom{
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    gap: 0.25rem;
+    font-size: 0.825rem;
+    color: var(--header);
+    & .label{
+      color: var(--text);
+    }
+    & .item{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
+    & .number{
+      display: flex;
+      align-items: center;
+      gap: 0.2rem;
+      user-select: text;
+      & .logo{
+        width: 1.2rem;
+        height: 1.2rem;
+        fill: var(--header);
+      }
+    }
+  }
+}

+ 44 - 0
src/widgets/WWithdraw/WWithdrawRules/WWithdrawRules.jsx

@@ -0,0 +1,44 @@
+import Popup from "@components/Popup";
+import styles from "./styles.module.scss";
+import {formatCash, stringFormat} from "@utils/helpers";
+import {useSelector} from "react-redux";
+import {getWithdrawBaseInfo, getWithdrawTax} from "@features/paySlice";
+const i18n=global.i18n;
+const WWithdrawRules = ({open,onClose}) => {
+    const info=useSelector(getWithdrawBaseInfo);
+    const tax=useSelector(getWithdrawTax);
+    const renderContent=()=>{
+        let OrderLimit="";
+        let MinExchange="";
+        let MaxExchange="";
+        if (info){
+            OrderLimit=info.OrderLimit;
+            MinExchange=info.MinExchange;
+            MaxExchange=info.MaxExchange;
+        }
+        let min=formatCash(MinExchange,0);
+        let max=formatCash(MaxExchange,0);
+        return (
+            <div className={styles.rule_content}>
+                <div>{stringFormat(i18n.t("withdraw.help.rule_1"), [min, max])}</div>
+                <div>{i18n.t("withdraw.help.rule_2")}</div>
+                <div>{stringFormat(i18n.t("withdraw.help.rule_3"), [OrderLimit])}</div>
+                <div>{i18n.t("withdraw.help.rule_4")}</div>
+                <div>{i18n.t("withdraw.help.rule_5")}</div>
+                <div>{i18n.t("withdraw.help.rule_6")}</div>
+                <div className={styles.tips}>{stringFormat(i18n.t("withdraw.help.rule_tips"), [tax])}</div>
+            </div>
+        )
+    }
+    return (
+        <Popup onClose={onClose} open={open}>
+            <div className={styles.content}>
+                <div className={styles.title}>
+                {i18n.t("withdraw.help.title")}
+                </div>
+                {renderContent()}
+            </div>
+        </Popup>
+    )
+}
+export default WWithdrawRules;

+ 22 - 0
src/widgets/WWithdraw/WWithdrawRules/styles.module.scss

@@ -0,0 +1,22 @@
+.content{
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+  color: var(--header);
+  & .title{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 1rem;
+  }
+  & .rule_content{
+    display: flex;
+    flex-direction: column;
+    gap: 1rem;
+    color: var(--header);
+    white-space: pre-line;
+    & .tips{
+      color: var(--coin-yellow);
+    }
+  }
+}

+ 20 - 5
src/widgets/WWithdraw/WithdrawHeader.jsx

@@ -1,12 +1,15 @@
 import styles from "./styles.module.scss";
 import SvgIcon from "@components/SvgIcon";
-import {ReactComponent as Video} from "@assets/singlesvg/video.svg";
-import USD from "@assets/lvicon/USD.black.png";
-import {numFormatter} from "@utils/helpers";
+import {useNavigate} from "react-router-dom";
+import {ROUTE_PATH} from "@constants/routes";
+import {useState} from "react";
+import WWithdrawRules from "@widgets/WWithdraw/WWithdrawRules/WWithdrawRules";
 const i18n=global.i18n;
 const WithdrawHeader=({...props})=>{
     const route=props.route;
     const close=props.close;
+    const navigate=useNavigate();
+    const [openRules,setOpenRules]=useState(false);
     const renderHeader=()=>{
         let title=route?route.title:"";
         return (
@@ -23,10 +26,14 @@ const WithdrawHeader=({...props})=>{
                         </button>
                     </div>
                     <div className={styles.right}>
-                        <button className={styles.right_btn}>
+                        <button className={styles.right_btn} onClick={()=>{
+                            setOpenRules(true);
+                        }}>
                             <SvgIcon className={styles.icon} herf={"icon_Help"}/>
                         </button>
-                        <button className={styles.right_btn}>
+                        <button className={styles.right_btn} onClick={()=>{
+                            navigate(route.path+ROUTE_PATH.WITHDRAW_REACORD);
+                        }}>
                             <SvgIcon className={styles.icon} herf={"icon_History"}/>
                         </button>
                     </div>
@@ -34,9 +41,17 @@ const WithdrawHeader=({...props})=>{
             </div>
         )
     }
+    const renderRules=()=>{
+        return (
+            <WWithdrawRules open={openRules} onClose={()=>{
+                setOpenRules(false);
+            }}></WWithdrawRules>
+        )
+    }
     return (
         <div id={props.id} className={props.className}>
             {renderHeader()}
+            {renderRules()}
         </div>
     )
 }