Browse Source

feat: 增加一些功能

v7
谢亚昕 4 months ago
parent
commit
5cfce7e636
  1. 2
      .env
  2. 4
      package.json
  3. 68
      pnpm-lock.yaml
  4. BIN
      public/Alibaba-PuHuiTi-Heavy.ttf
  5. 2
      public/data.json
  6. 2
      src/App.tsx
  7. 15
      src/api/_utils/index.ts
  8. 57
      src/api/http.ts
  9. 17
      src/api/index.ts
  10. 15
      src/index.css
  11. 13
      src/layout/base.tsx
  12. 7
      src/plugins/request.ts
  13. 48
      src/store/account.ts
  14. 9
      src/ui/Login/Login.tsx
  15. 4
      src/ui/Register/Register.tsx
  16. 41
      src/views/Home/index.tsx

2
.env

@ -1,2 +1,2 @@
# appwrite的项目ID
VUE_APPWRITE_PROJECT_ID=
VITE_APP_WRITE_PROJECT_ID=

4
package.json

@ -17,6 +17,7 @@
"@blueprintjs/icons": "^5.14.0",
"@blueprintjs/table": "^5.2.5",
"@types/lodash": "^4.17.13",
"ahooks": "^3.8.2",
"appwrite": "^16.0.2",
"axios": "^1.7.7",
"framer-motion": "^11.11.17",
@ -29,7 +30,8 @@
"react-markdown": "^9.0.1",
"react-router": "^7.0.1",
"react-toastify": "^10.0.6",
"styled-components": "^6.1.13"
"styled-components": "^6.1.13",
"swr": "^2.2.5"
},
"devDependencies": {
"@eslint/js": "^9.13.0",

68
pnpm-lock.yaml

@ -23,6 +23,9 @@ importers:
'@types/lodash':
specifier: ^4.17.13
version: 4.17.13
ahooks:
specifier: ^3.8.2
version: 3.8.2(react@18.3.1)
appwrite:
specifier: ^16.0.2
version: 16.0.2
@ -62,6 +65,9 @@ importers:
styled-components:
specifier: ^6
version: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
swr:
specifier: ^2.2.5
version: 2.2.5(react@18.3.1)
devDependencies:
'@eslint/js':
specifier: ^9.13.0
@ -657,6 +663,12 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
ahooks@3.8.2:
resolution: {integrity: sha512-iuYD3PWopdw9HqH++3ul6/Oxl9Q9/tP+np4yiYd0mIzN3QfDKnyII28MWUcmSkaHF415R6bUcrl0vD/80NNogg==}
engines: {node: '>=8.0.0'}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@ -730,6 +742,9 @@ packages:
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@ -772,6 +787,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'}
@ -989,6 +1007,9 @@ packages:
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
intersection-observer@0.12.2:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
@ -1032,6 +1053,10 @@ packages:
react:
optional: true
js-cookie@3.0.5:
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
engines: {node: '>=14'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -1361,6 +1386,9 @@ packages:
remark-rehype@11.1.1:
resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==}
resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@ -1380,6 +1408,10 @@ packages:
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
screenfull@5.2.0:
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
engines: {node: '>=0.10.0'}
semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
engines: {node: '>=10'}
@ -1436,6 +1468,11 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
swr@2.2.5:
resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@ -2010,6 +2047,19 @@ snapshots:
acorn@8.14.0: {}
ahooks@3.8.2(react@18.3.1):
dependencies:
'@babel/runtime': 7.26.0
dayjs: 1.11.13
intersection-observer: 0.12.2
js-cookie: 3.0.5
lodash: 4.17.21
react: 18.3.1
react-fast-compare: 3.2.2
resize-observer-polyfill: 1.5.1
screenfull: 5.2.0
tslib: 2.6.3
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@ -2099,6 +2149,8 @@ snapshots:
classnames@2.5.1: {}
client-only@0.0.1: {}
clsx@2.1.1: {}
color-convert@2.0.1:
@ -2139,6 +2191,8 @@ snapshots:
csstype@3.1.3: {}
dayjs@1.11.13: {}
debug@4.3.7:
dependencies:
ms: 2.1.3
@ -2388,6 +2442,8 @@ snapshots:
inline-style-parser@0.2.4: {}
intersection-observer@0.12.2: {}
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@ -2416,6 +2472,8 @@ snapshots:
'@types/react': 18.3.12
react: 18.3.1
js-cookie@3.0.5: {}
js-tokens@4.0.0: {}
js-yaml@4.1.0:
@ -2897,6 +2955,8 @@ snapshots:
unified: 11.0.5
vfile: 6.0.3
resize-observer-polyfill@1.5.1: {}
resolve-from@4.0.0: {}
reusify@1.0.4: {}
@ -2933,6 +2993,8 @@ snapshots:
dependencies:
loose-envify: 1.4.0
screenfull@5.2.0: {}
semver@7.6.3: {}
sentence-case@3.0.4:
@ -2991,6 +3053,12 @@ snapshots:
dependencies:
has-flag: 4.0.0
swr@2.2.5(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
use-sync-external-store: 1.2.2(react@18.3.1)
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0

BIN
public/Alibaba-PuHuiTi-Heavy.ttf

Binary file not shown.

2
public/data.json

@ -1,3 +1,3 @@
{
"aa": "1231"
"a": "FUCK"
}

2
src/App.tsx

@ -3,8 +3,10 @@ import { ToastContainer, Bounce } from "react-toastify"
import BaseLayout from "@/layout/base"
import Home from "@/views/Home"
import NoMatch from "@/viewsSys/NoMatch"
import AccountStore from "@/store/account"
export default function App() {
AccountStore.initUser()
return (
<>
<HashRouter>

15
src/api/_utils/index.ts

@ -0,0 +1,15 @@
import { Account, Client, ID } from "appwrite"
import { toast } from "react-toastify"
const client = new Client()
client.setProject(import.meta.env.VUE_APPWRITE_PROJECT_ID)
export async function apiRegister(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)
}
}

57
src/api/http.ts

@ -0,0 +1,57 @@
import { Account, Client, ID } from "appwrite"
import { toast } from "react-toastify"
const client = new Client()
client.setProject(import.meta.env.VITE_APP_WRITE_PROJECT_ID)
export async function apiRegister(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)
}
}
export async function apiLogin(opts: { email: string; password: string }, errText?: string) {
const account = new Account(client)
try {
return await account.createEmailPasswordSession(opts.email, opts.password);
} catch (error: any) {
toast.error(error?.message ?? errText)
return Promise.reject(error)
}
}
async function checkLogin() {
try {
await getAccount()
return true
} catch (error) {
return false
}
}
export async function listSessions(errText?: string) {
const account = new Account(client)
try {
return await await account.listSessions();
} catch (error: any) {
toast.error(error?.message ?? errText)
return Promise.reject(error)
}
}
export async function getAccount(errText?: string) {
const account = new Account(client)
try {
return await await account.get();
} catch (error: any) {
toast.error(error?.message ?? errText)
return Promise.reject(error)
}
}

17
src/api/index.ts

@ -1,15 +1,4 @@
import { Account, Client, ID } from "appwrite"
import { toast } from "react-toastify"
const client = new Client()
import * as Http from "./http"
client.setProject(import.meta.env.VUE_APPWRITE_PROJECT_ID)
export async function apiRegister(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)
}
}
// export * from "./http"
export default Http

15
src/index.css

@ -4,12 +4,23 @@
@import "@blueprintjs/icons/lib/css/blueprint-icons.css";
@import "@blueprintjs/table/lib/css/table.css";
html,body, #root{
@font-face {
font-family: "Alibaba";
src: url("/Alibaba-PuHuiTi-Heavy.ttf") format("woff");
}
html,
body,
#root {
height: 100%;
}
body {
height: 100%;
/* font-family: Alibaba; */
}
.container {
max-width: 1200px;
margin: 0 auto;
}

13
src/layout/base.tsx

@ -7,6 +7,8 @@ import { Notice } from "@/ui/Notice"
import { Markdown } from "@/ui/Markdown"
import { Register } from "@/ui/Register/Register"
import { Login } from "@/ui/Login/Login"
import AccountStore from "@/store/account"
import { useAtom } from "jotai"
interface PageProps {
// children: ReactNode
@ -30,6 +32,7 @@ function BaseLayout(props: PageProps) {
const [isOpenAbout, setIsOpenAbout] = useState(false)
const [isOpenAuth, setIsOpenAuth] = useState(false)
const [isRegister, setIsRegister] = useState(false)
const [user] = useAtom(AccountStore.userStore)
const about = `
> 退
@ -79,11 +82,17 @@ function BaseLayout(props: PageProps) {
<Notice />
</Navbar.Group>
<Navbar.Group align={Alignment.RIGHT}>
{
!user.isLogin && (
<>
<Button icon="user" className="bp5-minimal" text="登录 / 注册" onClick={() => setIsOpenAuth(true)} />
<Navbar.Divider />
<NavLink to={"/about"}>
</>
)
}
{/* <NavLink to={"/about"}> */}
<Button onClick={() => setIsOpenAbout(true)} className="bp5-minimal" text="关于" />
</NavLink>
{/* </NavLink> */}
</Navbar.Group>
</Navbar>
<ContentWrapper>

7
src/plugins/request.ts

@ -11,11 +11,12 @@ Fly.use([allPlugin.loading])
Fly.init("$defalut", {
timeout: 10000,
}, [allPlugin.repeat])
}, [])
Fly.init("empty", {
Fly.init("up", {
timeout: 10000,
})
}, [allPlugin.repeat])
// Fly.disposeAll()
// https://api.52vmy.cn/
// Fly.invoke().request({ method: "get", url: "http://localhost:5173/asd", global: false }).then((res) => console.log(res.data)).catch(console.log)

48
src/store/account.ts

@ -1,9 +1,45 @@
import { atom } from "jotai"
import { atom, useAtom } from "jotai"
import Http from "@/api"
import { useEffect } from "react"
import { atomWithStorage } from 'jotai/utils';
const userStore = {
userAtom: atom({
token: "",
}),
const userStore = atomWithStorage<{
isLogin: boolean
userInfo: any
}>('user', {
isLogin: false,
userInfo: null
});
// const userStore = atom<{
// isLogin: boolean
// userInfo: any
// }>({
// isLogin: false,
// userInfo: null
// });
function initUser() {
const [user, setUser] = useAtom(userStore)
let isRequest = false
useEffect(() => {
if (isRequest) return
isRequest = true
Http.getAccount().then(res => {
setUser({
...user,
isLogin: true,
userInfo: res
})
}).catch(() => {
setUser({
...user,
isLogin: false,
userInfo: false
})
})
}, [])
return [user, setUser]
}
export { userStore }
export default { userStore, initUser }

9
src/ui/Login/Login.tsx

@ -1,3 +1,4 @@
import Http from "@/api"
import { Button, FormGroup, InputGroup, Intent, Tooltip } from "@blueprintjs/core"
import { FC, ReactNode, useCallback, useState } from "react"
import { SubmitHandler, useForm } from "react-hook-form"
@ -22,8 +23,12 @@ export function Login({ onSuccess, children }: IProps) {
handleSubmit,
formState: { errors },
} = useForm<Inputs>()
const onSubmit: SubmitHandler<Inputs> = data => {
console.log(data)
const onSubmit: SubmitHandler<Inputs> = async (data) => {
console.log(await Http.apiLogin({
email: data.email,
password: data.password,
}));
onSuccess && onSuccess()
}

4
src/ui/Register/Register.tsx

@ -1,6 +1,6 @@
// import { Tag } from "@blueprintjs/core";
import { apiRegister } from "@/api"
import Http from "@/api"
import { Button, FormGroup, InputGroup, Intent, Tooltip } from "@blueprintjs/core"
import { FC, FormEvent, ReactNode, useCallback, useEffect, useRef, useState } from "react"
import { useForm } from "react-hook-form"
@ -33,7 +33,7 @@ export function Register({ onSuccess, children }: IProps) {
if (repeatPassword !== password) return toast.error("两次输入的密码不一致")
setIsLoading(true)
try {
await toast.promise(apiRegister({ email, password }, "注册失败"), {
await toast.promise(Http.apiRegister({ email, password }, "注册失败"), {
pending: '注册中',
success: '注册成功',
error: '注册失败'

41
src/views/Home/index.tsx

@ -1,36 +1,41 @@
import withPage from "@/base/withPage"
import { Hero } from "@/ui/Hero"
import { Button } from "@blueprintjs/core"
import { ReactNode, useCallback } from "react"
import { Fly } from "@/plugins/request"
import { toast } from "react-toastify"
import useSWRMutation from 'swr/mutation'
import Http from "@/api";
interface IProps {
// children: ReactNode
}
export default function Home({ }: IProps) {
const onClick = useCallback(() => {
// toast.success("Wow so easy !")
Fly.invoke().request<{ a: number }>({
const { data, error, trigger, isMutating } = useSWRMutation("/data.json", (url: string) => {
return Fly.invoke().request<{ a: number }>({
method: "get",
url: "/data.json",
global: false,
loadingAttrs: {
text: "aasd",
minShow: false
}
}).then((res) => console.log(res.data)).catch(err => {
toast.error(err.stack)
url: url
})
}, [])
}, {
onSuccess(resule, params) {
console.log(resule, params);
},
onError(resule, params) {
console.log(resule, params);
},
});
const aa = () => {
Http.getAccount()
}
return (
<>
<Hero></Hero>
{/* <Hero></Hero> */}
<div className="container">
<Button onClick={onClick}></Button>
<div>{JSON.stringify(data?.data)}</div>
<div>{error?.message}</div>
{isMutating ? 1 : 2}
<Button onClick={() => trigger()}></Button>
<Button onClick={() => aa()}>Sessions</Button>
</div>
</>
)

Loading…
Cancel
Save