diff --git a/_app.js b/_app.js deleted file mode 100644 index 770b118..0000000 --- a/_app.js +++ /dev/null @@ -1 +0,0 @@ -import '@fortawesome/fontawesome-svg-core/styles.css'; // 引入Font Awesome CSS diff --git a/api/public.js b/api/public.js new file mode 100644 index 0000000..91ceced --- /dev/null +++ b/api/public.js @@ -0,0 +1,81 @@ +import { get } from "@/utils/storeInfo"; +import require from "@/utils/require"; +import { Toast } from "antd-mobile"; +//关注和取关功能 +export const handleFollow = async (isFollowed, followedID, callback) => { + const account = get("account"); + let body = { + [!isFollowed ? "account_relations" : "sentences"]: [ + { sub_mid: account.mid, obj_mid: followedID, predicate: 0 }, + { sub_mid: followedID, obj_mid: account.mid, predicate: 1 }, + ], + }; + try { + const data = await require("POST", `/api/account_relation/${ + !isFollowed ? "create" : "delete" + }`, { + body, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } else { + callback(!isFollowed); + } + } catch (error) { + console.error(error); + } +}; + +//点赞和取消点赞功能 +export const thumbsUp = async (id, times = 1,callback) => { + console.log("times", times); + try { + const body = { + moment_id: id, + times: times==1?-1:1, + }; + const data = await require("POST", `/api/moment/thumbs_up`, { + body, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + }else{ + callback(times==1?-1:1); + } + } catch (error) { + console.error(error); + } +}; + +export async function checkRelation(subMid, objMid, predicate) { + try { + const data = await require("POST", `/api/account_relation/list_by_sentence`, { + body:{ + sub_mid: subMid, + obj_mid: objMid, + predicate: predicate, + }, + }); + if (data.ret === -1) { + Toast.show({ + type: "error", + text1: data.msg, + topOffset: 60, + }); + return; + } + return data.data.is_account_relation_existed; + } catch (error) { + console.error(error); + } +} diff --git a/api/space.js b/api/space.js new file mode 100644 index 0000000..61976e4 --- /dev/null +++ b/api/space.js @@ -0,0 +1,65 @@ +import require from "@/utils/require"; + +export const getStreamerInfo = async (mid) => { + try { + const data = await require("POST", "/api/zone/list_by_mid", { + body: { + mid, + }, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + return { + ...data.data.list[0], + refund_enable: data.data.refund_enable, + refund_status: data.data.refund_status, + }; + } catch (error) { + console.error(error); + } +}; +//获取空间数据并将该空间标为已读 +export const getSpaceData = async (mid) => { + try { + const data = await require("POST", `/api/zone/list_by_mid`, { + body: { + mid, + }, + }); + if (data.ret === -1) { + Toast.show({ + type: "error", + text1: data.msg, + topOffset: 60, + }); + return; + } + + //将空间标为已读 + const data2 = await require("POST", `/api/zone_session/upsert`, { + body: { + zid: data.data.list[0].id, + }, + }); + if (data2.ret === -1) { + Toast.show({ + type: "error", + text1: data2.msg, + topOffset: 60, + }); + return; + } + return { + isRefunding: data.data.refund_status === 1, + noRole: data.data.list[0].visitor_role === 4, + }; + } catch (error) { + console.error(error); + } +}; diff --git a/app/globals.css b/app/globals.css index c15ee24..164b07c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -151,4 +151,9 @@ body{ .adm-dialog .adm-dialog-content{ max-height: none; height: 100%; +} +.adm-toast-icon{ + display: flex; + justify-content: center; + align-items: center; } \ No newline at end of file diff --git a/app/layout.js b/app/layout.js index 9a30a8b..fd5bb48 100644 --- a/app/layout.js +++ b/app/layout.js @@ -1,12 +1,15 @@ +"use client"; import { Inter } from "next/font/google"; -import React, { useEffect } from 'react'; +import React, { useEffect } from "react"; import "./globals.css"; import BottomNav from "../components/BottomNav"; +import { Provider } from 'react-redux'; +import store from '../store'; const inter = Inter({ subsets: ["latin"] }); export const metadata = { title: "铁粉空间", - + description: "与Ta永不失联", keywords: [ "铁粉空间", @@ -29,18 +32,14 @@ export const viewport = { }; export default function RootLayout({ children }) { return ( - + - {children} +
- + {children} +
diff --git a/app/loading.js b/app/loading.js index 7e8d0a9..66b1979 100644 --- a/app/loading.js +++ b/app/loading.js @@ -4,7 +4,7 @@ import React from "react"; import { SpinLoading } from "antd-mobile"; export default function Loading() { return ( -
+
); diff --git a/app/login/page.js b/app/login/page.js index 0db407c..66dd4d7 100644 --- a/app/login/page.js +++ b/app/login/page.js @@ -1,15 +1,24 @@ "use client"; -import React, { useState, useRef } from "react"; - -import baseRequest from "@/utils/baseRequest"; -import { generateSignature } from "@/utils/crypto"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; -import { Input, Button, Swiper, Tabs, Divider, Checkbox } from "antd-mobile"; +import React, { useState, useRef, useEffect } from "react"; +import { + Input, + Button, + Swiper, + Tabs, + Divider, + Checkbox, + Toast, +} from "antd-mobile"; import { useRouter } from "next/navigation"; import styles from "./index.module.css"; - +import { JSEncrypt } from "jsencrypt"; +import { handleLogin } from "@/store/actions"; +import { saveUserInfo,removeUserInfo } from "@/utils/storeInfo"; +import { connect } from "react-redux"; +import { cryptoPassword } from "@/utils/crypto"; +import require from "@/utils/require"; +import {signOut,signIn} from "@/utils/auth"; /* params格式: { @@ -20,15 +29,171 @@ const tabItems = [ { key: "code", title: "验证码登录" }, { key: "password", title: "帐号密码登录" }, ]; -export default function Login({}) { +function Login({ handleLogin }) { const [activeIndex, setActiveIndex] = useState(0); - const [regionCode, setRegionCode] = useState(""); - const [mobilePhone, setMobilePhone] = useState(""); const [veriCode, setVeriCode] = useState(""); - const [password, setPassword] = useState(""); const [isCounting, setIsCounting] = useState(false); const [seconds, setSeconds] = useState(60); + const [loginInfo, setLoginInfo] = useState({ + mobilePhone: "", + regionCode: "86", + password: "", + checked: false, + }); + const router = useRouter(); const swiperRef = useRef(null); + useEffect(() => { + handleLogin({ isSignin: false, userToken: null }); + signOut() + removeUserInfo(); + },[]) + useEffect(() => { + let interval; + if (isCounting && seconds > 0) { + interval = setInterval(() => { + setSeconds(seconds - 1); + }, 1000); + } else { + setIsCounting(false); + setSeconds(60); + clearInterval(interval); + } + return () => { + clearInterval(interval); + }; + }, [isCounting, seconds]); + const handleSubmit = async (type) => { + const { mobilePhone, password, regionCode, checked } = loginInfo; + //验证数据格式 + if (!checked) { + Toast.show({ + icon: "fail", + content: "请先阅读并同意《用户协议》和《隐私政策》后登录", + position: "top", + }); + return; + } + if (!mobilePhone.match(/^1[3456789]\d{9}$/)) { + Toast.show({ + icon: "fail", + content: "手机号码格式错误", + position: "top", + }); + return; + } + if (type === "password") { + if (password.length < 8) { + Toast.show({ + icon: "fail", + content: "密码不得小于8位", + position: "top", + }); + return; + } + if (password.length > 15) { + Toast.show({ + icon: "fail", + content: "密码不得大于15位", + position: "top", + }); + return; + } + } else { + if (veriCode.length !== 6) { + Toast.show({ + icon: "fail", + content: "请输入正确的验证码", + position: "top", + }); + return; + } + } + //对手机号进行RSA加密 + const encrypt = new JSEncrypt(); + encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY); + const mobile_phone = encrypt.encrypt(mobilePhone); + //MD5加密password + const encryptedPassword = cryptoPassword(password); + //发送登录请求 + let body = { + mobile_phone, + region_code: regionCode, + }; + body = + type === "password" + ? { + ...body, + password: encryptedPassword, + } + : { + ...body, + code: veriCode, + }; + try { + const data = await require("POST", `/api/login/${ + type === "password" ? "login_by_pswd" : "login_by_veri_code" + }`, { + body, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + //登录 + saveUserInfo(data, mobilePhone, regionCode); + signIn(data); + handleLogin({ isSignin: true, userToken: data.data.token }); + router.push("/"); + } catch (error) { + console.error(error); + } + }; + //点击获取验证码 + const handleVerification = async () => { + const { mobilePhone, regionCode, checked } = loginInfo; + if (!checked) { + Toast.show({ + icon: "fail", + content: "请先阅读并同意《用户协议》和《隐私政策》后登录", + position: "top", + }); + return; + } + //手机号校验 + if (!mobilePhone.match(/^1[3456789]\d{9}$/)) { + Toast.show({ + type: "error", + text1: "手机号码格式错误", + topOffset: 60, + }); + return; + } + //开始倒计时 + setIsCounting(true); + //对手机号进行RSA加密 + const encrypt = new JSEncrypt(); + encrypt.setPublicKey(process.env.NEXT_PUBLIC_RSA_KEY); + const mobile_phone = encrypt.encrypt(mobilePhone); + //发送短信验证码 + try { + await fetch(`/api/veri_code/send`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + mobile_phone, + region_code: regionCode, + }), + }); + } catch (error) { + console.error(error); + } + }; return (
@@ -65,14 +230,33 @@ export default function Login({}) {
-

+{regionCode}

+

+ +{loginInfo.regionCode} +

setMobilePhone(value)} - value={mobilePhone} + onChange={(value) => + setLoginInfo({ ...loginInfo, mobilePhone: value }) + } + value={loginInfo.mobilePhone} + style={{ "--color": "#FFFFFF", "--font-size": "16px" }} + /> +
+ +
+

+ 密码 +

+ setLoginInfo({ ...loginInfo, password: value})} + value={loginInfo.password} + type="password" style={{ "--color": "#FFFFFF", "--font-size": "16px" }} />
@@ -83,7 +267,7 @@ export default function Login({}) {

setVeriCode(value)} + onChange={(value) => setVeriCode(value)} value={veriCode} type="number" style={{ @@ -95,7 +279,7 @@ export default function Login({}) { shape="rounded" size="mini" disabled={isCounting} - // onClick={handleSubmit} + onClick={handleVerification} style={{ "--background-color": "#FF669E", color: "#FFFFFF" }} className="whitespace-nowrap" > @@ -104,19 +288,29 @@ export default function Login({}) {
- +
-

+{regionCode}

+

+ +{loginInfo.regionCode} +

setMobilePhone(value)} - value={mobilePhone} + onChange={(value) => + setLoginInfo({ ...loginInfo, mobilePhone: value }) + } + value={loginInfo.mobilePhone} style={{ "--color": "#FFFFFF", "--font-size": "16px" }} />
@@ -126,10 +320,13 @@ export default function Login({}) { 密码

setVeriCode(value)} - value={password} - type="number" + onChange={(value) => + setLoginInfo({ ...loginInfo, password: value }) + } + value={loginInfo.password} + type="password" style={{ "--placeholder-color": "#FFFFFF80", "--font-size": "16px", @@ -137,20 +334,29 @@ export default function Login({}) { />
- + ); } -const LoginBtn = () => { - +const LoginBtn = ({ loginInfo, setLoginInfo, type, handleSubmit }) => { const router = useRouter(); + useEffect(() => { + console.log("loginInfo", loginInfo); + }, []); return (
setLoginInfo({ ...loginInfo, checked: value })} style={{ "--icon-size": "14px", "--font-size": "14px", @@ -186,7 +392,7 @@ const LoginBtn = () => { shape="rounded" size="middle" block - // onClick={handleSubmit} + onClick={() => handleSubmit(type)} style={{ "--background-color": "#FF669E", color: "#FFFFFF" }} className="mt-2" > @@ -195,3 +401,8 @@ const LoginBtn = () => {
); }; + +const mapDispatchToProps = { + handleLogin, +}; +export default connect(null, mapDispatchToProps)(Login); diff --git a/app/messageDetail/page.js b/app/messageDetail/page.js index 3372f4f..baa19a6 100644 --- a/app/messageDetail/page.js +++ b/app/messageDetail/page.js @@ -12,6 +12,7 @@ import { PullToRefresh, List, InfiniteScroll, + Toast } from "antd-mobile"; import { useRouter } from "next/navigation"; const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk"; @@ -29,9 +30,9 @@ export default function MessageDetail({}) { const getSession = async () => { const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { - const base = baseRequest(); + const base = baseRequest(); const account = await get("account"); - const signature = generateSignature({ + const signature = generateSignature({ mid: account.mid, ...base, }); @@ -51,9 +52,9 @@ export default function MessageDetail({}) { const detailData = await detailResponse.json(); if (detailData.ret === -1) { Toast.show({ - type: "error", - text1: detailData.msg, - topOffset: 60, + icon: "fail", + content: data.msg, + position: "top", }); return; } @@ -96,9 +97,7 @@ export default function MessageDetail({}) {
- - - + diff --git a/app/my/page.js b/app/my/page.js index 8c391af..5f71e42 100644 --- a/app/my/page.js +++ b/app/my/page.js @@ -1,16 +1,24 @@ "use client"; -import React from "react"; +import React,{useEffect,useState} from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleRight } from "@fortawesome/free-solid-svg-icons"; import { Avatar, Image } from "antd-mobile"; import { useRouter,useSearchParams } from "next/navigation"; import withAuth from "@/components/WithAuth"; +import {get} from "@/utils/storeInfo"; const My = () => { + const [userInfo, setUserInfo] = useState({}); const searchParams = useSearchParams(); const router = useRouter(); + useEffect(() => { + const userInfo = get("account"); + if (userInfo) { + setUserInfo(userInfo); + } + },[]) return ( -
+
router.push("my/editUserProfile/selectUserProfileItem")}> { />
-
router.push("profile")}> +
router.push("profile/"+userInfo.mid)}>
-

测试账号

+

{userInfo.name}

- 213422 + {userInfo.user_id}
@@ -71,11 +80,11 @@ const My = () => {

粉丝

  • router.push("my/wallet")}> -

    540

    +

    {userInfo.gold_num}

    金币

  • router.push("my/wallet")}> -

    0

    +

    {userInfo.diamond_num}

    钻石

  • diff --git a/app/my/setting/switchAccount/page.js b/app/my/setting/switchAccount/page.js index af608d5..8f3a2ec 100644 --- a/app/my/setting/switchAccount/page.js +++ b/app/my/setting/switchAccount/page.js @@ -71,6 +71,7 @@ export default function SwitchAccount() { 213422 diff --git a/app/page.js b/app/page.js index 16b54e2..1912cd9 100644 --- a/app/page.js +++ b/app/page.js @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState, Suspense } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Tabs, Swiper, @@ -10,15 +10,15 @@ import { List, Image, } from "antd-mobile"; - +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faRefresh } from "@fortawesome/free-solid-svg-icons"; import PostItem from "../components/PostItem"; import { sleep } from "antd-mobile/es/utils/sleep"; import "./index.css"; import PostItemSkeleton from "@/components/skeletons/PostItemSkeleton"; import Link from "next/link"; -import requre from "@/utils/require"; -import baseRequest from "@/utils/baseRequest"; -import { generateSignature } from "@/utils/crypto"; +import require from "@/utils/require"; +import Empty from "@/components/Empty"; const variables = { "@active-line-color": "#f00", // 将主题色改为红色 }; @@ -34,46 +34,14 @@ export default function Home() { const swiperRef = useRef(null); const [activeIndex, setActiveIndex] = useState(0); const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); - const [hasMore, setHasMore] = useState(true); + const [scrollHeight, setScrollHeight] = useState(0); - const [commenPostList,setCommenPostList] = useState([]) // 获取屏幕高度 // const scrollHeight = 600; useEffect(() => { - setScrollHeight(window.innerHeight - 126); - getPostList(2); + setScrollHeight(window.innerHeight - 112); // getData(0) }, []); - async function doRefresh() { - // await sleep(1000); - // Toast.show({ - // icon: "fail", - // content: "刷新失败", - // }); - // throw new Error("刷新失败"); - getPostList(1); - } - async function loadMore() { - // const append = await getPostList(0); - // setData((val) => [...val, ...append]); - // setHasMore(append.length > 0); - } - const getPostList = async (type = 0) => { - const data = await requre("POST", "/api/moment/recomm_list", { - body: { op_type: type }, - }); - setLoading(false) - if (data.ret == -1) { - Toast.show({ - icon: "fail", - content: "加载失败", - }); - }else{ - setCommenPostList(data.data.recomm_list) - } - console.log("res", data); - }; return (
    @@ -87,6 +55,7 @@ export default function Home() { > {tabItems.map((item) => (
    null} ref={swiperRef} defaultIndex={activeIndex} @@ -113,33 +82,199 @@ export default function Home() { }} > - - - {loading &&
    } - {commenPostList.map(item=> - - )} - -
    -
    +
    - - - - - - - - +
    ); } +const RecommPostList = ({ scrollHeight }) => { + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(true); + const [commenPostList, setCommenPostList] = useState([]); + const [currentTime, setCurrentTime] = useState(); + useEffect(() => { + getRecommPostList(2).then((res) => { + setCommenPostList(res); + }); + }, []); + async function doRefresh() { + // await sleep(1000); + // Toast.show({ + // icon: "fail", + // content: "刷新失败", + // }); + // throw new Error("刷新失败"); + const list = await getRecommPostList(1); + setCommenPostList(list); + } + async function loadMore() { + const list = await getRecommPostList(0); + if (list.length == 0) { + setHasMore(false); + } + setCommenPostList([...commenPostList, ...list]); + } + const getRecommPostList = async (type = 2) => { + setLoading(true); + const data = await require("POST", "/api/moment/recomm_list", { + body: { op_type: type }, + }); + setLoading(false); + if (data.ret == -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + } else { + return data.data.recomm_list; + } + }; + return ( +
    + + {loading && ( +
    + + + + + +
    + )} + {commenPostList.map((item) => ( + + + + ))} + {commenPostList.length == 0 && ( +
    + +
    + )} +
    + {!!commenPostList.length && ( + + )} +
    + +
    +
    + ); +}; +const FollowPostList = ({ scrollHeight }) => { + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(false); + const [followPostList, setFollowPostList] = useState([]); + const [currentTime, setCurrentTime] = useState(); + const [offset, setOffset] = useState(0); + useEffect(() => { + getFollowPostList().then((res) => { + setFollowPostList(res); + }); + }, []); + async function doRefresh() { + // await sleep(1000); + // Toast.show({ + // icon: "fail", + // content: "刷新失败", + // }); + // throw new Error("刷新失败"); + // getRecommPostList(1); + } + async function loadMore() { + const list = await getFollowPostList(); + const newList = [...followPostList, ...list]; + setOffset(newList.length / 4); + setFollowPostList(newList); + } + const getFollowPostList = async () => { + setLoading(true); + setCurrentTime(Math.floor(new Date().getTime() / 1000)); + const data = await require("POST", "/api/account_relation/list_follow", { + body: { offset, limit: 4 }, + }, true); + setHasMore(data.data.list.length > 0); + if (data.data.list.length > 0) { + //查关注主播展示资料 + const followsResponse = + await require("POST", "/api/moment/list_by_mids", { + body: { + offset, + limit: 4, + ct_upper_bound: currentTime, + mids: data.data.list.map((item) => item.obj_mid), + }, + }); + setLoading(false); + if (data.ret == -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + } else { + return followsResponse.data.list; + } + } else { + setLoading(false); + } + }; + return ( +
    + {/* */} + + {loading && ( +
    + + + + + +
    + )} + {followPostList.map((item, index) => ( + + + + ))} + {followPostList.length == 0 && ( +
    + +
    + )} +
    + {!!followPostList.length && ( + + )} + {/*
    + +
    */} + {/*
    */} +
    + ); +}; diff --git a/app/profile/[mid]/page.js b/app/profile/[mid]/page.js new file mode 100644 index 0000000..187c5f4 --- /dev/null +++ b/app/profile/[mid]/page.js @@ -0,0 +1,360 @@ +"use client"; + +import React, { useEffect, useRef, useState } from "react"; +import { + Image, + Swiper, + Divider, + ImageViewer, + Popover, + Toast, +} from "antd-mobile"; +import { useRouter, useParams } from "next/navigation"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faAngleLeft, + faAngleRight, + faEllipsisVertical, + faCopy, + faWarning, +} from "@fortawesome/free-solid-svg-icons"; +import require from "@/utils/require"; +import AddWeChat from "@/components/AddWeChat"; +import { handleFollow, checkRelation } from "@/api/public"; +import { get } from "@/utils/storeInfo"; +// import * as Clipboard from "expo-clipboard"; +export default function PersonSpace() { + const { mid } = useParams(); + const router = useRouter(); + const [streamerInfo, setStreamerInfo] = useState(null); + const [spaceData, setSpaceData] = useState(null); + const [loading, setLoading] = useState(false); + const [visible, setVisible] = useState(false); + const [isFollow, setIsFollow] = useState(false); + // 获取屏幕高度 + // const scrollHeight = 600; + const photos = [ + { url: "https://picsum.photos/seed/picsum/200/300", type: "video" }, + { url: "https://picsum.photos/seed/picsum/200/300", type: "img" }, + ]; + useEffect(() => { + getStreamerInfo(); + getSpaceData(); + getRelationData(); + }, []); + const showPhotos = (photos, index) => { + ImageViewer.Multi.show({ + images: photos.map((item) => item.url), + defaultIndex: index, + }); + }; + const getStreamerInfo = async () => { + try { + setLoading(true); + const data = await require("POST", "/api/streamer/list_ext_by_mid", { + body: { + mid: Number(mid), + }, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + setStreamerInfo({ + ...data.data, + }); + setLoading(false); + } catch (error) { + console.error(error); + } + }; + const getSpaceData = async () => { + try { + const data = await require("POST", "/api/zone/list_by_mid", { + body: { + mid: Number(mid), + }, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + setSpaceData(data.data.list[0]); + } catch (error) { + console.error(error); + } + }; + const getRelationData = async () => { + const account = get("account"); + const subMid = account.mid; + const objMid = Number(mid); + const temIsFollowed = await checkRelation(subMid, objMid, 0); + setIsFollow(temIsFollowed); + }; + return ( +
    +
    +
    + { + router.back(); + }} + /> +
    + { + router.push("messageDetail"); + }} + > + + 举报 +
    + } + trigger="click" + placement="left" + > + { + // router.back(); + }} + /> + +
    + {/* 内容 */} +
    +
    + + {photos.map((photo, index) => ( + +
    { + showPhotos(photos, index); + }} + > + { + // Toast.show(`你点击了卡片 ${index + 1}`); + // }} + /> + {photo.type == "video" && ( +
    + +
    + )} +
    +
    + ))} +
    +
    +
    +
    +
    +
    +

    + {streamerInfo?.streamer_ext?.name} +

    +
    + +
    +
    +
      + {streamerInfo?.streamer_ext?.tag.map((item, index) => ( +
    • + {item} +
    • + ))} +
    +
    +
    +
      +
    • + + {streamerInfo?.streamer_ext?.user_id} +
    • +
    +

    + 个性签名| + {streamerInfo?.streamer_ext?.bio} +

    +
    +
    + +
    +
    router.push("/space/"+mid)}> +
    + 空间动态 +
    + + 查看{spaceData?.zone_moment_count}条 + + { + router.back(); + }} + /> +
    +
    +
    + {spaceData?.previews?.images.map((item, index) => ( +
    + +
    + ))} +
    +
    +
    + + {streamerInfo?.streamer_ext?.platforms && ( + <> + +
    +

    来这找我玩

    + +
      +
    • +
      + +
      + 微信: + 点击查看 +
      +
      +
    • + {streamerInfo?.streamer_ext?.platforms?.map((item) => ( +
    • +
      + +
      + {item?.link_name}: + {item?.nickname} +
      +
      +
      +
      { + // Clipboard.setStringAsync(item.url); + // }} + > + + 复制 +
      +
      + + 前往 +
      +
      +
    • + ))} +
    +
    + + )} +
    +
    +
    handleFollow(isFollow, Number(mid), setIsFollow)} + > + {isFollow ? "已关注" : "关注"} +
    +
    setVisible(true)} + > + 添加微信 +
    +
    +
    + +
    + ); +} diff --git a/app/profile/page.js b/app/profile/page.js deleted file mode 100644 index 9b89841..0000000 --- a/app/profile/page.js +++ /dev/null @@ -1,263 +0,0 @@ -"use client"; - -import React, { useEffect, useRef, useState } from "react"; -import { Image, Swiper, Divider, ImageViewer, Popover } from "antd-mobile"; -import { useRouter } from "next/navigation"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faAngleLeft, - faAngleRight, - faEllipsisVertical, - faCopy, - faWarning, -} from "@fortawesome/free-solid-svg-icons"; -export default function PersonSpace() { - const router = useRouter(); - // 获取屏幕高度 - // const scrollHeight = 600; - const [searchValue, setSearchValue] = useState(""); - const photos = [ - { url: "https://picsum.photos/seed/picsum/200/300", type: "video" }, - { url: "https://picsum.photos/seed/picsum/200/300", type: "img" }, - ]; - useEffect(() => {}, []); - const showPhotos = (photos, index) => { - ImageViewer.Multi.show({ - images: photos.map((item) => item.url), - defaultIndex: index, - }); - }; - return ( -
    -
    -
    - { - router.back(); - }} - /> -
    - { - router.push("messageDetail"); - }} - > - - 举报 -
    - } - trigger="click" - placement="left" - > - { - // router.back(); - }} - /> - -
    - {/* 内容 */} -
    -
    - - {photos.map((photo, index) => ( - -
    { - showPhotos(photos, index); - }} - > - { - // Toast.show(`你点击了卡片 ${index + 1}`); - // }} - /> - {photo.type == "video" && ( -
    - -
    - )} -
    -
    - ))} -
    -
    -
    -
    -
    -
    -

    PUPIHAN

    -
    - -
    -
    -
      -
    • - 颜值高 -
    • -
    • - 身材好 -
    • -
    • - 女王范 -
    • -
    -
    -
    -
      -
    • - - 213422 -
    • -
    -

    - 个性签名| - 专属圈内容都在空间里,永久更新外面看不到哟 -

    -
    -
    - -
    -
    router.push("/space/person_space")}> -
    - 空间动态 -
    - 查看60条 - { - router.back(); - }} - /> -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -

    来这找我玩

    -
      -
    • -
      - -
      - 微信: - 点击查看 -
      -
      -
    • -
    • -
      - -
      - 抖音: - PUPIHAN -
      -
      -
      -
      - { - router.back(); - }} - /> - 复制 -
      -
      - { - router.back(); - }} - /> - 前往 -
      -
      -
    • -
    -
    -
    -
    -
    - 关注 -
    -
    - 添加微信 -
    -
    -
    -
    - ); -} diff --git a/app/search/page.js b/app/search/page.js index 4c74459..78112f9 100644 --- a/app/search/page.js +++ b/app/search/page.js @@ -1,31 +1,102 @@ "use client"; import React, { useEffect, useRef, useState } from "react"; -import { Input, List } from "antd-mobile"; +import { Input, List, DotLoading, Toast } from "antd-mobile"; import { useRouter } from "next/navigation"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; +import require from "@/utils/require"; +import { debounce } from "@/utils/tools"; +import Empty from "@/components/Empty"; +const newDebounce = debounce(function (fn) { + fn && fn(); +}, 500); export default function Search() { const router = useRouter(); // 获取屏幕高度 // const scrollHeight = 600; const [searchValue, setSearchValue] = useState(""); + const [loading, setLoading] = useState(false); + const [streamers, setStreamers] = useState([]); + const [zones, setZones] = useState([]); useEffect(() => {}, []); + + const isNumeric = (str) => { + return /^\d+$/.test(str); + }; + const getResult = async (value) => { + console.log("searchValue", value); + const isSearchInt = isNumeric(value); + let api = ""; + let querryParams = ""; + if (isSearchInt) { + api = "/api/streamer/list_ext_fuzzily_by_user_id"; + querryParams = { user_id: parseInt(value, 10) }; + } else { + api = "/api/streamer/list_ext_fuzzily_by_name"; + querryParams = { name: value }; + } + try { + setLoading(true); + const data = await require("POST", api, { + body: { + ...querryParams, + offset: 0, + limit: 20, + }, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + // if (!ignore) { + const zonesData = data.data.list.filter((item) => item.zones.length > 0); + setStreamers(data.data.list); + setZones(zonesData); + // } + setLoading(false); + } catch (error) { + console.error(error); + } + }; return (
    - { + { router.back(); - }}/> + }} + />
    -
    +
    { - setSearchValue(val); + setSearchValue((old) => { + let test = (e) => { + if (val == "") { + setStreamers([]); + setZones([]); + return; + } + getResult(val); + }; + newDebounce(test); + return val; + }); }} /> +
    + {loading && } +
    {searchValue && (

    - -
    - -
    -
    - 用户名 - - ID 845457 - -
    -

    御姐风细跟高跟鞋太绝了

    -
    + {!streamers.length && ( +
    +
    - - -
    - -
    -
    - 用户名 - - ID 845457 - + )} + {streamers.map((item) => ( + router.push(`/space/${item.mid}`)} + key={item.id} + > +
    + +
    +
    + + {item?.name} + + + ID {item.user_id} + +
    +

    {item?.bio}

    -

    御姐风细跟高跟鞋太绝了

    -
    - + + ))}
    ); diff --git a/app/space/[id]/page.js b/app/space/[id]/page.js new file mode 100644 index 0000000..301cd9a --- /dev/null +++ b/app/space/[id]/page.js @@ -0,0 +1,474 @@ +"use client"; + +import React, { useEffect, useState, useRef,useMemo } from "react"; +import { + Image, + Mask, + FloatingPanel, + JumboTabs, + List, + InfiniteScroll, + ProgressBar, + Toast, +} from "antd-mobile"; +import { useRouter, useParams } from "next/navigation"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faAngleLeft, faRefresh } from "@fortawesome/free-solid-svg-icons"; +import PostItem from "@/components/PostItem"; +import PostItemSkeleton from "@/components/skeletons/PostItemSkeleton"; +import Empty from "@/components/Empty"; +import require from "@/utils/require"; +import AddWeChat from "@/components/AddWeChat"; +import SeeTiefen from "@/components/SeeTiefen"; +import DefaultMask from "@/components/DefaultMask"; +import {getSpaceData,getStreamerInfo} from '@/api/space' +const anchors = [ + window.innerHeight - 280, + window.innerHeight - 280, + window.innerHeight - 60, +]; +const tabItems = [ + { label: "全部", key: "all" }, + { label: "铁粉专享", key: "ironFan" }, + { label: "超粉专享", key: "chaofen" }, +]; +export default function PersonSpace() { + const router = useRouter(); + const { id } = useParams(); + const contentBox = useRef(); + const [hasMore, setHasMore] = useState(true); + const [scrollHeight, setScrollHeight] = useState(0); + const [postList, setPostList] = useState([]); + const [offset, setOffset] = useState(0); + const [maskVisible, setMaskVisible] = useState({ visible: false, type: "" }); + const [currentKey, setCurrentKey] = useState("all"); + const [loading, setLoading] = useState(false); + const [streamerInfo, setStreamerInfo] = useState(null); + const [currentTime, setCurrentTime] = useState(); + //退款中Modal是否展示 + const [isRefundingModalVisible, setIsRefundingModalVisible] = useState(false); + const ironFanProgress = useMemo( + () => Math.floor((streamerInfo?.expenditure / streamerInfo?.ironfanship_price) * 100), + [streamerInfo] + ); + useEffect(() => { + setScrollHeight(window.innerHeight - 126); + if (contentBox.current) { + contentBox.current.style.transform = "translateY(-12px)"; + // debugger + } + getStreamerInfo(Number(id)); + getCurrentTime(); + getSpaceData(Number(id)).then(res=>{ + const {isRefunding,noRole}=res; + isRefunding && router.push("/"); + noRole && router.push("/person_space_introduce/"+id); + }) + }, []); + useEffect(() => { + setOffset(0); + if (streamerInfo) { + getPostList(streamerInfo.id, currentKey, true).then((res) => { + setPostList(res); + }); + } + }, [currentKey, streamerInfo]); + const getCurrentTime = async () => { + setCurrentTime(Math.floor(new Date().getTime() / 1000)); + }; + async function loadMore() { + const append = await getPostList(streamerInfo.id, currentKey, false); + if (append) { + setPostList((val) => [...val, ...append]); + setOffset((val) => val + 1); + setHasMore(append.length > 0); + } + } + const getPostList = async (zid, activeKey, first) => { + try { + setLoading(true); + let body = { + zid: zid, + ct_upper_bound: Math.floor(new Date().getTime() / 1000), + offset: first ? 0 : offset, + limit: 4, + }; + switch (activeKey) { + case "all": + body = body; + break; + case "ironFan": + body = { + ...body, + c_type: 1, + is_ironfan_visible: 1, + }; + break; + case "chaofen": + body = { + ...body, + c_type: 1, + }; + break; + + default: + break; + } + const data = await require("POST", "/api/zone_moment/list_by_zid", { + body, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + + setLoading(false); + return data.data.list; + } catch (error) { + console.error(error); + } + }; + return ( +
    +
    +
    + { + router.back(); + }} + /> +
    + router.push("setting")} + /> +
    + {/* 内容 */} +
    +
    +
    +
    +
    + +
    +

    + {streamerInfo?.streamer_ext?.name} +

    +
    +
    + + {streamerInfo?.streamer_ext?.user_id} +
    +
    + + {streamerInfo?.zone_moment_count} +
    +
    +
    +
    +
    setMaskVisible(true)} + > +

    + 分享 +

    +
    +
    +
      +
    • + setMaskVisible({ visible: true, type: "weChat" }) + } + > +
      + +
      +

      查看微信

      +
    • +
    • + setMaskVisible({ visible: true, type: "ironFan" }) + } + > +
      + +
      +

      + {streamerInfo?.is_ironfanship_unlocked === 1 + ? "已是铁粉" + : "成为铁粉"} +

      +

      {`${parseInt( + streamerInfo?.expenditure / 100, + 10 + )}/${parseInt(streamerInfo?.ironfanship_price / 100, 10)}`}

      +
    • +
    • { + streamerInfo?.is_superfanship_unlocked === 1 ? setCurrentKey("chaofen") + : router.push("/pay"); + }} + > +
      + +
      +

      + {streamerInfo?.is_superfanship_unlocked === 1 + ? "尊贵超粉" + : "成为超粉"} +

      +
    • +
    • setMaskVisible(true)} + > +
      + +
      +

      举报

      +
    • + {streamerInfo?.visitor_role === 3 && ( +
    • + router.push("VisibleToOneselfSpacePosts") + } + className="flex flex-col items-center" + > + +

      审核未通过

      +
    • + )} +
    +
    +
    + + setCurrentKey(key)} + activeKey={currentKey} + className="bg-deepBg" + > + {tabItems.map((it) => ( +
    + ) + } + destroyOnClose={true} + > + + {loading && ( + <> + + + + + + )} + {!postList.length && ( +
    + +
    + )} + {postList.map((item, index) => ( + + + + ))} + {/* */} + {!!postList.length && streamerInfo && ( + + )} +
    + + ))} + + +
    + { + router.refresh(); + }} + /> +
    +
      +
    • setMaskVisible({ visible: true, type: "weChat" })} + > +
      + +
      +

      查看微信

      +
    • +
    • setMaskVisible({ visible: true, type: "ironFan" })} + > +
      + +
      +

      已是铁粉

      + {/*

      0/299

      */} +
    • +
    • { + setCurrentKey("chaofen"); + }} + > +
      + +
      +

      尊贵超粉

      +
    • +
    + {maskVisible.type=="weChat" && setMaskVisible({ visible: false, type: "" })} + price={streamerInfo?.streamer_ext?.wechat_coin_price} + name={streamerInfo?.streamer_ext?.name} + streamerMid={streamerInfo?.streamer_ext?.mid} + avatar={streamerInfo?.streamer_ext?.avatar?.images[0]?.urls[0]} + />} + {maskVisible.type=="ironFan" && { + setMaskVisible({ visible: false, type: "" }); + }} + handleClick={()=>{ + setCurrentKey("ironFan"); + setMaskVisible({ visible: false, type: "" }); + }} + />} + { + setIsRefundingModalVisible(false); + // setTimeout(() => navigation.replace("HomeTab"), 500); + }}/> + {/*
    +
    +
    { + router.push("/pay"); + }} + > + + 39.9元立即加入 + +
    +
    +
    */} +
    +
    + ); +} diff --git a/app/space/page.js b/app/space/page.js index c6e84b1..980b52d 100644 --- a/app/space/page.js +++ b/app/space/page.js @@ -79,7 +79,7 @@ export default function Space() { setHasMore(append.length > 0); } return ( -
    +
    -
    +
    ); } diff --git a/app/space/person_space/page.js b/app/space/person_space/page.js deleted file mode 100644 index 816e107..0000000 --- a/app/space/person_space/page.js +++ /dev/null @@ -1,537 +0,0 @@ -"use client"; - -import React, { useEffect, useState, useRef } from "react"; -import { - Image, - Mask, - FloatingPanel, - JumboTabs, - List, - InfiniteScroll, - ProgressBar, -} from "antd-mobile"; -import { useRouter } from "next/navigation"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faAngleLeft, - faRefresh, -} from "@fortawesome/free-solid-svg-icons"; -import PostItem from "@/components/PostItem"; -import PostItemSkeleton from "@/components/skeletons/PostItemSkeleton"; -import Empty from "@/components/Empty"; -const anchors = [ - window.innerHeight - 280, - window.innerHeight - 280, - window.innerHeight - 60, -]; -export default function PersonSpace() { - const router = useRouter(); - const contentBox = useRef(); - const [hasMore, setHasMore] = useState(true); - const [scrollHeight, setScrollHeight] = useState(0); - // 获取屏幕高度 - // const scrollHeight = 600; - const photos = [ - "https://picsum.photos/seed/picsum/200/300", - "https://picsum.photos/seed/picsum/200/300", - ]; - const [maskVisible, setMaskVisible] = useState({ visible: false, type: "" }); - const [currentKey, setCurrentKey] = useState("all"); - useEffect(() => { - setScrollHeight(window.innerHeight - 126); - if (contentBox.current) { - contentBox.current.style.transform = "translateY(-12px)"; - // debugger - } - }, []); - async function loadMore() { - const append = await mockRequest(); - setData((val) => [...val, ...append]); - setHasMore(append.length > 0); - } - return ( -
    -
    -
    - { - router.back(); - }} - /> -
    - router.push("setting")}/> -
    - {/* 内容 */} -
    -
    -
    -
    -
    - -
    -

    草莓不可爱

    -
    - - 213422 -
    -
    -
    -
    setMaskVisible(true)} - > -

    - 分享 -

    -
    -
    -
      -
    • - setMaskVisible({ visible: true, type: "weChat" }) - } - > -
      - -
      -

      查看微信

      -
    • -
    • - setMaskVisible({ visible: true, type: "ironFan" }) - } - > -
      - -
      -

      已是铁粉

      -

      0/299

      -
    • -
    • setCurrentKey("chaofen")} - > -
      - -
      -

      尊贵超粉

      -
    • -
    • setMaskVisible(true)} - > -
      - -
      -

      举报

      -
    • -
    -
    -
    - - setCurrentKey(key)} - activeKey={currentKey} - className="bg-deepBg" - > -
    - ) - } - destroyOnClose={true} - > - - - - - - - - - - - - - - -
    - ) - } - destroyOnClose={true} - > -
    - -
    - -
    - ) - } - destroyOnClose={true} - > - - - - - - - - - - - - - - - - -
    - { - router.refresh(); - }} - /> -
    -
      -
    • setMaskVisible({ visible: true, type: "weChat" })} - > -
      - -
      -

      查看微信

      -
    • -
    • setMaskVisible({ visible: true, type: "ironFan" })} - > -
      - -
      -

      已是铁粉

      - {/*

      0/299

      */} -
    • -
    • setCurrentKey("chaofen")} - > -
      - -
      -

      尊贵超粉

      -
    • -
    - -
    -
    setMaskVisible({ visible: false, type: "" })} - >
    -
    -
    - {maskVisible.type === "weChat" && } - {maskVisible.type === "ironFan" && ( - { - setCurrentKey("ironFan"); - setMaskVisible({ visible: false, type: "" }); - }} - /> - )} -
    -
    -
    -
    - {/*
    -
    -
    { - router.push("/pay"); - }} - > - - 39.9元立即加入 - -
    -
    -
    */} -
    -
    - ); -} - -const SeeTiefen = ({ closeMask, handleClick }) => { - return ( -
    -

    - 当前铁粉解锁进度: - 50% -

    - -

    - 空间内累计消费达到¥1280即可成为 - 铁粉 - ,可查看所有铁粉专享内容哟,快来成为我的铁粉吧~ -

    -
    - 查看铁粉专享内容 -
    -
    - ); -}; -const SeeWechat = ({ closeMask }) => { - return ( -
    - -

    草莓不可爱

    -
    - 解锁后展示 -
    -

    - 添加时请备注自己“铁粉空间”昵称 -
    - 若解锁后72小时为通过好友,请联系客服 -

    -
    - 解锁微信(990金币) -
    -
    - ); -}; diff --git a/app/space/person_space_introduce/[mid]/page.js b/app/space/person_space_introduce/[mid]/page.js new file mode 100644 index 0000000..c6a92f7 --- /dev/null +++ b/app/space/person_space_introduce/[mid]/page.js @@ -0,0 +1,280 @@ +"use client"; + +import React, { useEffect, useState, useRef } from "react"; +import { Image, ImageViewer,Dialog } from "antd-mobile"; +import { useRouter, useParams } from "next/navigation"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faAngleLeft, faAngleRight,faClose,faSave } from "@fortawesome/free-solid-svg-icons"; +import AddWeChat from "@/components/AddWeChat"; +import { getStreamerInfo } from "@/api/space"; +export default function PersonSpaceIntroduce() { + const router = useRouter(); + const contentBox = useRef(); + // 获取屏幕高度 + // const scrollHeight = 600; + const [visible, setVisible] = useState(false); + const [data, setData] = useState({}); + const [isLoading, setIsloading] = useState(true); + const { mid } = useParams(); + useEffect(() => { + if (contentBox.current) { + contentBox.current.style.transform = "translateY(-12px)"; + // debugger + } + + getStreamerInfo(Number(mid)).then((res) => { + setData(res); + console.log("mid", mid); + }); + }, []); + const showPhotos = (photos, index) => { + ImageViewer.Multi.show({ + images: photos.map((item) => item.urls[0]), + defaultIndex: index, + }); + }; + const handleShowVideo = (video) => { + Dialog.className = "videoMask"; + Dialog.show({ + title: "", + content: ( +
    +
    +
    Dialog.clear()} + > + +
    +
    +
    + +
    +
    + +
    +
    + ), + bodyStyle: { + background: "none", + maxHeight: "none", + height: "100%", + }, + }); + }; + return ( +
    +
    +
    + { + router.back(); + }} + /> +
    +
    + {/* 内容 */} +
    +
    +
    +
    +
    + +
    +

    + {data?.streamer_ext?.name} +

    +
    + + {data?.streamer_ext?.user_id} +
    +
    +
    +
    setVisible(true)} + > +
    + +
    +

    查看微信

    +
    +
    +
      +
    • +

      {data?.zone_moment_count}

      +

      动态

      +
    • +
    • +

      {data?.image_count}

      +

      照片

      +
    • +
    • +

      {data?.video_count}

      +

      视频

      +
    • +
    +
    +
    +
    +

    + 空间介绍 +

    +

    {data?.profile}

    +
      +
    • + handleShowVideo({ + url: data?.streamer_ext?.shorts?.videos[0]?.cover_urls[0], + mp4: data?.streamer_ext?.shorts?.videos[0]?.urls[0], + }) + } + > +
      + +
      + +
    • + {data?.streamer_ext?.album?.images.map((item, index) => ( +
    • + showPhotos(data?.streamer_ext?.album?.images, index) + } + > + +
    • + ))} +
    + {data?.admission_price !== 0 && ( +
    +

    + 付费须知 +

    +
    +

    + 1、加入后,您可以查看空间内相关内容; +

    +

    + 2、本空间由空间主人自行创建,加入空间前请确认相关风险,本平台不提供相关保证,请避免上当受骗; +

    +

    + 3、虚拟商品一经售出不予退款,请确认阅读上述条款并无异议后进行购买。 +

    +

    + 4、本平台不提供违法及色情内容,如您发现空间内存在以上内容,请联系人工客服举报处理。 +

    +
    +
    + )} +
    + setVisible(false)} + name={data?.streamer_ext?.name} + price={data?.streamer_ext?.wechat_coin_price} + streamerMid={data?.streamer_ext?.mid} + avatar={data?.streamer_ext?.avatar?.images[0]?.urls[0]} + /> + {/* +
    setVisible(false)}> +
    + +

    草莓不可爱

    +
    + 解锁后展示 +
    +

    + 添加时请备注自己“铁粉空间”昵称 +
    + 若解锁后72小时为通过好友,请联系客服 +

    +
    + 解锁微信(990金币) +
    +
    +
    +
    */} +
    +
    + {/* */} +
    { + router.push("/pay"); + }} + > + + 39.9元立即加入 + +
    +
    +
    +
    +
    + ); +} diff --git a/app/space/person_space_introduce/page.js b/app/space/person_space_introduce/page.js deleted file mode 100644 index 6493442..0000000 --- a/app/space/person_space_introduce/page.js +++ /dev/null @@ -1,200 +0,0 @@ -"use client"; - -import React, { useEffect, useState, useRef } from "react"; -import { Image, Mask } from "antd-mobile"; -import { useRouter } from "next/navigation"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faAngleLeft, faAngleRight } from "@fortawesome/free-solid-svg-icons"; -export default function PersonSpaceIntroduce() { - const router = useRouter(); - const contentBox = useRef(); - // 获取屏幕高度 - // const scrollHeight = 600; - const photos = [ - "https://picsum.photos/seed/picsum/200/300", - "https://picsum.photos/seed/picsum/200/300", - ]; - const [visible, setVisible] = useState(false); - useEffect(() => { - if (contentBox.current) { - contentBox.current.style.transform = "translateY(-12px)"; - // debugger - } - }, []); - return ( -
    -
    -
    - { - router.back(); - }} - /> -
    -
    - {/* 内容 */} -
    -
    -
    -
    -
    - -
    -

    草莓不可爱

    -
    - - 213422 -
    -
    -
    -
    setVisible(true)} - > -
    - -
    -

    查看微信

    -
    -
    -
      -
    • -

      38

      -

      动态

      -
    • -
    • -

      38

      -

      照片

      -
    • -
    • -

      38

      -

      视频

      -
    • -
    -
    -
    -
    -

    空间介绍

    -

    - 草莓秘密基地,一次进入永久权限。都是你想看到的哟。 -

    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -

    付费须知

    -
    -

    - 1、加入后,您可以查看空间内相关内容; -

    -

    - 2、本空间由空间主人自行创建,加入空间前请确认相关风险,本平台不提供相关保证,请避免上当受骗; -

    -

    - 3、虚拟商品一经售出不予退款,请确认阅读上述条款并无异议后进行购买。 -

    -

    - 4、本平台不提供违法及色情内容,如您发现空间内存在以上内容,请联系人工客服举报处理。 -

    -
    -
    -
    - -
    setVisible(false)}> -
    - -

    草莓不可爱

    -
    - 解锁后展示 -
    -

    - 添加时请备注自己“铁粉空间”昵称 -
    - 若解锁后72小时为通过好友,请联系客服 -

    -
    - 解锁微信(990金币) -
    -
    -
    -
    -
    -
    - {/* */} -
    { - router.push("/pay"); - }}> - - 39.9元立即加入 - -
    -
    -
    -
    -
    - ); -} diff --git a/components/AddWeChat/index.js b/components/AddWeChat/index.js new file mode 100644 index 0000000..328ba4d --- /dev/null +++ b/components/AddWeChat/index.js @@ -0,0 +1,117 @@ +"use client"; + +import React, { useState } from "react"; +import { Image, Mask, Toast } from "antd-mobile"; +import require from "@/utils/require"; +import { get } from "@/utils/storeInfo"; +import { useRouter } from "next/navigation"; +export default function AddWeChat({ + visible, + closeMask, + name, + price, + streamerMid, + avatar +}) { + const [isMoneyEnough, setIsMoneyEnough] = useState(true); + const router = useRouter(); + //点击解锁微信按钮 + const unlockWechat = async () => { + //余额不够就显示余额不足前往充值,够就直接购买 + + //先支付,支付成功后添加解锁关系,再展示解锁界面 + //支付金币解锁微信 + const account = get("account"); + if (account) { + try { + const userResponse = await require("POST", "/api/account/list_by_mid", { + body: { + mid: account.mid, + }, + }); + if (userResponse.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + if (userResponse?.data.account?.gold_num >= price) { + console.log("余额足够"); + const data = await require("POST", "/api/vas/one_step_unlock", { + body: { + contact_product_id: "contact_wechat", + uid: streamerMid, + }, + }); + if (data.ret === -1) { + Toast.show({ + icon: "fail", + content: data.msg, + position: "top", + }); + return; + } + //展示解锁界面 + // setIsWechatUnlocked(true); + } else { + setIsMoneyEnough(false); + } + } catch (error) { + console.error(error); + } + } + }; + + return ( + +
    +
    closeMask(false)}>
    +
    + {isMoneyEnough ? ( + <> + +

    {name}

    +
    + 解锁后展示 +
    +

    + 添加时请备注自己“铁粉空间”昵称 +
    + 若解锁后72小时为通过好友,请联系客服 +

    +
    + 解锁微信({price}金币) +
    + + ) : ( + <> +

    + 余额不足 +

    +
    { + router.push("/my/wallet"); + closeMask(false); + setIsMoneyEnough(true); + }} + className="px-4 py-2 bg-[#FF669E] rounded-full items-center justify-center" + > + 前往充值 +
    + + )} +
    +
    +
    + ); +} diff --git a/components/DefaultMask/index.js b/components/DefaultMask/index.js new file mode 100644 index 0000000..bde60a7 --- /dev/null +++ b/components/DefaultMask/index.js @@ -0,0 +1,31 @@ +"use client"; + +import React from "react"; +import { Mask } from "antd-mobile"; +export default function DefaultMask({ + visible, + closeMask, + handleClick, + title, + content +}) { + + return ( + +
    +
    closeMask(false)}>
    +
    + +

    {title}

    +

    {content}

    +
    + 确定 +
    +
    +
    +
    + ); +} diff --git a/components/PaySpacePost/index.js b/components/PaySpacePost/index.js index e1c60a7..12db130 100644 --- a/components/PaySpacePost/index.js +++ b/components/PaySpacePost/index.js @@ -3,14 +3,18 @@ import React, { useRef, useState } from "react"; import { Image } from "antd-mobile"; -export default function PaySpacePost({ - type = "ironFan", - price = "0.00", - status = 0, -}) { +export default function PaySpacePost({ type = "ironFan", status = 0,data={} }) { return ( -
    -
    +
    +
    - {price}元 + {data.price / 100}元
    - {status === 1 ? "已付费解锁" : `${type === "ironFan"?'铁粉':'超粉'}免费查看`} + {!data.is_zone_moment_unlocked ? ( + + {status === 1 + ? "已付费解锁" + : `${type === "ironFan" ? "铁粉" : "超粉"}免费查看`} + + ) : ( + + {data.is_ironfan_visible === 1 + ? "铁粉免费查看" + : data.is_superfanship_enabled === 1 + ? "超粉免费查看" + : "付费解锁"} + + )}
    {status === 0 && ( -

    - 空间内任何消费满399元即可成为铁粉 +

    + 空间内任何消费满{data.ironfanship_price}元即可成为铁粉

    )}
    diff --git a/components/Photos/index.js b/components/Photos/index.js index 6d43249..adfd9c1 100644 --- a/components/Photos/index.js +++ b/components/Photos/index.js @@ -4,6 +4,7 @@ import React, { useEffect, useRef, useState } from "react"; import { Image, ImageViewer, Dialog } from "antd-mobile"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleUp, faClose, faSave } from "@fortawesome/free-solid-svg-icons"; + const tabItems = [ { key: "commend", title: "推荐" }, { key: "follow", title: "关注" }, @@ -13,15 +14,10 @@ export default function Photos({ media }) { const [seeAllPhotos, setSeeAllPhotos] = useState(false); const [currentPhotos, setCurrentPhotos] = useState([]); const [photos, setPhotos] = useState([]); - const [currentVideos, setCurrentVideos] = useState([]); - const [videoVisible, setVideoVisible] = useState({ - video: "", - visible: false, - }); useEffect(() => { if (media) { - let imgArr = media.images.map(item=>({type:"img",url:item.cover_urls[0]})) - let videoArr = media.videos.map(item=>({type:"video",url:item.cover_urls[0],mp4:item.urls[0]})) + let imgArr = media.images.map(item=>({type:"img",url:item.urls?.[0]})) + let videoArr = media.videos.map(item=>({type:"video",url:item.cover_urls?.[0],mp4:item.urls?.[0]})) let arr=[...imgArr,...videoArr] let newPhotos = [...arr]; setPhotos(arr); @@ -50,7 +46,7 @@ export default function Photos({ media }) { key="closeBtn" onClick={() => Dialog.clear()} > - +
    @@ -58,7 +54,7 @@ export default function Photos({ media }) { width="100%" height="100%" controls - className="w-screen h-[70vh] rounded-lg object-cover" + className="w-screen h-[70vh] rounded-lg object-contain" poster={video.url} > - +
    ), @@ -120,18 +116,18 @@ export default function Photos({ media }) { placeholder={
    } - width={currentPhotos.length > 1 ? "100%" : 150} - height={currentPhotos.length > 1 ? "24vw" : 200} - className="rounded" + width={currentPhotos.length > 1 ? "26vw" : "100%"} + height={currentPhotos.length > 1 ? "26vw" : "100%"} + className={`rounded max-w-[80vw] max-h-[80vw] `} fit="cover" src={item.url} /> {item.type == "video" && ( -
    +
    diff --git a/components/PostItem/index.js b/components/PostItem/index.js index 2d9d286..7917abb 100644 --- a/components/PostItem/index.js +++ b/components/PostItem/index.js @@ -7,6 +7,8 @@ import PaySpacePost from "../PaySpacePost"; import { Image } from "antd-mobile"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faAngleRight } from "@fortawesome/free-solid-svg-icons"; +import { handleFollow, thumbsUp } from "@/api/public"; +import {get} from "@/utils/storeInfo" export default function PostItem({ type, follow, @@ -15,9 +17,15 @@ export default function PostItem({ }) { const router = useRouter(); const [isOpenText, setIsOpenText] = useState(false); + const [isFollow, setIsFollow] = useState(data.is_followed); + const [isThumbsUp, setIsThumbsUp] = useState(data.is_thumbed_up); + //判断是否是发帖人 + const [isCreator, setIsCreator] = useState(false); useEffect(() => { + const account = get("account"); + if (account.mid === data.mid) setIsCreator(true); return () => { - router.prefetch("/profile"); + router.prefetch("/profile/"+data.mid); }; }, []); const getDays = useMemo(() => { @@ -35,15 +43,18 @@ export default function PostItem({ className="flex-none w-8 h-8 rounded-full mr-2" src={data.streamer_ext?.avatar.images[0].urls[0]} alt="" - onClick={() => router.push("/profile")} + onClick={() => router.push("/profile/"+data.mid)} />
    -
    +
    handleFollow(isFollow, data?.mid, setIsFollow)} + > {data.streamer_ext?.name} {type == "post" && ( - {data.is_followed ? "已关注" : "关注"} + {isFollow ? "已关注" : "关注"} )}
    @@ -51,50 +62,67 @@ export default function PostItem({

    {data.text}

    - { - data.text?.length>50 && + {data.text?.length > 50 && (
    setIsOpenText(!isOpenText)} - > - {isOpenText ? "收起" : "全文"} -
    - } + className="font-bold text-btn my-4 text-base" + onClick={() => setIsOpenText(!isOpenText)} + > + {isOpenText ? "收起" : "全文"} +
    + )}
    {data.media_component && } - {type == "space" && ( - + {type == "space" && !isCreator && !!data.c_type && ( + )}
    {type == "post" ? ( -
    - { - data.is_active_within_a_week && +
    router.push("/profile/"+data.mid)} + > + {data.is_active_within_a_week ? ( <> - - - {data.days_elapsed_since_the_last_zones_update < 7 - ? `空间${ - data.days_elapsed_since_the_last_zones_update === 0 - ? "今日" - : "new" === 1 - ? "昨日" - : "new" === 2 - ? "前天" - : data.days_elapsed_since_the_last_zones_update + "天前" - }有更新` - : "1" === 2 - ? "空间今日有更新" - : ""} - - + + + {data.days_elapsed_since_the_last_zones_update < 7 && + `空间${ + data.days_elapsed_since_the_last_zones_update === 0 + ? "今日" + : "new" === 1 + ? "昨日" + : "new" === 2 + ? "前天" + : data.days_elapsed_since_the_last_zones_update + + "天前" + }有更新`} + + - } + ) : ( + data?.streamer_ext?.zones?.length !== 0 && ( +
    + 查看TA的空间 + +
    + ) + )}
    ) : (
    @@ -109,9 +137,16 @@ export default function PostItem({
    )}
    -
    +
    thumbsUp(data.id, isThumbsUp, setIsThumbsUp)} + > +
    +
    closeMask(false)}>
    +
    +

    + 当前铁粉解锁进度: + {ironFanProgress}% +

    + +

    {`${ + expenditure / 100 + } / ${ironfanship_price / 100}`}

    +

    + 空间内累计消费达到¥1280即可成为 + 铁粉 + ,可查看所有铁粉专享内容哟,快来成为我的铁粉吧~ +

    +
    + 查看铁粉专享内容 +
    +
    +
    + + ); +} diff --git a/package-lock.json b/package-lock.json index 7c549e1..0ace2f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,16 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@reduxjs/toolkit": "^2.2.6", "antd-mobile": "^5.36.1", "cookies-next": "^4.0.0", "crypto-js": "^4.2.0", + "jsencrypt": "^3.3.2", "next": "14.0.2", "react": "^18", "react-dom": "^18", + "react-redux": "^9.1.2", + "redux": "^5.0.1", "sass": "^1.77.6" }, "devDependencies": { @@ -445,6 +449,29 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@reduxjs/toolkit": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", + "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", + "dependencies": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.2.tgz", @@ -463,6 +490,11 @@ "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.101.tgz", "integrity": "sha512-AAsx9Rgz2IzG8KJ6tXd6ndNkVcu+GYB6U/SnFAaokSPNx2N7dcIIfnighYUNumvj6YS2q39Dejz5tT0NCV7CWA==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@use-gesture/core": { "version": "10.3.0", "resolved": "https://registry.npmmirror.com/@use-gesture/core/-/core-10.3.0.tgz", @@ -1180,6 +1212,15 @@ "node": ">=0.10.0" } }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz", @@ -1323,6 +1364,11 @@ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/jsencrypt": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz", + "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", @@ -2025,6 +2071,28 @@ "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/react-redux": { + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "dependencies": { + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25", + "react": "^18.0", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", @@ -2045,11 +2113,29 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -2928,6 +3014,17 @@ "@react-spring/types": "~9.6.1" } }, + "@reduxjs/toolkit": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", + "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", + "requires": { + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + } + }, "@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.2.tgz", @@ -2946,6 +3043,11 @@ "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.101.tgz", "integrity": "sha512-AAsx9Rgz2IzG8KJ6tXd6ndNkVcu+GYB6U/SnFAaokSPNx2N7dcIIfnighYUNumvj6YS2q39Dejz5tT0NCV7CWA==" }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@use-gesture/core": { "version": "10.3.0", "resolved": "https://registry.npmmirror.com/@use-gesture/core/-/core-10.3.0.tgz", @@ -3457,6 +3559,11 @@ "optional": true, "peer": true }, + "immer": { + "version": "10.1.1", + "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==" + }, "immutable": { "version": "4.3.6", "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz", @@ -3562,6 +3669,11 @@ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "jsencrypt": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz", + "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", @@ -4000,6 +4112,15 @@ "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "react-redux": { + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz", + "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "requires": { + "@types/use-sync-external-store": "^0.0.3", + "use-sync-external-store": "^1.0.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", @@ -4017,11 +4138,27 @@ "picomatch": "^2.2.1" } }, + "redux": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "requires": {} + }, "regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, "resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", diff --git a/package.json b/package.json index e1887b9..c6f758e 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,16 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@reduxjs/toolkit": "^2.2.6", "antd-mobile": "^5.36.1", "cookies-next": "^4.0.0", "crypto-js": "^4.2.0", + "jsencrypt": "^3.3.2", "next": "14.0.2", "react": "^18", "react-dom": "^18", + "react-redux": "^9.1.2", + "redux": "^5.0.1", "sass": "^1.77.6" }, "devDependencies": { diff --git a/public/icons/edit.png b/public/icons/edit.png new file mode 100644 index 0000000..3dc0296 Binary files /dev/null and b/public/icons/edit.png differ diff --git a/store/actions.js b/store/actions.js new file mode 100644 index 0000000..f649044 --- /dev/null +++ b/store/actions.js @@ -0,0 +1,7 @@ +"use client"; +export const handleLogin = (data) => ({ + type: 'HANDLOGIN', + data +}); + + \ No newline at end of file diff --git a/store/index.js b/store/index.js new file mode 100644 index 0000000..f8743d3 --- /dev/null +++ b/store/index.js @@ -0,0 +1,11 @@ +"use client"; + +import { configureStore } from '@reduxjs/toolkit'; // 请确保已安装 "@reduxjs/toolkit" +import reducers from './reducers'; + +const store = configureStore({ + reducer: reducers, +}); +// export default rootReducer; + +export default store; diff --git a/store/reducers.js b/store/reducers.js new file mode 100644 index 0000000..7e40d48 --- /dev/null +++ b/store/reducers.js @@ -0,0 +1,26 @@ + +"use client"; + +import { combineReducers } from "redux"; +const initialState = { + authInfo: { + isSignin: false, + userToken: null, + }, +}; +let text = (data) => { + return data ? data.token : undefined; +}; +const reducer = (state = initialState, action) => { + switch (action.type) { + case "HANDLOGIN": + return { ...state, authInfo: action.data }; + default: + return state; + } +}; +const reducers = combineReducers({ + reducer, +}); + +export default reducers; diff --git a/utils/auth.js b/utils/auth.js index a7daaca..a3309d8 100644 --- a/utils/auth.js +++ b/utils/auth.js @@ -1,47 +1,23 @@ import { setCookie, deleteCookie, getCookie } from "cookies-next"; -import baseRequest from "./baseRequest"; -import { generateSignature } from "@/utils/crypto"; - +import require from "./require"; export async function checkAuth() { - const token = getCookie("token"); - const account = getCookie("account"); - if (token && account) { - //验证是否过期 - try { - const base = baseRequest(); - const signature = generateSignature({ - ...base, - }); - const response = await fetch( - `/api/login/validate?signature=${signature}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - ...base, - }), - } - ); - const data = await response.json(); - if (data.ret === -1) { - return false; - } - return true; - } catch (e) { - console.warn(e); + try { + const data = await require("POST", `/api/login/validate`); + if (data.ret === -1) { + return false; } + return true; + } catch (e) { + console.warn(e); } - return false; } export function signIn(data) { setCookie("token", data.data.token); - setCookie("account", data.data.account); + setCookie("mid", data.data.account.mid); } export function signOut() { deleteCookie("token"); - deleteCookie("account"); + deleteCookie("mid"); } diff --git a/utils/baseRequest.js b/utils/baseRequest.js index 926435d..ce594c6 100644 --- a/utils/baseRequest.js +++ b/utils/baseRequest.js @@ -1,9 +1,9 @@ import { getCookie } from "cookies-next"; - +import { get } from "./storeInfo"; export default function baseRequest() { const token = getCookie("token"); - const accountCookie = getCookie("account"); - const account = accountCookie === undefined ? {} : JSON.parse(accountCookie); + const accountCookie = get("account"); + const account = accountCookie === undefined ? {} : accountCookie; const mid = getCookie("mid"); const b_ts = new Date().getTime(); const baseRequest = { diff --git a/utils/require.js b/utils/require.js index 7157d09..7344e5f 100644 --- a/utils/require.js +++ b/utils/require.js @@ -1,8 +1,9 @@ import baseRequest from "./baseRequest"; +import {get} from "./storeInfo"; // import webviewBaseRequest from "@/utils/webviewBaseRequest"; const base = baseRequest(); // 创建一个封装 fetch 的函数 -export default function customFetch(method, url, options = {}) { +export default function customFetch(method, url, options = {},mid) { // 默认选项 const defaultOptions = { method: method, @@ -14,10 +15,14 @@ export default function customFetch(method, url, options = {}) { } // 可以添加其他默认选项 }; - const body=JSON.stringify({...base,...options.body}) + let newBody = {...options.body} + if(mid){ + newBody.mid=get("account").mid + } + const body=JSON.stringify({...base,...newBody}) // 合并选项 const mergedOptions = { ...defaultOptions, body}; - console.log("mergedOptions",mergedOptions) + // console.log("mergedOptions",mergedOptions) // 返回 Promise 对象 return new Promise((resolve, reject) => { fetch(url, mergedOptions) diff --git a/utils/storeInfo.js b/utils/storeInfo.js index 0fd8994..ebd6f9b 100644 --- a/utils/storeInfo.js +++ b/utils/storeInfo.js @@ -2,11 +2,26 @@ export function save(key,value){ localStorage.setItem(key,value) } export function get(key){ - localStorage.getItem(key) + let data = localStorage.getItem(key); + return data ? JSON.parse(data) : {}; } export function remove(key){ localStorage.removeItem(key) } export function clear(){ localStorage.clear() -} \ No newline at end of file +} + +export function saveUserInfo (data, mobilePhone, regionCode){ + save("token", data.data.token); + save("account", JSON.stringify(data.data.account)); + save("mobile_phone", mobilePhone); + save("region_code", regionCode); + } +export function removeUserInfo (){ + remove("token"); + remove("account"); + remove("mobile_phone"); + remove("region_code"); + + } \ No newline at end of file diff --git a/utils/tools.js b/utils/tools.js index 2c1afe7..a333265 100644 --- a/utils/tools.js +++ b/utils/tools.js @@ -1,12 +1,31 @@ //格式化时间戳 export function formatDeadline(timestamp) { - const date = new Date(timestamp * 1000); // 时间戳以秒为单位,需要乘以1000转换成毫秒 - const year = date.getFullYear(); - const month = ("0" + (date.getMonth() + 1)).slice(-2); - const day = ("0" + date.getDate()).slice(-2); - const hours = ("0" + date.getHours()).slice(-2); - const minutes = ("0" + date.getMinutes()).slice(-2); - const seconds = ("0" + date.getSeconds()).slice(-2); + const date = new Date(timestamp * 1000); // 时间戳以秒为单位,需要乘以1000转换成毫秒 + const year = date.getFullYear(); + const month = ("0" + (date.getMonth() + 1)).slice(-2); + const day = ("0" + date.getDate()).slice(-2); + const hours = ("0" + date.getHours()).slice(-2); + const minutes = ("0" + date.getMinutes()).slice(-2); + const seconds = ("0" + date.getSeconds()).slice(-2); - return `${year}年${month}月${day}日 ${hours}:${minutes}:${seconds}`; - } \ No newline at end of file + return `${year}年${month}月${day}日 ${hours}:${minutes}:${seconds}`; +} + +// 防抖函数 +export function debounce(fn, delay) { + let timer = null; + return (fnn) => { + //清除上一次的延时器 + if (timer) { + clearTimeout(timer); + // return; + console.log(timer); + } + //重新设置新的延时器 + timer = setTimeout(() => { + //修改this指向问题 + // fn.apply(this,value) + fn(fnn); + }, delay); + }; +}