From ceceee6ba7b29c0e05e51c8adc2e18407c8fba21 Mon Sep 17 00:00:00 2001
From: zrll_ <zhengruilin0@126.com>
Date: Thu, 16 Nov 2023 23:47:07 +0800
Subject: [PATCH] add picture show and pay

---
 app/authenticate/page.tsx  | 11 ++---
 app/buy/layout.tsx         | 13 +++++
 app/buy/page.tsx           | 97 ++++++++++++++++++++++++++++++++++++++
 app/dashboard/page.tsx     | 22 +++++++--
 components/navbar.tsx      |  2 +-
 components/picture.tsx     | 18 +++++--
 components/price.tsx       | 20 +++++++-
 config/prices.tsx          |  4 ++
 interface/hooks.ts         |  2 +-
 interface/model/picture.ts | 24 ++++++++++
 interface/pictureAPI.ts    | 11 ++++-
 package.json               |  1 +
 yarn.lock                  | 12 +++++
 13 files changed, 213 insertions(+), 24 deletions(-)
 create mode 100644 app/buy/layout.tsx
 create mode 100644 app/buy/page.tsx
 create mode 100644 interface/model/picture.ts

diff --git a/app/authenticate/page.tsx b/app/authenticate/page.tsx
index cdf5385..acf3308 100644
--- a/app/authenticate/page.tsx
+++ b/app/authenticate/page.tsx
@@ -1,6 +1,6 @@
 'use client'
 
-import {Card, CardBody, CardFooter, Spinner} from "@nextui-org/react";
+import {Card, CardBody, CardFooter} from "@nextui-org/react";
 import {Button} from "@nextui-org/button";
 import React, {ReactNode, useState} from "react";
 import {Input} from "@nextui-org/input";
@@ -118,7 +118,7 @@ export default function Page() {
                     </div>
                 </CardBody>
                 <CardFooter>
-                    <Button style={{position: "relative", left: 7}} color={buttonColor} disabled={buttonDisable}
+                    <Button style={{position: "relative", left: 7}} disabled={buttonDisable} color={buttonColor} isLoading={isLoading}
                             onClick={
                                 () => {
                                     if (state == "email") {
@@ -127,10 +127,8 @@ export default function Page() {
                                             return;
                                         }
                                         setLoading(true);
-                                        setButtonDisable(true);
                                         UserAPI.checkEmail(email).then(r => {
                                             setState(r ? "login" : "register")
-                                            setButtonDisable(false);
                                             setLoading(false);
                                             if (!r) {
                                                 Message.message("验证码已发送")
@@ -139,7 +137,6 @@ export default function Page() {
                                     }
                                     else if (state == "register") {
                                         setLoading(true);
-                                        setButtonDisable(true);
                                         if (password != confirmPassword) {
                                             Message.error("密码输入不一致")
                                         }
@@ -155,11 +152,9 @@ export default function Page() {
                                                 Message.error(message);
                                             }
                                             setLoading(false);
-                                            setButtonDisable(false);
                                         })
                                     } else {//login
                                         setLoading(true);
-                                        setButtonDisable(true);
 
                                         UserAPI.login(email, password).then((r) => {
                                             let [state, text] = r;
@@ -178,7 +173,7 @@ export default function Page() {
                                 }
                             }>
                         {isLoading ?
-                            <div className="justify-center flex space-x-3"><Spinner color="default" size="sm"/><p>加载中...</p></div> : state == "email" ? "下一步" : state == "login" ? "登录" : "注册"}
+                            "加载中..." : state == "email" ? "下一步" : state == "login" ? "登录" : "注册"}
                     </Button>
                 </CardFooter>
             </Card>
diff --git a/app/buy/layout.tsx b/app/buy/layout.tsx
new file mode 100644
index 0000000..7f94cd4
--- /dev/null
+++ b/app/buy/layout.tsx
@@ -0,0 +1,13 @@
+export default function BuyLayout({
+                                       children,
+                                   }: {
+    children: React.ReactNode;
+}) {
+    return (
+        <section className="flex flex-col items-center justify-center gap-4 py-8 md:py-10">
+            <div className="inline-block max-w-lg text-center justify-center">
+                {children}
+            </div>
+        </section>
+    );
+}
diff --git a/app/buy/page.tsx b/app/buy/page.tsx
new file mode 100644
index 0000000..27eb943
--- /dev/null
+++ b/app/buy/page.tsx
@@ -0,0 +1,97 @@
+'use client'
+
+import {title} from "@/components/primitives";
+import {IsLoggedIn} from "@/interface/hooks";
+import {useRouter} from "next/navigation";
+import {useState} from "react";
+import {Card, CardBody, CardFooter, CardHeader, Divider, Select, SelectItem} from "@nextui-org/react";
+import {Button, ButtonGroup} from "@nextui-org/button";
+
+export default function BuyPage() {
+    let router = useRouter();
+    let [group, setGroup] = useState("");
+    let [state, setState] = useState(0);
+    let [time, setTime] = useState("year");
+
+    let pages = [
+        <>
+            <Card>
+                <CardHeader>确认支付信息</CardHeader>
+                <CardBody>
+                    <div className={"space-y-10"}>
+                        <p>请确认下方信息是否正确,如信息错误导致损失,我们不承担任何责任。</p>
+                        <div className={"flex space-x-3"}>
+                            <Select
+                                label="请选择套餐"
+                                className="max-w-xs"
+                                labelPlacement={"outside"}
+                            >
+                                <SelectItem key="started" value="入门">
+                                    入门
+                                </SelectItem>
+                                <SelectItem key="advanced" value="进阶">
+                                    进阶
+                                </SelectItem>
+                                <SelectItem key="professional" value="专业">
+                                    专业
+                                </SelectItem>
+                            </Select>
+                            <ButtonGroup>
+                                <Button color={time == "month" ? "primary" : "default"} onClick={() => {
+                                    setTime("month")
+                                }}>月度</Button>
+                                <Button color={time == "quarter" ? "primary" : "default"} onClick={() => {
+                                    setTime("quarter")
+                                }}>季度</Button>
+                                <Button color={time == "half" ? "primary" : "default"} onClick={() => {
+                                    setTime("half")
+                                }}>半年</Button>
+                                <Button color={time == "year" ? "primary" : "default"} onClick={() => {
+                                    setTime("year")
+                                }}>年度</Button>
+                            </ButtonGroup>
+                        </div>
+                        <div>
+                            <p>支付方式</p>
+                            <ButtonGroup>
+                                <Button>微信支付</Button>
+                                <Button>支付宝</Button>
+                            </ButtonGroup>
+                        </div>
+                    </div>
+                </CardBody>
+                <Divider/>
+                <CardFooter>
+                    <Button color={"primary"} onClick={() => {
+                        setState(1)
+                    }}>下一步</Button>
+                </CardFooter>
+            </Card>
+        </>,
+        <>
+            <Card>
+                <CardHeader>支付</CardHeader>
+                <CardBody>
+                    <p>请扫描下方的二维码进行支付,支付成功后请点击下方的刷新按钮或直接前往用户中心。</p>
+                    <p>将立即进行支付,恕不退款。</p>
+                </CardBody>
+                <Divider/>
+                <CardFooter className={"space-x-3"}>
+                    <Button color={"secondary"}>刷新</Button>
+                    <Button variant={"light"} color={"danger"} onClick={() => {
+                        setState(0);
+                    }}>上一步</Button>
+                </CardFooter>
+            </Card></>
+    ]
+
+    if (!IsLoggedIn) {
+        router.push("/authenticate");
+    }
+    return (
+        <div className="max-w-7xl space-y-10">
+            <h1 className={title()}>购买</h1>
+            {pages[state]}
+        </div>
+    );
+}
diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx
index d0f17c2..1846f42 100644
--- a/app/dashboard/page.tsx
+++ b/app/dashboard/page.tsx
@@ -12,6 +12,9 @@ import {PictureAPI} from "@/interface/pictureAPI";
 import {useState} from "react";
 import {UserAPI} from "@/interface/userAPI";
 import {getGroupPrice} from "@/config/prices";
+import {PictureList} from "@/interface/model/picture";
+import {SERVER_URL} from "@/interface/api";
+import cookie from "react-cookies";
 
 export default function Page() {
     let [used, setUsed] = useState(0);
@@ -19,9 +22,9 @@ export default function Page() {
 
     let [timeLeft, setTimeLeft] = useState(100);
     let [timeDescription, setTimeDescription] = useState("无限时间");
+    let [pictures, setPictures] = useState<PictureList>();
 
-
-    if (used == 0 && total == 1) {
+    function updateInfo() {
         UserAPI.getExtendedInformation().then((r) => {
             if (r == undefined) {
                 router.push("/authenticate");
@@ -43,7 +46,13 @@ export default function Page() {
                 }
             }
         })
-        PictureAPI.getPicturesList().then();
+        PictureAPI.getPicturesList().then((r) => {
+            setPictures(r);
+        });
+    }
+
+    if (used == 0 && total == 1) {
+        updateInfo();
     }
 
 
@@ -78,6 +87,7 @@ export default function Page() {
                             let files = e.target.files!;
                             let file = files[0];
                             PictureAPI.uploadFile(file).then(() => {
+                                updateInfo();
                             });
                         }}></Uploader>
 
@@ -105,8 +115,10 @@ export default function Page() {
                     </a>
                 </CardFooter>
             </Card>
-
-            <Picture url="https://t7.baidu.com/it/u=2961459243,2146986594&fm=193&f=GIF" name="雪景.png"/>
+            {pictures?.records == null ? <a>请上传</a> : pictures!.records.map(picture => <Picture
+                url={SERVER_URL + "/picture/preview?shareMode=2&id=" + picture.id.toString() + "&token=" + cookie.load("token")}
+                name={picture.fileName}/>)}
+            {/*<Picture url="https://t7.baidu.com/it/u=2961459243,2146986594&fm=193&f=GIF" name="雪景.png"/>*/}
         </div>
     )
 }
\ No newline at end of file
diff --git a/components/navbar.tsx b/components/navbar.tsx
index f64e13f..1c7e12a 100644
--- a/components/navbar.tsx
+++ b/components/navbar.tsx
@@ -19,7 +19,7 @@ import NextLink from "next/link";
 import clsx from "clsx";
 
 import {ThemeSwitch} from "@/components/theme-switch";
-import {GithubIcon, Logo, LogoText,} from "@/components/icons";
+import {GithubIcon, LogoText,} from "@/components/icons";
 import {useRouter} from "next/navigation";
 import {useState} from "react";
 import cookie from "react-cookies";
diff --git a/components/picture.tsx b/components/picture.tsx
index 19dfb54..3bd8ef6 100644
--- a/components/picture.tsx
+++ b/components/picture.tsx
@@ -7,6 +7,7 @@ import {DropdownMenu, DropdownTrigger} from "@nextui-org/dropdown";
 import {Message} from "@/components/message";
 import {FiClipboard, FiShare} from "react-icons/fi";
 import {Input} from "@nextui-org/input";
+import cookie from "react-cookies";
 
 export default function Picture(props: PictureProps) {
     let [isChecked, setIsChecked] = useState(false);
@@ -14,7 +15,7 @@ export default function Picture(props: PictureProps) {
     const {isOpen, onOpen, onOpenChange} = useDisclosure();
     const descriptionOpen = useDisclosure();
     let [name, setName] = useState(props.name);
-    let [shareMode, setShareMode] = useState(props.pubicMode);
+    let [shareMode, setShareMode] = useState(props.pubicMode == null ? "none" : props.pubicMode!);
     let timeout: NodeJS.Timeout | null = null;
     let modelTimeout: NodeJS.Timeout | null = null;
     let [link, setShareLink] = useState("");
@@ -24,6 +25,8 @@ export default function Picture(props: PictureProps) {
         Message.success("已生成链接")
     }
 
+    console.log(cookie.load("token"));
+
     // @ts-ignore
     return (
         <>
@@ -35,17 +38,19 @@ export default function Picture(props: PictureProps) {
                 onPress={() => {
                     descriptionOpen.onOpen();
                 }}
+                style={{width: 450}}
             >
                 <Image
                     alt={props.name}
                     className="object-cover"
+                    width={450}
                     height={200}
                     src={props.url}
                     isZoomed
                 />
                 <CardFooter
                     className="justify-between before:bg-white/10 border-white/20 border-1 overflow-hidden py-1 absolute before:rounded-xl rounded-large bottom-1 w-auto shadow-small ml-1 z-10 space-x-2">
-                    <p className="text-tiny font-mono text-white/80">{props.name}</p>&nbsp;
+                    <p className="text-tiny font-mono ">{props.name}</p>&nbsp;
                     <Button isIconOnly className="text-white bg-black/20 justify-center" variant="light" size="sm"
                             onClick={() => {
                                 copy(props.url);
@@ -81,6 +86,7 @@ export default function Picture(props: PictureProps) {
                                     <Image
                                         alt={props.name}
                                         className="object-cover"
+                                        width={450}
                                         height={200}
                                         src={props.url}
                                     />
@@ -95,7 +101,7 @@ export default function Picture(props: PictureProps) {
                                                 variant="bordered"
                                                 className="capitalize"
                                             >
-                                                {shareMode == "watermark" ? "水印" : shareMode == "compressed" ? "压缩" : "原图"}
+                                                {shareMode == "watermark" ? "水印" : shareMode == "compressed" ? "压缩" : shareMode == "none" ? "不公开" : "原图"}
                                             </Button>
                                         </DropdownTrigger>
                                         <DropdownMenu
@@ -107,10 +113,12 @@ export default function Picture(props: PictureProps) {
                                             disabledKeys={["original"]}
                                             onAction={(key) => {
                                                 setShareMode(key.toString());
-                                                let description = key == "watermark" ? "水印" : key == "compressed" ? "压缩" : "原图";
+                                                console.log(key);
+                                                let description = key == "watermark" ? "水印" : key == "compressed" ? "压缩" : key == "none" ? "不公开" : "原图";
                                                 Message.success("已经设置公开等级为:" + description);
                                             }}
                                         >
+                                            <DropdownItem key="none">不公开</DropdownItem>
                                             <DropdownItem key="watermark">公开水印版本</DropdownItem>
                                             <DropdownItem key="compressed">公开压缩版本</DropdownItem>
                                             <DropdownItem key="original"
@@ -119,7 +127,7 @@ export default function Picture(props: PictureProps) {
                                     </Dropdown>
                                     <div>
                                         {link == "" ?
-                                            <Button onClick={generateShareLink}>生成分享链接</Button> :
+                                            <Button disabled={shareMode == "none"} color={shareMode == "none" ? "default" : "primary"} onClick={generateShareLink}>生成分享链接</Button> :
                                             <Input value={link} style={{width: 260}} variant={"underlined"} endContent={
                                                 <Button isIconOnly style={{position: "relative", left: 7}} size="sm"
                                                         onClick={() => {
diff --git a/components/price.tsx b/components/price.tsx
index ea55c6c..cae1ad3 100644
--- a/components/price.tsx
+++ b/components/price.tsx
@@ -4,8 +4,23 @@ import {Card, CardBody, CardFooter, CardHeader, Divider} from "@nextui-org/react
 import {Button} from "@nextui-org/button";
 import {Spacer} from "@nextui-org/spacer";
 import {ReactNode} from "react";
+import {IsLoggedIn} from "@/interface/hooks";
+import {useRouter} from "next/navigation";
 
 export const Price = (price: PriceProps) => {
+    const router = useRouter();
+    function onStart() {
+        if (IsLoggedIn) {
+            if (price.price.price == 0) {
+                router.push("/dashboard");
+            } else {
+                router.push("/buy");
+            }
+        } else {
+            router.push("/authenticate");
+        }
+    }
+
 
     return (
         <>
@@ -22,7 +37,7 @@ export const Price = (price: PriceProps) => {
                 </CardBody>
                 <Divider/>
                 <CardFooter>
-                    <Button variant="ghost" color="primary">开始使用</Button>
+                    <Button variant="ghost" color="primary" onClick={onStart}>开始使用</Button>
                     <Spacer style={{width: 20}}/>
                     <p>{price.price.price} 元/月</p>
                     <Spacer style={{width: 20}}/>
@@ -38,7 +53,8 @@ export type PriceInfo = {
     name: ReactNode,
     singleFile: string,
     allSpace: string,
-    price: number
+    price: number,
+    plainName: string
 }
 
 type PriceProps = {
diff --git a/config/prices.tsx b/config/prices.tsx
index 42563f1..fe8d74f 100644
--- a/config/prices.tsx
+++ b/config/prices.tsx
@@ -3,6 +3,7 @@ import {PiFireDuotone} from "react-icons/pi";
 
 export const priceFree: PriceInfo = {
     name: "免费",
+    plainName: "免费",
     singleFile: "50 MB",
     allSpace: "2 GB",
     price: 0,
@@ -10,6 +11,7 @@ export const priceFree: PriceInfo = {
 
 export const priceStarted: PriceInfo = {
     name: "入门",
+    plainName: "入门",
     singleFile: "50 MB",
     allSpace: "10 GB",
     price: 30, // 0.9 320
@@ -20,6 +22,7 @@ export const priceAdvanced: PriceInfo = {
         <span>进阶</span>
         <PiFireDuotone style={{color: "red"}}/>
     </div>,
+    plainName: "进阶",
     singleFile: "100 MB",
     allSpace: "50 GB",
     price: 50, // 0.8 480
@@ -27,6 +30,7 @@ export const priceAdvanced: PriceInfo = {
 
 export const priceProfessional: PriceInfo = {
     name: "专业",
+    plainName: "专业",
     singleFile: "不限",
     allSpace: "200 GB",
     price: 150, // 0.7 1250
diff --git a/interface/hooks.ts b/interface/hooks.ts
index ee10e5b..b45e7a2 100644
--- a/interface/hooks.ts
+++ b/interface/hooks.ts
@@ -3,7 +3,7 @@
 import cookie from "react-cookies";
 
 export var SetLoggedInState: any = null;
-export var IsLoggedIn: boolean = !cookie.load("token") == undefined;
+export var IsLoggedIn: boolean = cookie.load("token") != undefined;
 
 export function UpdateSetLoggedInHook(hook: any) {
     SetLoggedInState = (loggedIn: boolean) => {
diff --git a/interface/model/picture.ts b/interface/model/picture.ts
new file mode 100644
index 0000000..b10f316
--- /dev/null
+++ b/interface/model/picture.ts
@@ -0,0 +1,24 @@
+export interface Picture {
+    id: number;
+    uid: number;
+    pid: string;
+    fileName: string;
+    size: number;
+    downloads: number;
+    createTime: string;
+    updateTime: string;
+}
+
+export interface PictureList {
+    records: Picture[];
+    total: number;
+    size: number;
+    current: number;
+    orders: any[];
+    optimizeCountSql: boolean;
+    searchCount: boolean;
+    optimizeJoinOfCountSql: boolean;
+    maxLimit?: number;
+    countId?: string;
+    pages: number;
+}
\ No newline at end of file
diff --git a/interface/pictureAPI.ts b/interface/pictureAPI.ts
index 9671e69..6465fc7 100644
--- a/interface/pictureAPI.ts
+++ b/interface/pictureAPI.ts
@@ -1,7 +1,7 @@
 import {SERVER_URL} from "@/interface/api";
-import {base} from "next/dist/build/webpack/config/blocks/base";
 import {Message} from "@/components/message";
 import cookie from "react-cookies";
+import {PictureList} from "@/interface/model/picture";
 
 export class PictureAPI {
     static async uploadFile(file: File) {
@@ -40,6 +40,13 @@ export class PictureAPI {
         };
 
         let result = await fetch(SERVER_URL + "/picture", requestOptions);
-        console.log(result);
+        if (!result.ok) {
+            Message.error("登录状态失效");
+            cookie.remove('token');
+            return;
+        }
+        let list: PictureList = JSON.parse(await result.text());
+        console.log(list);
+        return list;
     }
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index ef946ec..cc3a79a 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
     "postcss": "8.4.29",
     "react": "18.2.0",
     "react-cookies": "^0.1.1",
+    "react-device-detect": "^2.2.3",
     "react-dom": "18.2.0",
     "react-hot-toast": "^2.4.1",
     "react-icons": "^4.11.0",
diff --git a/yarn.lock b/yarn.lock
index 55a75e7..2eca46e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5545,6 +5545,13 @@ react-cookies@^0.1.1:
     cookie "^0.3.1"
     object-assign "^4.1.1"
 
+react-device-detect@^2.2.3:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca"
+  integrity sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==
+  dependencies:
+    ua-parser-js "^1.0.33"
+
 react-dom@18.2.0:
   version "18.2.0"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -6164,6 +6171,11 @@ typescript@5.0.4:
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
   integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
 
+ua-parser-js@^1.0.33:
+  version "1.0.37"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f"
+  integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==
+
 unbox-primitive@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"