- try to do anthore logic of script
This commit is contained in:
Alexandrina-Kuzeleva
2025-11-25 10:26:39 +03:00
parent 684299ac3b
commit ebde8a0171

View File

@@ -1,6 +1,6 @@
<template>
<div style="border: 2px solid red; padding: 10px">
DEBUG: Template rendered
DEBUG: Template rendered
</div>
<div v-if="loading" class="text-center p-10 text-gray-600">
Загружаем таблицу лидеров...
@@ -24,6 +24,7 @@
:src="statusIcon"
/>-->
<p class="status" :class="statusClass">{{ statusLabel }}</p>
<p class="place" v-if="currentUserPosition <= 3">{{ currentUserPosition }}-е место</p>
</template>
<template v-else>
@@ -33,12 +34,19 @@
<p class="points">{{ currentUserPoints }} баллов</p>
<template v-if="currentUserPosition > 1">
<!-- Мотивационный текст -->
<template v-if="currentUserPosition && currentUserPosition > 1">
<p class="motivation-text">
До {{ currentUserPosition - 1 }}-го места нужно:
<strong>{{ pointsToNext }} баллов</strong>
</p>
</template>
<template v-else-if="!currentUserPosition && sorted.length">
<p class="motivation-text">
До лидера нужно:
<strong>{{ sorted[0].points - currentUserPoints }} баллов</strong>
</p>
</template>
</div>
<!-- Лучший участник -->
@@ -47,13 +55,13 @@
<template v-if="topUser">
<div class="initial-circle">
{{ topUser.user[0].toUpperCase() }}
{{ topUserInitial }}
</div>
<p class="username">
<a
class="user-link"
:href="`/lms/user/${topUser.username}`"
:href="userProfileLink(topUser.user)"
>
{{ topUser.full_name }}
</a>
@@ -79,10 +87,10 @@
<thead>
<tr>
<th>#</th>
<th>Пользователь</th>
<th style="min-width: 150px;">Пользователь</th>
<th>Баллы активности</th>
<th>Общая сумма</th>
<th>Бонус (МПГУ)</th>
<th>Общая сумма баллов</th>
<th>Бонусные баллы (МПГУ)</th>
</tr>
</thead>
@@ -97,11 +105,15 @@
<td>
<div class="flex items-center">
<div class="initial-circle-small">
{{ item.user[0].toUpperCase() }}
{{ getUserInitial(item.user) }}
</div>
<div>
<a :href="`/lms/user/${item.username}`"
class="user-link">{{ item.full_name }}</a>
<a
:href="userProfileLink(item.user)"
class="user-link"
>
{{ item.full_name }}
</a>
<!-- значки
<img v-if="index === 0" class="cup-badge" src="/files/gold-cup.png">
@@ -119,7 +131,7 @@
</table>
<p class="stats-note">
* Бонус 1 балл за каждые 100 очков (максимум 10)
* Бонусные баллы рассчитываются как 1 балл за каждые 100 очков активности (максимум 10)
</p>
</div>
</section>
@@ -135,117 +147,117 @@ const $user = inject("$user");
const currentUserEmail = $user.data.email;
const loading = ref(true);
const sorted = ref([]);
// Загружаем все Energy Point Log
const logs = createResource({
const logsResource = createResource({
url: "frappe.client.get_list",
params: {
doctype: "Energy Point Log",
fields: ["name", "user", "points"],
limit_page_length: 2000
fields: ["user", "points"],
limit_page_length: 10000 // увеличим лимит для больших данных
},
auto: false,
onSuccess() {
});
// Загружаем данные пользователей
const usersResource = createResource({
url: "frappe.client.get_list",
params: {
doctype: "User",
fields: ["name", "full_name", "username"],
filters: [["enabled", "=", 1]], // только активные пользователи
limit_page_length: 1000
},
auto: false,
});
// Основная функция загрузки данных
async function loadData() {
try {
loading.value = true;
// Параллельно загружаем логи и пользователей
const [logsData, usersData] = await Promise.all([
logsResource.fetch(),
usersResource.fetch()
]);
// Создаем карту пользователей для быстрого доступа
const usersMap = {};
usersData.forEach(user => {
usersMap[user.name] = {
full_name: user.full_name || user.name,
username: user.username
};
});
// Суммируем баллы по пользователям
const userPoints = {};
logsData.forEach(log => {
if (!userPoints[log.user]) userPoints[log.user] = 0;
userPoints[log.user] += log.points;
});
// Создаем сортированный массив
const leaderboard = Object.entries(userPoints)
.map(([user, points]) => ({
user,
points,
full_name: usersMap[user]?.full_name || user,
username: usersMap[user]?.username || user,
bonus: Math.min(Math.floor(points / 100), 10)
}))
.sort((a, b) => b.points - a.points);
sorted.value = leaderboard;
} catch (error) {
console.error("Ошибка загрузки данных:", error);
} finally {
loading.value = false;
}
});
// Собираем баллы по пользователям
const userPoints = computed(() => {
const map = {};
if (!logs.data) return map;
logs.data.forEach(entry => {
if (!map[entry.user]) map[entry.user] = 0;
map[entry.user] += entry.points;
});
return map;
});
// Загружаем данные пользователей (имя + username)
const userCache = ref({});
async function loadUserProfile(email) {
if (userCache.value[email]) return userCache.value[email];
const profile = createResource({
url: 'frappe.client.get',
params: {
doctype: 'User',
name: email
},
auto: true
})
userCache.value[email] = {
full_name: profile.message.full_name,
username: profile.message.username
};
return userCache.value[email];
}
// Готовим сортированный массив
const sorted = ref([]);
async function buildSorted() {
const arr = [];
for (const [user, points] of Object.entries(userPoints.value)) {
const data = await loadUserProfile(user);
arr.push({
user,
points,
full_name: data.full_name,
username: data.username,
bonus: Math.min(Math.floor(points / 100), 10)
});
}
arr.sort((a, b) => b.points - a.points);
sorted.value = arr;
}
// Текущий пользователь
const currentUserPoints = computed(
() => userPoints.value[currentUserEmail] || 0
);
// Computed свойства
const currentUserPoints = computed(() => {
const user = sorted.value.find(u => u.user === currentUserEmail);
return user ? user.points : 0;
});
const currentUserPosition = computed(() => {
const index = sorted.value.findIndex(u => u.user === currentUserEmail);
return index === -1 ? null : index + 1;
return index >= 0 ? index + 1 : null;
});
const topUser = computed(() => sorted.value[0]);
const pointsToNext = computed(() => {
if (!currentUserPosition.value || currentUserPosition.value <= 1) return 0;
const next = sorted.value[currentUserPosition.value - 2];
return next.points - currentUserPoints.value;
const nextUser = sorted.value[currentUserPosition.value - 2];
return nextUser.points - currentUserPoints.value;
});
// Топ-1 участник
const topUser = computed(() => sorted.value[0]);
// UI статусы
const statusLabel = computed(() => {
if (!currentUserPosition.value) return "Не в рейтинге";
if (!currentUserPosition.value) return "Стремитесь выше";
if (currentUserPosition.value === 1) return "Чемпион";
if (currentUserPosition.value === 2) return "Второе место";
if (currentUserPosition.value === 3) return "Третье место";
if (currentUserPosition.value <= 5) return "В топ-5";
if (currentUserPosition.value <= 10) return "В топ-10";
return "Стремитесь выше";
switch (currentUserPosition.value) {
case 1: return "Чемпион";
case 2: return "Второе место";
case 3: return "Третье место";
default:
if (currentUserPosition.value <= 5) return "В топ-5";
if (currentUserPosition.value <= 10) return "В топ-10";
return "Стремитесь выше";
}
});
const statusIcon = computed(() => {
if (currentUserPosition.value === 1) return "/files/gold-cup.png";
if (currentUserPosition.value === 2) return "/files/silver-cup.png";
if (currentUserPosition.value === 3) return "/files/bronze-cup.png";
if (currentUserPosition.value && currentUserPosition.value <= 10) return "/files/star.svg";
return "/files/dart-board.svg";
});
@@ -256,14 +268,27 @@ const statusClass = computed(() => {
return "";
});
// Загружаем
onMounted(async () => {
await logs.fetch();
await buildSorted();
console.log("onMounted");
const topUserInitial = computed(() => {
return topUser.value ? topUser.value.user[0].toUpperCase() : 'N/A';
});
// Вспомогательные функции
function getUserInitial(email) {
return email ? email[0].toUpperCase() : '?';
}
function userProfileLink(userEmail) {
const user = sorted.value.find(u => u.user === userEmail);
return user ? `/lms/user/${user.username}` : '#';
}
// Загружаем данные при монтировании
onMounted(() => {
loadData();
});
</script>
<style>
/* сюда полностью переносите ваш CSS — он совместим */
/* Ваш полный CSS стиль здесь */
/* Общие стили */
</style>