mirror of
https://github.com/Dadechin/XRoomDashboardFront.git
synced 2025-07-05 01:34:34 +00:00
added ready player page
This commit is contained in:
parent
74afb1f48d
commit
b30442dfc9
|
@ -99,7 +99,8 @@ export default {
|
|||
},
|
||||
profileImageUrl() {
|
||||
if (!this.customer?.profile_img) return this.defaultProfileImage;
|
||||
return `http://194.62.43.230:8000/media/${this.customer.profile_img}`;
|
||||
return `${this.customer.profile_img}`;
|
||||
// return `http://194.62.43.230:8000/media/${this.customer.profile_img}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
463
xroom-dashboard/src/pages/dashboard/readyPlayer.vue
Normal file
463
xroom-dashboard/src/pages/dashboard/readyPlayer.vue
Normal file
|
@ -0,0 +1,463 @@
|
|||
<template>
|
||||
<SidebarMenu />
|
||||
|
||||
<div class="dashboard-page">
|
||||
<div class="content">
|
||||
<!-- Header -->
|
||||
<div class="header-row">
|
||||
<div class="right-actions">
|
||||
<button class="subscription-button">
|
||||
<img src="https://c.animaapp.com/m9nvumalUMfQbN/img/frame-6.svg" class="button-icon" />
|
||||
خرید اشتراک
|
||||
</button>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<span class="user-name">{{ userData.user.first_name }} {{ userData.user.last_name }}</span>
|
||||
<div class="avatar-box">
|
||||
<img class="avatar-icon" src="https://c.animaapp.com/m9nvumalUMfQbN/img/frame.svg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-title">ساخت آواتار جدید</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="profile-edit-container">
|
||||
<div class="column">
|
||||
<div class="form-section">
|
||||
<h3>آواتار واقعیت مجازی شما</h3>
|
||||
<p class="section-description">
|
||||
میتوانید با استفاده از ابزار Ready Player Me آواتار خود را شخصیسازی کنید.
|
||||
</p>
|
||||
|
||||
<div v-if="avatarUrl" class="avatar-preview">
|
||||
<img :src="getAvatarThumbnail(avatarUrl)" class="avatar-image" />
|
||||
<p class="avatar-url">آواتار شما با موفقیت ایجاد شد</p>
|
||||
</div>
|
||||
<div v-else class="avatar-placeholder">
|
||||
<p>برای ساخت آواتار روی دکمه زیر کلیک کنید</p>
|
||||
</div>
|
||||
|
||||
<button class="save-btn" @click="openAvatarEditor" :disabled="saving">
|
||||
{{ saving ? 'در حال ذخیره...' : avatarUrl ? 'ویرایش آواتار' : 'ساخت آواتار' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="form-section instructions">
|
||||
<h3>راهنمای ساخت آواتار</h3>
|
||||
<ul class="instruction-list">
|
||||
<li>روی دکمه "ساخت آواتار" کلیک کنید</li>
|
||||
<li>در پنجره باز شده، آواتار خود را شخصیسازی کنید</li>
|
||||
<li>پس از اتمام، روی دکمه "Done" کلیک کنید</li>
|
||||
<li>آواتار شما ذخیره شده و در این صفحه نمایش داده میشود</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="avatarUrl" class="form-section">
|
||||
<h3>آواتارهای پیشفرض</h3>
|
||||
<div class="default-avatars">
|
||||
<h4>مردان</h4>
|
||||
<div class="avatar-grid">
|
||||
<div
|
||||
v-for="avatar in maleAvatars"
|
||||
:key="avatar.id"
|
||||
class="avatar-option"
|
||||
@click="selectAvatar(avatar)"
|
||||
>
|
||||
<img :src="getAvatarThumbnail(avatar.src)" class="avatar-thumbnail" />
|
||||
<span>{{ avatar.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>زنان</h4>
|
||||
<div class="avatar-grid">
|
||||
<div
|
||||
v-for="avatar in femaleAvatars"
|
||||
:key="avatar.id"
|
||||
class="avatar-option"
|
||||
@click="selectAvatar(avatar)"
|
||||
>
|
||||
<img :src="getAvatarThumbnail(avatar.src)" class="avatar-thumbnail" />
|
||||
<span>{{ avatar.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden iframe for Ready Player Me -->
|
||||
<iframe
|
||||
id="frame"
|
||||
class="rpm-frame"
|
||||
:src="iframeSrc"
|
||||
allow="camera *; microphone *; clipboard-write"
|
||||
hidden
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SidebarMenu from '@/components/SidebarMenu.vue'
|
||||
import axios from '@/axios';
|
||||
|
||||
export default {
|
||||
name: 'ChangeAvatar',
|
||||
components: {
|
||||
SidebarMenu
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
subdomain: 'xroom',
|
||||
avatarUrl: '',
|
||||
frame: null,
|
||||
saving: false,
|
||||
userData: {
|
||||
user: { first_name: '', last_name: '' }
|
||||
},
|
||||
maleAvatars: [
|
||||
{ id: 1, name: 'مرد ۱', src: 'http://194.62.43.230:8000/media/2025/5/5/men1.glb' },
|
||||
{ id: 2, name: 'مرد ۲', src: 'http://194.62.43.230:8000/media/2025/5/5/men1.glb' },
|
||||
{ id: 7, name: 'مرد ۳', src: 'http://194.62.43.230:8000/media/2025/5/5/men1.glb' },
|
||||
],
|
||||
femaleAvatars: [
|
||||
{ id: 4, name: 'زن ۱', src: 'http://194.62.43.230:8000/media/2025/5/5/men1.glb' },
|
||||
{ id: 10, name: 'زن ۳', src: 'http://194.62.43.230:8000/media/2025/5/5/men1.glb' },
|
||||
],
|
||||
baseUrl: 'http://194.62.43.230:8000'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
iframeSrc() {
|
||||
return `https://${this.subdomain}.readyplayer.me/avatar?frameApi`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.frame = document.getElementById('frame');
|
||||
window.addEventListener('message', this.subscribe);
|
||||
document.addEventListener('message', this.subscribe);
|
||||
|
||||
// Load existing avatar if available
|
||||
this.loadUserData();
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('message', this.subscribe);
|
||||
document.removeEventListener('message', this.subscribe);
|
||||
},
|
||||
methods: {
|
||||
async saveAvatarUrl(glbUrl) {
|
||||
try {
|
||||
const customer = JSON.parse(localStorage.getItem('customer') || '{}');
|
||||
const payload = {
|
||||
profile_glb_url: glbUrl
|
||||
};
|
||||
|
||||
// If profile_img is null, set it to the thumbnail of the GLB
|
||||
if (!customer.profile_img) {
|
||||
payload.profile_img = this.getAvatarThumbnail(glbUrl);
|
||||
}
|
||||
|
||||
const response = await axios.post(`${this.baseUrl}/editProfile/`, payload, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
// Update local storage
|
||||
if (!customer.profile_img) {
|
||||
customer.profile_img = payload.profile_img;
|
||||
}
|
||||
customer.profile_glb_url = glbUrl;
|
||||
localStorage.setItem('customer', JSON.stringify(customer));
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error saving avatar URL:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
getAvatarThumbnail(glbUrl) {
|
||||
// Replace .glb with .png for thumbnail
|
||||
// Adjust this based on your actual thumbnail URLs
|
||||
if (glbUrl.includes('readyplayer.me')) {
|
||||
// For Ready Player Me avatars, use their thumbnail service
|
||||
return glbUrl.replace('.glb', '.png');
|
||||
}
|
||||
return glbUrl.replace('.glb', '.png') || 'https://i.imgur.com/QbXfV6C.png';
|
||||
},
|
||||
loadUserData() {
|
||||
const customer = JSON.parse(localStorage.getItem('customer') || '{}');
|
||||
if (customer.profile_glb_url) {
|
||||
this.avatarUrl = customer.profile_glb_url;
|
||||
}
|
||||
},
|
||||
async subscribe(event) {
|
||||
const json = this.parse(event);
|
||||
|
||||
if (json?.source !== 'readyplayerme') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.eventName === 'v1.frame.ready') {
|
||||
this.frame.contentWindow.postMessage(
|
||||
JSON.stringify({
|
||||
target: 'readyplayerme',
|
||||
type: 'subscribe',
|
||||
eventName: 'v1.**'
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
if (json.eventName === 'v1.avatar.exported') {
|
||||
this.saving = true;
|
||||
try {
|
||||
console.log(`Avatar URL: ${json.data.url}`);
|
||||
this.avatarUrl = json.data.url;
|
||||
this.frame.hidden = true;
|
||||
|
||||
// Save the avatar URL to your backend
|
||||
await this.saveAvatarUrl(json.data.url);
|
||||
|
||||
alert('آواتار با موفقیت ذخیره شد');
|
||||
} catch (error) {
|
||||
console.error('Error saving avatar:', error);
|
||||
alert('خطا در ذخیره آواتار');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
parse(event) {
|
||||
try {
|
||||
return JSON.parse(event.data);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
openAvatarEditor() {
|
||||
this.frame.hidden = false;
|
||||
},
|
||||
|
||||
|
||||
async selectAvatar(avatar) {
|
||||
this.saving = true;
|
||||
try {
|
||||
await this.saveAvatarUrl(avatar.src);
|
||||
this.avatarUrl = avatar.src;
|
||||
alert('آواتار پیشفرض با موفقیت انتخاب شد');
|
||||
} catch (error) {
|
||||
console.error('Error selecting avatar:', error);
|
||||
alert(error.response?.data?.detail || error.message || 'خطا در انتخاب آواتار');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
async fetchUserData() {
|
||||
try {
|
||||
const response = await axios.get('/getInfo');
|
||||
this.userData = response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching user data:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Previous styles remain the same, add these new styles */
|
||||
|
||||
.default-avatars {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.avatar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
margin: 10px 0 20px;
|
||||
}
|
||||
|
||||
.avatar-option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.avatar-option:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.avatar-thumbnail {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* Rest of your existing styles... */
|
||||
.dashboard-page {
|
||||
margin-right: 360px;
|
||||
padding: 20px;
|
||||
direction: rtl;
|
||||
font-family: IRANSansXFaNum, sans-serif;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 20px;
|
||||
padding: 35px 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
margin: 24px 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.profile-edit-container {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.form-section h3 {
|
||||
margin-bottom: 8px;
|
||||
color: #222;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #777;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.avatar-url {
|
||||
color: #4CAF50;
|
||||
font-size: 14px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
background: #3a57e8;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.save-btn:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.instruction-list {
|
||||
padding-right: 20px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.instruction-list li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.rpm-frame {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.avatar-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.subscription-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #3a57e8;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
</style>
|
|
@ -40,6 +40,12 @@ const routes = [
|
|||
component: () => import('@/pages/dashboard/EditProfile.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/dashboard/readyPlayer',
|
||||
name: 'ReadyPlayer',
|
||||
component: () => import('@/pages/dashboard/readyPlayer.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: '/dashboard/ChangeAvatar',
|
||||
name: 'ChangeAvatar',
|
||||
|
|
Loading…
Reference in New Issue
Block a user