皮肤上传
This commit is contained in:
parent
6b489789d3
commit
ab5e87ef42
@ -4,6 +4,7 @@ import Login from '@/views/Login'
|
||||
import Register from '@/views/Register'
|
||||
import Profile from '@/views/profile/Profile'
|
||||
import Textures from '@/views/profile/Textures'
|
||||
import Security from '@/views/profile/Security'
|
||||
import Layout from '@/views/Layout'
|
||||
|
||||
const router = createBrowserRouter([
|
||||
@ -19,7 +20,7 @@ function Root() {
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/textures" element={<Textures />} />
|
||||
|
||||
<Route path="/security" element={<Security />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
<ScrollRestoration />
|
||||
|
@ -53,4 +53,21 @@ export async function yggProfile(uuid: string) {
|
||||
throw new Error(data?.errorMessage)
|
||||
}
|
||||
return data as YggProfile
|
||||
}
|
||||
|
||||
export async function upTextures(uuid: string, token: string, textureType: 'skin' | 'cape', model: 'slim' | '', file: File) {
|
||||
const f = new FormData()
|
||||
f.set("file", file)
|
||||
f.set("model", model)
|
||||
|
||||
const r = await fetch(import.meta.env.VITE_APIADDR + "/api/yggdrasil/api/user/profile/" + uuid + "/" + textureType, {
|
||||
method: "PUT",
|
||||
body: f,
|
||||
headers: {
|
||||
"Authorization": "Bearer " + token
|
||||
}
|
||||
})
|
||||
if (r.status != 204) {
|
||||
throw new Error("上传失败 " + String(r.status))
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ export interface tokenData {
|
||||
accessToken: string
|
||||
selectedProfile: {
|
||||
name: string
|
||||
uuid: string
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ const MyToolbar = memo(function MyToolbar() {
|
||||
setAnchorEl(null);
|
||||
setNowUser({ name: "", uuid: "" })
|
||||
setToken("")
|
||||
navigate("/")
|
||||
navigate("/login")
|
||||
})
|
||||
|
||||
useTilg()
|
||||
@ -240,7 +240,7 @@ const MyDrawer = function MyDrawer() {
|
||||
{
|
||||
icon: <SecurityIcon />,
|
||||
title: '安全设置',
|
||||
link: '/setting'
|
||||
link: '/security'
|
||||
}
|
||||
] as ListItem[], [])
|
||||
|
||||
|
@ -49,7 +49,7 @@ export default function SignIn() {
|
||||
if (!v) return
|
||||
setToken(v.accessToken)
|
||||
setUserInfo({
|
||||
uuid: v.selectedProfile.uuid,
|
||||
uuid: v.selectedProfile.id,
|
||||
name: v.selectedProfile.name,
|
||||
})
|
||||
navigate("/profile")
|
||||
|
@ -5,10 +5,9 @@ import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import CardHeader from '@mui/material/CardHeader';
|
||||
import { useHover, useRequest, useUnmount } from 'ahooks';
|
||||
import { ApiErr } from '@/apis/error';
|
||||
import { LayoutAlertErr, token } from '@/store/store';
|
||||
import { LayoutAlertErr, user } from '@/store/store';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { userInfo, yggProfile } from '@/apis/apis';
|
||||
import { yggProfile } from '@/apis/apis';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
@ -22,32 +21,21 @@ import useTilg from 'tilg';
|
||||
import useTitle from '@/hooks/useTitle';
|
||||
|
||||
const Profile = function Profile() {
|
||||
const nowToken = useAtomValue(token)
|
||||
const navigate = useNavigate();
|
||||
const setErr = useSetAtom(LayoutAlertErr)
|
||||
const userinfo = useAtomValue(user)
|
||||
|
||||
const [textures, setTextures] = useState({ skin: "", cape: "", model: "default" })
|
||||
useTitle("个人信息")
|
||||
|
||||
const userinfo = useRequest(() => userInfo(nowToken), {
|
||||
refreshDeps: [nowToken],
|
||||
cacheKey: "/api/v1/user" + nowToken,
|
||||
staleTime: 60000,
|
||||
onError: e => {
|
||||
if (e instanceof ApiErr && e.code == 5) {
|
||||
navigate("/login")
|
||||
}
|
||||
console.warn(e)
|
||||
setErr(String(e))
|
||||
}
|
||||
})
|
||||
|
||||
const SkinInfo = useRequest(() => yggProfile(userinfo.data?.uuid ?? ""), {
|
||||
cacheKey: "/api/yggdrasil/sessionserver/session/minecraft/profile/" + userinfo.data?.uuid,
|
||||
const SkinInfo = useRequest(() => yggProfile(userinfo.uuid ?? ""), {
|
||||
cacheKey: "/api/yggdrasil/sessionserver/session/minecraft/profile/" + userinfo?.uuid,
|
||||
onError: e => {
|
||||
console.warn(e)
|
||||
setErr(String(e))
|
||||
},
|
||||
refreshDeps: [userinfo.data?.uuid],
|
||||
refreshDeps: [userinfo?.uuid],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@ -70,12 +58,10 @@ const Profile = function Profile() {
|
||||
<Card sx={{ gridArea: "a" }}>
|
||||
<CardHeader title="信息" />
|
||||
<CardContent sx={{ display: "grid", gridTemplateColumns: "4em auto" }}>
|
||||
<Typography>uid</Typography>
|
||||
<Typography sx={{ wordBreak: 'break-all' }}>{(userinfo.loading && !userinfo.data) ? <Skeleton /> : userinfo.data?.uid}</Typography>
|
||||
<Typography>name</Typography>
|
||||
<Typography>{(SkinInfo.loading || userinfo.loading) && !SkinInfo.data ? <Skeleton /> : SkinInfo.data?.name}</Typography>
|
||||
<Typography>{userinfo.name}</Typography>
|
||||
<Typography>uuid</Typography>
|
||||
<Typography sx={{ wordBreak: 'break-all' }}>{(userinfo.loading && !userinfo.data) ? <Skeleton /> : userinfo.data?.uuid}</Typography>
|
||||
<Typography sx={{ wordBreak: 'break-all' }}>{userinfo.uuid}</Typography>
|
||||
</CardContent>
|
||||
{/* <CardActions>
|
||||
<Button size="small">更改</Button>
|
||||
|
6
frontend/src/views/profile/Security.tsx
Normal file
6
frontend/src/views/profile/Security.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
export default function Security() {
|
||||
return (<>
|
||||
<p>密码设置</p>
|
||||
</>)
|
||||
}
|
@ -16,45 +16,48 @@ import Box from "@mui/material/Box";
|
||||
import ReactSkinview3d from "react-skinview3d";
|
||||
import { useUnmount } from "ahooks";
|
||||
import { SkinViewer } from "skinview3d";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { LayoutAlertErr, token, user } from "@/store/store";
|
||||
import { upTextures } from "@/apis/apis";
|
||||
import Loading from "@/components/Loading";
|
||||
|
||||
const Textures = function Textures() {
|
||||
const [redioValue, setRedioValue] = useState("skin")
|
||||
useTitle("上传皮肤")
|
||||
const [file, setFile] = useState<File | null>(null)
|
||||
const skin = useRef({
|
||||
skinUrl: "",
|
||||
capeUrl: "",
|
||||
})
|
||||
const skin = useRef("")
|
||||
const skinview3dView = useRef<SkinViewer | null>(null);
|
||||
const setErr = useSetAtom(LayoutAlertErr)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const userinfo = useAtomValue(user)
|
||||
const nowToken = useAtomValue(token)
|
||||
|
||||
useUnmount(() => {
|
||||
skin.current.skinUrl && URL.revokeObjectURL(skin.current.skinUrl)
|
||||
skin.current.capeUrl && URL.revokeObjectURL(skin.current.capeUrl)
|
||||
skin.current && URL.revokeObjectURL(skin.current)
|
||||
skinview3dView.current?.dispose()
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (file) {
|
||||
const nu = URL.createObjectURL(file)
|
||||
skin.current.skinUrl && URL.revokeObjectURL(skin.current.skinUrl)
|
||||
skin.current.capeUrl && URL.revokeObjectURL(skin.current.capeUrl)
|
||||
skin.current && URL.revokeObjectURL(skin.current)
|
||||
skinview3dView.current?.loadSkin(null)
|
||||
skinview3dView.current?.loadCape(null)
|
||||
switch (redioValue) {
|
||||
case "skin":
|
||||
skin.current.skinUrl = nu
|
||||
skin.current = nu
|
||||
skinview3dView.current?.loadSkin(nu, { model: "default" }).then(() =>
|
||||
skinview3dView.current?.loadSkin(nu, { model: "default" })
|
||||
)
|
||||
break
|
||||
case "slim":
|
||||
skin.current.skinUrl = nu
|
||||
skin.current = nu
|
||||
skinview3dView.current?.loadSkin(nu, { model: "slim" }).then(() =>
|
||||
skinview3dView.current?.loadSkin(nu, { model: "slim" })
|
||||
)
|
||||
break
|
||||
case "cape":
|
||||
skin.current.capeUrl = nu
|
||||
skin.current = nu
|
||||
skinview3dView.current?.loadCape(nu).then(() => {
|
||||
skinview3dView.current?.loadCape(nu)
|
||||
})
|
||||
@ -70,6 +73,15 @@ const Textures = function Textures() {
|
||||
setFile(newFile)
|
||||
}
|
||||
|
||||
const handleToUpload = () => {
|
||||
if (!file || loading) return
|
||||
setLoading(true)
|
||||
const textureType = redioValue == "cape" ? "cape" : "skin"
|
||||
const model = redioValue == "slim" ? "slim" : ""
|
||||
upTextures(userinfo.uuid, nowToken, textureType, model, file).catch(e => [setErr(String(e)), console.warn(e)]).
|
||||
finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
useTilg()
|
||||
|
||||
return (<>
|
||||
@ -98,7 +110,7 @@ const Textures = function Textures() {
|
||||
</FormControl>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button variant="contained" sx={{ maxWidth: "3em" }}>上传</Button>
|
||||
<Button variant="contained" sx={{ maxWidth: "3em" }} onClick={handleToUpload}>上传</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
<Card sx={{ gridArea: "b" }}>
|
||||
@ -114,6 +126,7 @@ const Textures = function Textures() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
{loading && <Loading />}
|
||||
</>)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user