diff --git a/.env b/.env
new file mode 100644
index 0000000..22fc2ee
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+# appwrite的项目ID
+VUE_APPWRITE_PROJECT_ID=
diff --git a/package.json b/package.json
index 9d60913..c0c3844 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,9 @@
"@types/lodash": "^4.17.13",
"@types/react-router-dom": "^5.3.3",
"axios": "^1.7.7",
+ "appwrite": "^16.0.2",
"framer-motion": "^11.11.17",
+ "jotai": "^2.10.3",
"lodash": "^4.17.21",
"normalize.css": "^8.0.1",
"react": "^18.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index caa1aa9..3bda8e5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -29,9 +29,15 @@ importers:
axios:
specifier: ^1.7.7
version: 1.7.7
+ appwrite:
+ specifier: ^16.0.2
+ version: 16.0.2
framer-motion:
specifier: ^11.11.17
version: 11.11.17(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ jotai:
+ specifier: ^2.10.3
+ version: 2.10.3(@types/react@18.3.12)(react@18.3.1)
lodash:
specifier: ^4.17.21
version: 4.17.21
@@ -661,6 +667,9 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ appwrite@16.0.2:
+ resolution: {integrity: sha512-sCMVOe9wdB8OneIz6LtoYhp797GEXoudAdygNZgezPiybGOlMPcQ8kP1c/FW1eES0ledaqgaZ0PHb+LwZia8pw==}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -1015,6 +1024,18 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ jotai@2.10.3:
+ resolution: {integrity: sha512-Nnf4IwrLhNfuz2JOQLI0V/AgwcpxvVy8Ec8PidIIDeRi4KCFpwTFIpHAAcU+yCgnw/oASYElq9UY0YdUUegsSA==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=17.0.0'
+ react: '>=17.0.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -2012,6 +2033,8 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ appwrite@16.0.2: {}
+
argparse@2.0.1: {}
asynckit@0.4.0: {}
@@ -2411,6 +2434,11 @@ snapshots:
isexe@2.0.0: {}
+ jotai@2.10.3(@types/react@18.3.12)(react@18.3.1):
+ optionalDependencies:
+ '@types/react': 18.3.12
+ react: 18.3.1
+
js-tokens@4.0.0: {}
js-yaml@4.1.0:
diff --git a/src/api/index.ts b/src/api/index.ts
new file mode 100644
index 0000000..36b0b7d
--- /dev/null
+++ b/src/api/index.ts
@@ -0,0 +1,16 @@
+import { Toast } from "@/utils/toast"
+import { Account, Client, ID } from "appwrite"
+
+const client = new Client()
+
+client.setProject(import.meta.env.VUE_APPWRITE_PROJECT_ID)
+
+export async function register(opts: { email: string; password: string }, errText: string) {
+ const account = new Account(client)
+ try {
+ await account.create(ID.unique(), opts.email, opts.password)
+ } catch (error: any) {
+ Toast.error(error?.message ?? errText)
+ return Promise.reject(error)
+ }
+}
diff --git a/src/base/withPage.tsx b/src/base/withPage.tsx
index a97f6c0..5e9f76d 100644
--- a/src/base/withPage.tsx
+++ b/src/base/withPage.tsx
@@ -1,5 +1,5 @@
import { FC, ReactNode } from "react"
-import { LeftIn, OpacityIn } from "@/effect"
+import { OpacityIn } from "@/effect"
import styled from "styled-components"
interface PageProps {
diff --git a/src/effect/index.tsx b/src/effect/index.tsx
index 3974e40..59c85af 100644
--- a/src/effect/index.tsx
+++ b/src/effect/index.tsx
@@ -1,5 +1,5 @@
import { motion } from "framer-motion"
-import styled from "styled-components"
+
export function OpacityIn({ children, className }: any) {
return (
diff --git a/src/layout/GlobalStyles.ts b/src/layout/GlobalStyles.ts
index 56c2963..6e1d0f9 100644
--- a/src/layout/GlobalStyles.ts
+++ b/src/layout/GlobalStyles.ts
@@ -1,5 +1,5 @@
import { createGlobalStyle } from "styled-components"
export const GlobalStyles = createGlobalStyle`
-
+
`
diff --git a/src/layout/base.tsx b/src/layout/base.tsx
index 0a9b4c3..d9e0e88 100644
--- a/src/layout/base.tsx
+++ b/src/layout/base.tsx
@@ -3,6 +3,9 @@ import { ReactNode, useState } from "react"
import { NavLink, useHistory } from "react-router-dom"
import styled from "styled-components"
import { GlobalStyles } from "./GlobalStyles"
+import { Notice } from "@/ui/Notice"
+import { Markdown } from "@/ui/Markdown"
+import { Register } from "@/ui/Register/Register"
interface PageProps {
children: ReactNode
@@ -24,18 +27,29 @@ const ContentWrapper = styled.div`
function BaseLayout(props: PageProps) {
const [isOpen, setIsOpen] = useState(false)
const [isOpenAbout, setIsOpenAbout] = useState(false)
+ const [isOpenRegister, setIsOpenRegister] = useState(false)
const router = useHistory()
+ const about = `
+> 人生如逆水行舟,不进则退。
+
+ 我来自一南方小镇,不善言辞,今作此小站,聊慰平生。
+
+ 如有意愿,欢迎联系。
+
+[Github](https://github.com/npmrun)
+ `
+
return (
- 东风天堂
+ 天涯行宫
-
+
setIsOpen(isOpen)}
content={
}
fill={true}
placement="bottom"
>
-
+
+
+
+
-
+
{props.children}
-
)
diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx
index 091a4ba..1593197 100644
--- a/src/router/AppRouter.tsx
+++ b/src/router/AppRouter.tsx
@@ -1,5 +1,5 @@
import { HashRouter as Router, Switch, Redirect, Route } from "react-router-dom"
-import React, { Fragment, ReactNode } from "react"
+import React, { Fragment } from "react"
import AuthRoute from "./AuthRoute"
import routes from "./route"
import Loading from "./Loading"
diff --git a/src/router/Loading.tsx b/src/router/Loading.tsx
index 2e495fd..e8bf24f 100644
--- a/src/router/Loading.tsx
+++ b/src/router/Loading.tsx
@@ -1,8 +1,6 @@
-import { createRef, ReactNode, useEffect, useState } from "react"
+import { ReactNode, useEffect, useState } from "react"
import styled from "styled-components"
-interface IProps {}
-
const LoadingComp = styled.div`
position: absolute;
top: 0;
@@ -37,7 +35,7 @@ const LoadingComp = styled.div`
}
`
-export default function Loading(props: IProps): ReactNode {
+export default function Loading(): ReactNode {
const [show, setShow] = useState(false)
useEffect(() => {
const timeId = setTimeout(() => {
diff --git a/src/router/index.ts b/src/router/index.ts
index d92cc67..742b334 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -30,7 +30,7 @@ export default function useRoute() {
const { pathname } = useLocation()
let oroute = JSON.parse(JSON.stringify(routes))
let index = foundRoute(routes, pathname)
- let isLayout = true
+ // let isLayout = true
let curA = null
let curB = null
if (index && index.length) {
@@ -45,7 +45,7 @@ export default function useRoute() {
cur.children = []
}
if (cur.layout != undefined) {
- isLayout = !!cur.layout
+ // isLayout = !!cur.layout
}
curA = cur
curB = res
diff --git a/src/store/account.ts b/src/store/account.ts
new file mode 100644
index 0000000..21c58b4
--- /dev/null
+++ b/src/store/account.ts
@@ -0,0 +1,9 @@
+import { atom } from "jotai"
+
+const userStore = {
+ userAtom: atom({
+ token: "",
+ }),
+}
+
+export { userStore }
diff --git a/src/ui/Hero/index.tsx b/src/ui/Hero/index.tsx
new file mode 100644
index 0000000..a76c103
--- /dev/null
+++ b/src/ui/Hero/index.tsx
@@ -0,0 +1,52 @@
+import { OpacityIn } from "@/effect"
+import styled from "styled-components"
+
+const FigureElement = styled.figure`
+ margin: 0;
+ position: relative;
+ height: 350px;
+ .img-wrapper {
+ height: 100%;
+ width: 100%;
+ filter: brightness(0.75);
+ img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ object-position: top -100px right 0;
+ display: block;
+ }
+ }
+ .mask {
+ background: url(data:image/webp;base64,UklGRkYAAABXRUJQVlA4WAoAAAAQAAAAAQAAAQAAQUxQSAUAAAAA/wAAAABWUDggGgAAADABAJ0BKgIAAgAAwBIlpAADcAD+/u6qAAAA);
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ }
+ figcaption {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ color: white;
+ padding: 15px 20px;
+ font-size: 2em;
+ text-align: justify;
+ }
+`
+
+export function Hero() {
+ return (
+
+
+
+
+
+ 珠光照月,斑驳裂影。桃艳压山,风月无边。
+
+ )
+}
diff --git a/src/ui/Markdown/index.tsx b/src/ui/Markdown/index.tsx
new file mode 100644
index 0000000..680a159
--- /dev/null
+++ b/src/ui/Markdown/index.tsx
@@ -0,0 +1,22 @@
+import ReactMarkdown from "react-markdown"
+import styled from "styled-components"
+
+const MarkdownElement = styled(ReactMarkdown)`
+ blockquote {
+ margin: 0;
+ background-color: gainsboro;
+ padding: 10px 10px 10px 10px;
+ p:last-child {
+ margin-bottom: 0;
+ font-size: 1.1em;
+ }
+ }
+`
+
+interface IProps {
+ children: string
+}
+
+export function Markdown({ children }: IProps) {
+ return {children}
+}
diff --git a/src/ui/Notice/index.tsx b/src/ui/Notice/index.tsx
new file mode 100644
index 0000000..c12cd1c
--- /dev/null
+++ b/src/ui/Notice/index.tsx
@@ -0,0 +1,11 @@
+// import { Tag } from "@blueprintjs/core";
+
+export function Notice() {
+ return (
+ <>
+ {/*
+ 公告: 吾生也有涯,而知也无涯。以有涯随无涯,殆已!
+ */}
+ >
+ )
+}
diff --git a/src/ui/Register/Register.tsx b/src/ui/Register/Register.tsx
new file mode 100644
index 0000000..e93b9eb
--- /dev/null
+++ b/src/ui/Register/Register.tsx
@@ -0,0 +1,111 @@
+// import { Tag } from "@blueprintjs/core";
+
+import { register } from "@/api"
+import { Toast } from "@/utils/toast"
+import { Button, FormGroup, InputGroup, Intent, Tooltip } from "@blueprintjs/core"
+import { FC, ReactNode, useCallback, useEffect, useState } from "react"
+
+interface IProps {
+ onSuccess?: () => void
+ children?: {
+ Wrapper?: FC<{ children: ReactNode }>
+ Default?: FC<{ children: ReactNode }> //({ children }: { children: ReactNode }) => JSX.Element
+ }
+}
+
+export function Register({ onSuccess, children }: IProps) {
+ const [isLoading, setIsLoading] = useState(false)
+ const [showPassword, setShowPassword] = useState(false)
+ const [email, setEmail] = useState("")
+ const [password, setPassword] = useState("")
+ const [repeatPassword, setRepeatPassword] = useState("")
+
+ const clickRegister = useCallback(async () => {
+ if (isLoading) {
+ Toast.success("正在请求中")
+ return
+ }
+ if (!email) return Toast.error("请输入邮箱")
+ if (!password) return Toast.error("请输入密码")
+ if (!repeatPassword) return Toast.error("请再一次输入密码")
+ if (repeatPassword !== password) return Toast.error("两次输入的密码不一致")
+ let toastID = Toast.loading("注册中")
+ setIsLoading(true)
+ try {
+ await register({ email, password }, "注册失败")
+ Toast.success("注册成功")
+ onSuccess && onSuccess()
+ } catch (error) {
+ console.error(error)
+ } finally {
+ Toast.loadingDismiss(toastID)
+ setIsLoading(false)
+ }
+ }, [password, email, repeatPassword, isLoading])
+
+ useEffect(()=>{
+ return ()=>{
+ console.log("取消请求");
+ }
+ }, [])
+
+ const WrapperComp = useCallback(
+ ({ children: child }: { children: ReactNode }) => children?.Wrapper?.({ children: child }) ?? <>{child}>,
+ [children?.Wrapper],
+ )
+ const DefaultComp = useCallback(
+ ({ children: child }: { children: ReactNode }) => children?.Default?.({ children: child }) ?? <>{child}>,
+ [children?.Default],
+ )
+
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/src/utils/toast.ts b/src/utils/toast.ts
new file mode 100644
index 0000000..914bf28
--- /dev/null
+++ b/src/utils/toast.ts
@@ -0,0 +1,21 @@
+import { OverlayToaster, Position, Toaster } from "@blueprintjs/core"
+
+interface Toaster2 {
+ error: Function
+ success: Function
+ loading: Function
+ loadingDismiss: Function
+}
+
+export const Toast: Toaster & Toaster2 = OverlayToaster.create({ position: Position.TOP_RIGHT }) as any
+export const TToast: Toaster & Toaster2 = OverlayToaster.create({ position: Position.TOP }) as any
+
+Toast.loadingDismiss = (key: string) => {
+ TToast.dismiss(key)
+}
+Toast.error = (message: string) => Toast.show({ message: message, icon: "error", intent: "danger", timeout: 3000 })
+Toast.success = (message: string) => Toast.show({ message: message, intent: "success", timeout: 3000 })
+Toast.loading = (message: string) => {
+ const key = TToast.show({ message: message, intent: "primary", timeout: 0 })
+ return key
+}
diff --git a/src/views/Child.tsx b/src/views/Child.tsx
index 2d724d4..12144fa 100644
--- a/src/views/Child.tsx
+++ b/src/views/Child.tsx
@@ -1,5 +1,4 @@
-import { Button, Dialog, DialogBody, DialogFooter } from "@blueprintjs/core";
-import { useCallback, useState } from "react";
+import { Button } from "@blueprintjs/core";
function Child() {
return <>
diff --git a/src/views/Home/index.tsx b/src/views/Home/index.tsx
index 61c01d3..7c0e9aa 100644
--- a/src/views/Home/index.tsx
+++ b/src/views/Home/index.tsx
@@ -1,37 +1,19 @@
import withPage from "@/base/withPage"
-import { useHistory } from "react-router-dom"
-import styled from "styled-components"
-import ReactMarkdown from "react-markdown"
-import MdText from "./index.md?raw"
+import { Hero } from "@/ui/Hero"
+import { ReactNode } from "react"
-const Title = styled.h1`
- font-size: 1.5em;
- text-align: center;
- color: #bf4f74;
-`
+interface IProps {
+ children: ReactNode
+}
-const Wrapper = styled.section`
- padding: 4em;
- background: papayawhip;
-`
-
-export default withPage(function Project({ children }) {
- const router = useHistory()
- function toChild() {
- router.push("/project/child")
- }
+export default withPage(function Project({}: IProps) {
return (
-
-
- HOME
-
-
{MdText}
-
-
- vaas
-
- {children}
+ <>
+
+
+
+
-
+ >
)
})
diff --git a/vite.config.ts b/vite.config.ts
index c2f38d9..a9b62b2 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -4,6 +4,7 @@ import react from "@vitejs/plugin-react-swc"
// https://vite.dev/config/
export default defineConfig({
+ base: "./",
resolve: {
alias: {
"@": join(__dirname, "src"),