Files
frontend/src/views/AccountView.vue
2025-06-18 09:42:12 +03:00

182 lines
5.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="account-container">
<h1>Личный кабинет</h1>
<div v-if="authStore.user" class="user-info">
<p><strong>Имя пользователя:</strong> {{ authStore.user.username }}</p>
<p><strong>Email:</strong> {{ authStore.user.email }}</p>
<p><strong>Роль:</strong> {{ authStore.user.role }}</p>
</div>
<div class="skin-manager">
<h2>Управление скином</h2>
<div class="skin-preview">
<canvas ref="skinCanvas" class="skin-canvas"></canvas>
</div>
<form @submit.prevent="onSkinUpload" class="skin-upload-form">
<label for="skin-file">Загрузить новый скин (PNG, 64x64)</label>
<input id="skin-file" type="file" @change="onFileSelected" accept="image/png" />
<button type="submit" :disabled="!selectedFile || isLoading">
{{ isLoading ? "Загрузка..." : "Загрузить" }}
</button>
<div v-if="uploadError" class="error-message">{{ uploadError }}</div>
<div v-if="uploadSuccess" class="success-message">Скин успешно обновлен!</div>
</form>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from "vue";
import { useAuthStore } from "@/stores/auth";
import { SkinViewer } from "skinview3d";
import apiClient from "@/api/axios";
const authStore = useAuthStore();
// Для 3D-вьювера
const skinCanvas = ref<HTMLCanvasElement | null>(null);
let skinViewer: SkinViewer | null = null;
// Для формы загрузки
const selectedFile = ref<File | null>(null);
const isLoading = ref(false);
const uploadError = ref<string | null>(null);
const uploadSuccess = ref(false);
const onFileSelected = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) {
selectedFile.value = target.files[0];
}
};
const onSkinUpload = async () => {
if (!selectedFile.value) return;
isLoading.value = true;
uploadError.value = null;
uploadSuccess.value = false;
const formData = new FormData();
formData.append("skin", selectedFile.value);
try {
await apiClient.post("/user/skin", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
uploadSuccess.value = true;
// Обновляем скин во вьювере после успешной загрузки
if (skinViewer) {
skinViewer.loadSkin(URL.createObjectURL(selectedFile.value));
}
await authStore.fetchUserProfile();
} catch (e: any) {
uploadError.value = e.response?.data || "Ошибка при загрузке файла.";
} finally {
isLoading.value = false;
}
};
const setupSkinViewer = () => {
if (skinCanvas.value && !skinViewer) {
skinViewer = new SkinViewer({
canvas: skinCanvas.value,
width: 300,
height: 400,
});
}
};
// Следим за изменением URL скина в сторе и обновляем вьювер
watch(
() => authStore.skinUrl,
(newUrl) => {
if (skinViewer) {
if (newUrl) {
skinViewer.loadSkin(newUrl);
} else {
// Если у пользователя нет скина, показываем скин по умолчанию
skinViewer.loadSkin("/default_skin.png");
}
}
},
{ immediate: true },
);
// Инициализация 3D-вьювера при монтировании компонента
onMounted(() => {
setupSkinViewer();
if (!authStore.skinUrl && authStore.isAuthenticated) {
authStore.fetchUserProfile();
}
});
// Очистка ресурсов при размонтировании
onUnmounted(() => {
skinViewer?.dispose();
});
</script>
<style scoped>
.account-container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
.user-info {
background-color: #f9f9f9;
border: 1px solid #eee;
padding: 1rem;
border-radius: 8px;
margin-bottom: 2rem;
}
.skin-manager {
display: flex;
gap: 2rem;
align-items: flex-start;
}
.skin-preview {
flex-shrink: 0;
}
.skin-canvas {
border: 1px solid #ccc;
border-radius: 8px;
}
.skin-upload-form {
flex-grow: 1;
}
.error-message,
.success-message {
margin-top: 1rem;
padding: 0.75rem;
border-radius: 4px;
text-align: center;
}
.error-message {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.success-message {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
input[type="file"] {
margin: 1rem 0;
}
button {
padding: 0.5rem 1rem;
background-color: #42b983;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
}
</style>