Fix Ranking code step1
This commit is contained in:
@ -10,9 +10,40 @@
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-gray-50">
|
||||
<div class="container mx-auto p-4">
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<!-- ログインフォーム -->
|
||||
<div id="loginForm" class="fixed inset-0 bg-gray-800 bg-opacity-50 flex items-center justify-center">
|
||||
<div class="bg-white p-8 rounded-lg shadow-lg w-96">
|
||||
<h2 class="text-2xl font-bold mb-6 text-center">ログイン</h2>
|
||||
<form onsubmit="return login(event)">
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="username">
|
||||
ユーザー名
|
||||
</label>
|
||||
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
id="username" type="text" placeholder="ユーザー名">
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="password">
|
||||
パスワード
|
||||
</label>
|
||||
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline"
|
||||
id="password" type="password" placeholder="パスワード">
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||
type="submit">
|
||||
ログイン
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mainContent" class="container mx-auto p-4">
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 mb-6" style="display: none;">
|
||||
<h1 class="text-2xl font-bold mb-6">通過審査管理画面</h1>
|
||||
<button onclick="logout()" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700">
|
||||
ログアウト
|
||||
</button>
|
||||
|
||||
<!-- 選択フォーム -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
||||
@ -125,8 +156,62 @@
|
||||
let original_goal_time = '';
|
||||
let selected_event_code = '';
|
||||
|
||||
// ユーザー認証用の定数(実際の運用ではサーバーサイドで管理すべき)
|
||||
const VALID_USERNAME = 'admin';
|
||||
const VALID_PASSWORD = 'password123';
|
||||
|
||||
// セッション管理
|
||||
function checkAuth() {
|
||||
const isAuthenticated = sessionStorage.getItem('isAuthenticated');
|
||||
if (isAuthenticated) {
|
||||
document.getElementById('loginForm').style.display = 'none';
|
||||
document.getElementById('mainContent').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('loginForm').style.display = 'flex';
|
||||
document.getElementById('mainContent').style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// ログイン処理
|
||||
async function login(event) {
|
||||
event.preventDefault();
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
const response = await fetch('/api/login/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: username, // メールアドレス
|
||||
password: password
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'ログインに失敗しました');
|
||||
}
|
||||
|
||||
// SuperVisor User のみを許可する。
|
||||
console.info('Login successful:', data);
|
||||
sessionStorage.setItem('isAuthenticated', 'true');
|
||||
checkAuth();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ログアウト処理
|
||||
function logout() {
|
||||
sessionStorage.removeItem('isAuthenticated');
|
||||
checkAuth();
|
||||
}
|
||||
|
||||
// イベントリスナーの設定
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkAuth();
|
||||
|
||||
// Sortable初期化これで、通過順序を変更できる
|
||||
const checkinList = document.getElementById('checkinList');
|
||||
new Sortable(checkinList, {
|
||||
|
||||
295
supervisor/html/ranking.html
Normal file
295
supervisor/html/ranking.html
Normal file
@ -0,0 +1,295 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ランキング</title>
|
||||
<style>
|
||||
.box2 { margin: 10px; padding: 10px; border: 1px solid #ccc; }
|
||||
.best3 { margin: 5px 0; padding: 5px; }
|
||||
.span2 { margin-left: 20px; }
|
||||
.span3 { font-weight: bold; }
|
||||
.span6 { display: inline-block; width: 30px; }
|
||||
.black { background-color: #f0f0f0; padding: 10px; }
|
||||
.arrow { margin: 10px 0; }
|
||||
.arrow2 { margin: 10px 0; }
|
||||
.disqualified { color: #999; }
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.status-retired {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
.status-finished {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
.status-running {
|
||||
background-color: #e3f2fd;
|
||||
color: #1565c0;
|
||||
}
|
||||
.disqualified-header {
|
||||
margin-top: 20px;
|
||||
padding: 5px;
|
||||
background-color: #f0f0f0;
|
||||
border-left: 4px solid #999;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ranking">
|
||||
<div class="black">
|
||||
<span class="span3"></span>
|
||||
<span style="font-size: 24px">ランキング</span>
|
||||
</div>
|
||||
|
||||
<div class="arrow">
|
||||
<select id="eventSelect">
|
||||
<option value="">イベントを選択してください</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="arrow2">
|
||||
<select id="classSelect" disabled>
|
||||
<option value="">クラスを選択してください</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="toggleButton" onclick="toggleView()">TOP3表示</button>
|
||||
|
||||
<!-- 通常のランキング表示 -->
|
||||
<div id="normalRanking">
|
||||
<div id="teamList"></div>
|
||||
</div>
|
||||
|
||||
<!-- TOP3のランキング表示 -->
|
||||
<div id="top3Ranking" style="display: none;">
|
||||
<div id="top3List"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let showTop3 = false;
|
||||
|
||||
// ページ読み込み時にイベント一覧を取得
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await loadEvents();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
// 日時のフォーマット
|
||||
function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '未ゴール';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('ja-JP', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// イベントリスナーの設定
|
||||
function setupEventListeners() {
|
||||
document.getElementById('eventSelect').addEventListener('change', async function() {
|
||||
const eventCode = this.value;
|
||||
if (eventCode) {
|
||||
await loadClasses(eventCode);
|
||||
await updateRankings();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('classSelect').addEventListener('change', async function() {
|
||||
await updateRankings();
|
||||
});
|
||||
}
|
||||
|
||||
// イベント一覧の取得と表示
|
||||
async function loadEvents() {
|
||||
try {
|
||||
const response = await fetch('/api/newevent2/');
|
||||
const events = await response.json();
|
||||
const select = document.getElementById('eventSelect');
|
||||
|
||||
events.filter(event => event.public).forEach(event => {
|
||||
const option = document.createElement('option');
|
||||
option.value = event.event_name;
|
||||
option.textContent = event.event_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('イベント一覧の取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// クラス一覧の取得と表示
|
||||
async function loadClasses(eventCode) {
|
||||
try {
|
||||
const response = await fetch(`/api/categories/${eventCode}/`);
|
||||
const classes = await response.json();
|
||||
const select = document.getElementById('classSelect');
|
||||
|
||||
// 既存のオプションをクリア
|
||||
select.innerHTML = '<option value="">クラスを選択してください</option>';
|
||||
select.disabled = false;
|
||||
|
||||
classes.forEach(cls => {
|
||||
const option = document.createElement('option');
|
||||
option.value = cls.category_name;
|
||||
option.textContent = cls.category_name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('クラス一覧の取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ランキングの更新
|
||||
async function updateRankings() {
|
||||
const eventCode = document.getElementById('eventSelect').value;
|
||||
if (!eventCode) return;
|
||||
|
||||
try {
|
||||
if (showTop3) {
|
||||
await loadTop3Rankings(eventCode);
|
||||
} else {
|
||||
const classCode = document.getElementById('classSelect').value;
|
||||
if (classCode) {
|
||||
await loadClassRankings(eventCode, classCode);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('ランキングの取得に失敗:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// クラス別ランキングの表示
|
||||
async function loadClassRankings(eventCode, classCode) {
|
||||
const response = await fetch(`/api/rankings/${eventCode}/${classCode}/`);
|
||||
const rankingData = await response.json();
|
||||
displayNormalRankings(rankingData);
|
||||
}
|
||||
|
||||
// TOP3ランキングの表示
|
||||
async function loadTop3Rankings(eventCode) {
|
||||
const response = await fetch(`/api/rankings/top3/${eventCode}/`);
|
||||
const rankingData = await response.json();
|
||||
displayTop3Rankings(rankingData);
|
||||
}
|
||||
|
||||
// 通常ランキングの表示処理
|
||||
function displayNormalRankings(rankingData) {
|
||||
const container = document.getElementById('teamList');
|
||||
container.innerHTML = '';
|
||||
|
||||
// 有効なランキングの表示
|
||||
rankingData.rankings.forEach((team, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'best';
|
||||
const statusClass = team.status === '棄権' ? 'status-retired' :
|
||||
team.status === 'ゴール' ? 'status-finished' : 'status-running';
|
||||
div.innerHTML = `
|
||||
${index + 1}. ${team.team_name} (${team.zekken_number})
|
||||
<span class="span2">合計得点:${team.final_point}</span>
|
||||
<span class="span2">獲得ポイント:${team.point}</span>
|
||||
<span class="span2">遅刻減点: ${team.late_point}</span>
|
||||
<span class="span2">最終更新: ${formatDateTime(team.last_checkin)}</span>
|
||||
<span class="status ${statusClass}">(${team.status})</span>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
// 失格チームの表示(見出し)
|
||||
if (rankingData.disqualified && rankingData.disqualified.length > 0) {
|
||||
const disqHeader = document.createElement('div');
|
||||
disqHeader.className = 'disqualified-header';
|
||||
disqHeader.innerHTML = '<h3>失格チーム</h3>';
|
||||
container.appendChild(disqHeader);
|
||||
|
||||
// 失格チームのリスト
|
||||
rankingData.disqualified.forEach(team => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'best disqualified';
|
||||
div.innerHTML = `
|
||||
${team.team_name} (${team.zekken_number})
|
||||
<span class="span2">獲得ポイント:${team.point}</span>
|
||||
<span class="span2">理由: ${team.reason}</span>
|
||||
<span class="span2">ゴール: ${formatDateTime(team.goal_time)}</span>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TOP3ランキングの表示処理
|
||||
function displayTop3Rankings(rankingData) {
|
||||
const container = document.getElementById('top3List');
|
||||
container.innerHTML = '';
|
||||
|
||||
Object.entries(rankingData).forEach(([category, data]) => {
|
||||
const categoryDiv = document.createElement('div');
|
||||
categoryDiv.className = 'box2';
|
||||
|
||||
const categoryHeader = document.createElement('h3');
|
||||
categoryHeader.textContent = category;
|
||||
categoryDiv.appendChild(categoryHeader);
|
||||
|
||||
// 有効なランキングの表示
|
||||
data.rankings.forEach((team, index) => {
|
||||
const teamDiv = document.createElement('div');
|
||||
teamDiv.className = 'best3';
|
||||
const statusClass = team.status === '棄権' ? 'status-retired' :
|
||||
team.status === 'ゴール' ? 'status-finished' : 'status-running';
|
||||
teamDiv.innerHTML = `
|
||||
<span class="span6">${index + 1}</span>
|
||||
${team.team_name} (${team.zekken_number}) <span class="status ${statusClass}">(${team.status})</span><br>
|
||||
合計得点:${team.final_point} (獲得:${team.point} 減点:${team.late_point})<br>
|
||||
最終更新: ${formatDateTime(team.last_checkin)}
|
||||
`;
|
||||
categoryDiv.appendChild(teamDiv);
|
||||
});
|
||||
|
||||
// 失格チームの表示
|
||||
if (data.disqualified && data.disqualified.length > 0) {
|
||||
const disqHeader = document.createElement('div');
|
||||
disqHeader.className = 'disqualified-header';
|
||||
disqHeader.innerHTML = '<h4>失格チーム</h4>';
|
||||
categoryDiv.appendChild(disqHeader);
|
||||
|
||||
data.disqualified.forEach(team => {
|
||||
const teamDiv = document.createElement('div');
|
||||
teamDiv.className = 'best3 disqualified';
|
||||
teamDiv.innerHTML = `
|
||||
${team.team_name} (${team.zekken_number})<br>
|
||||
獲得ポイント:${team.point} 理由:${team.reason}<br>
|
||||
ゴール: ${formatDateTime(team.goal_time)}
|
||||
`;
|
||||
categoryDiv.appendChild(teamDiv);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(categoryDiv);
|
||||
});
|
||||
}
|
||||
|
||||
// 表示モードの切り替え
|
||||
function toggleView() {
|
||||
showTop3 = !showTop3;
|
||||
const button = document.getElementById('toggleButton');
|
||||
const normalRanking = document.getElementById('normalRanking');
|
||||
const top3Ranking = document.getElementById('top3Ranking');
|
||||
const classSelect = document.getElementById('classSelect');
|
||||
|
||||
button.textContent = showTop3 ? 'クラス別ランキング' : 'TOP3表示';
|
||||
normalRanking.style.display = showTop3 ? 'none' : 'block';
|
||||
top3Ranking.style.display = showTop3 ? 'block' : 'none';
|
||||
classSelect.disabled = showTop3;
|
||||
|
||||
updateRankings();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
486
supervisor/html/ranking_bck.html
Normal file
486
supervisor/html/ranking_bck.html
Normal file
@ -0,0 +1,486 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>ランキング</title>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
.flex-slave {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#map {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
|
||||
#best{
|
||||
position: relative;
|
||||
line-height: 1.5em;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 10px 25px 0 rgba(0, 0, 0, .5);
|
||||
margin: 0 auto;
|
||||
margin-top: 20px;
|
||||
border-radius:8px;
|
||||
counter-increment: count-ex1-5;
|
||||
content: counter(number);
|
||||
padding: 0px 10px 10px 30px;
|
||||
margin:0px 0px 0px 5px;
|
||||
}
|
||||
#best::marker{
|
||||
font-weight:bold;
|
||||
color: #ff0801;
|
||||
}
|
||||
|
||||
#best li{
|
||||
counter-increment: count-ex1-5;
|
||||
content: counter(number);
|
||||
padding: 8px 10px 0px 0px;
|
||||
margin:8px 0px 0px 8px;
|
||||
line-height: 2em;
|
||||
}
|
||||
li::marker{
|
||||
font-weight:bold;
|
||||
color: #ff0801;
|
||||
line-height: 1;
|
||||
|
||||
}
|
||||
|
||||
#best::before {
|
||||
|
||||
position: absolute;
|
||||
background: #ff0801;
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
border-radius: 50%;
|
||||
left: 0;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
content: "\f521";
|
||||
font-weight: 900;
|
||||
margin:6px 0px 0px 10px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.black{
|
||||
background-color: #000;
|
||||
}
|
||||
h5 {
|
||||
position: relative;
|
||||
padding: 0px 0px 0px 70px;
|
||||
|
||||
|
||||
background-image: linear-gradient(0deg, #b8751e 0%, #ffce08 37%, #fefeb2 47%, #fafad6 50%, #fefeb2 53%, #e1ce08 63%, #b8751e 100%);
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
display:flex;
|
||||
align-items: center;
|
||||
height:60px;
|
||||
}
|
||||
|
||||
h5 .span3 {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 0px;
|
||||
display: inline-block;
|
||||
width: 52px;
|
||||
height: px;
|
||||
text-align: center;
|
||||
background: #fa4141;
|
||||
}
|
||||
|
||||
h5 .span3:before,
|
||||
h5 .span3:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
|
||||
}
|
||||
|
||||
h5 .span3:before {
|
||||
right: -10px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid #d90606;
|
||||
}
|
||||
|
||||
h5 .span3:after {
|
||||
top: 50%;
|
||||
left: 0;
|
||||
display: block;
|
||||
height: %;
|
||||
border: 26px solid #fa4141;
|
||||
border-bottom-width: 15px;
|
||||
border-bottom-color: transparent;
|
||||
|
||||
}
|
||||
|
||||
h5 .span3 i {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: #fff100;
|
||||
padding-top: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.best
|
||||
select.name{
|
||||
display: block;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.best2 li{
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 1.5rem 2rem 1.5rem 130px;
|
||||
word-break: break-all;
|
||||
border-top: 3px solid #000;
|
||||
border-radius: 12px 0 0 0;
|
||||
margin: 10px 0px 0px 0px;
|
||||
}
|
||||
.best2 span{
|
||||
font-size: 40px;
|
||||
font-size: 4rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
color: #fff;
|
||||
border-radius: 10px 0 20px 10px;
|
||||
background: #000;
|
||||
}
|
||||
.button3{
|
||||
color: #fff;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 0;
|
||||
background-image: -webkit-linear-gradient(left, #fa709a 0%, #fee140 100%);
|
||||
background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%);
|
||||
-webkit-box-shadow: 0 5px 5px rgba(0, 0, 0, .1);
|
||||
box-shadow: 0 3px 5px rgba(0, 0, 0, .1);
|
||||
border-radius: 100vh;
|
||||
font-family: "Arial", "メイリオ";
|
||||
/*letter-spacing: 0.1em;*/
|
||||
padding: 7px 25px 7px 25px;
|
||||
|
||||
}
|
||||
|
||||
.button3:hover{
|
||||
-webkit-transform: translate(0, -2px);
|
||||
transform: translate(0, -2px);
|
||||
color: #fff;
|
||||
-webkit-box-shadow: 0 8px 15px rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 8px 15px rgba(0, 0, 0, .2);
|
||||
font-family: "Arial", "メイリオ";
|
||||
|
||||
|
||||
}
|
||||
|
||||
#best::before {
|
||||
|
||||
position: absolute;
|
||||
background: #ff0801;
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
border-radius: 50%;
|
||||
left: 0;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
content: "\f521";
|
||||
font-weight: 900;
|
||||
margin:6px 0px 0px 10px;
|
||||
|
||||
}
|
||||
|
||||
select {
|
||||
display: block;
|
||||
border-left: solid 10px #27acd9;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-color: transparent transparent transparent blue;
|
||||
font-weight: bold;
|
||||
box-shadow: 3px 5px 3px -2px #aaaaaa,3px 3px 2px 0px #ffffff inset;
|
||||
margin-bottom: 10px;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.box2{
|
||||
position: relative;
|
||||
line-height: 1.5em;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 10px 25px 0 rgba(0, 0, 0, .5);
|
||||
margin: 0 auto;
|
||||
margin-top: 20px;
|
||||
border-radius:8px;
|
||||
counter-increment: count-ex1-5;
|
||||
content: counter(number);
|
||||
padding: 10px 10px 10px 30px;
|
||||
margin:10px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.best2::before {
|
||||
|
||||
position: absolute;
|
||||
background: #ff0801;
|
||||
color: #FFF;
|
||||
font-size: 15px;
|
||||
border-radius: 50%;
|
||||
left: 0;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
content: "\f521";
|
||||
font-weight: 900;
|
||||
margin: 10px 0px 0px 10px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
h3{
|
||||
margin: 3px 0px 0px 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
|
||||
#best{
|
||||
position: relative;
|
||||
line-height: 1.5em;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 10px 25px 0 rgba(0, 0, 0, .5);
|
||||
margin: 0 auto;
|
||||
margin-top: 20px;
|
||||
border-radius:8px;
|
||||
counter-increment: count-ex1-5;
|
||||
content: counter(number);
|
||||
padding: 0px 10px 10px 30px;
|
||||
margin:0px 0px 0px 5px;
|
||||
}
|
||||
#best::marker{
|
||||
font-weight:bold;
|
||||
color: #ff0801;
|
||||
}
|
||||
|
||||
#best li{
|
||||
counter-increment: count-ex1-5;
|
||||
content: counter(number);
|
||||
padding: 8px 10px 0px 0px;
|
||||
margin:8px 0px 0px 8px;
|
||||
line-height: 2em;
|
||||
margin: 10px 0px 0px 0px;
|
||||
}
|
||||
li::marker{
|
||||
font-weight:bold;
|
||||
color: #ff0801;
|
||||
line-height: 1;
|
||||
|
||||
}
|
||||
select{
|
||||
width:100%;
|
||||
|
||||
}
|
||||
.button3{
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
select.name{
|
||||
margin-bottom: -20px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.arrow{
|
||||
position: relative;
|
||||
}
|
||||
.arrow::after {
|
||||
color: #828282;
|
||||
position: absolute;
|
||||
top:18px; /* 矢印の位置 */
|
||||
right: 25px; /* 矢印の位置 */
|
||||
width: 13px; /* 矢印の大きさ */
|
||||
height: 13px; /* 矢印の大きさ */
|
||||
border-top: 3px solid #58504A; /* 矢印の線 */
|
||||
border-right: 3px solid #58504A; /* 矢印の線 */
|
||||
-webkit-transform: rotate(135deg); /* 矢印の傾き */
|
||||
transform: rotate(135deg); /* 矢印の傾き */
|
||||
pointer-events: none; /* 矢印部分もクリック可能にする */
|
||||
content: "";
|
||||
border-color: #828282;
|
||||
}
|
||||
.arrow2{
|
||||
position: relative;
|
||||
}
|
||||
.arrow2::after {
|
||||
color: #828282;
|
||||
position: absolute;
|
||||
top:20px; /* 矢印の位置 */
|
||||
right: 25px; /* 矢印の位置 */
|
||||
width: 13px; /* 矢印の大きさ */
|
||||
height: 13px; /* 矢印の大きさ */
|
||||
border-top: 3px solid #58504A; /* 矢印の線 */
|
||||
border-right: 3px solid #58504A; /* 矢印の線 */
|
||||
-webkit-transform: rotate(135deg); /* 矢印の傾き */
|
||||
transform: rotate(135deg); /* 矢印の傾き */
|
||||
pointer-events: none; /* 矢印部分もクリック可能にする */
|
||||
content: "";
|
||||
border-color: #828282;
|
||||
}
|
||||
|
||||
.score {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ranking">
|
||||
<div class="black">
|
||||
<h5><span class="span3"><i class="fas fa-crown"></i></span>
|
||||
<span style="font-size: 24px">ランキング</span></h5>
|
||||
</div>
|
||||
<form @submit.prevent="ranking_view">
|
||||
<div class="arrow">
|
||||
<select class="name" v-model="selectedEvent">
|
||||
<option disabled value="">イベント一覧</option>
|
||||
<option selected value="FC岐阜">with FC岐阜</option>
|
||||
<!--
|
||||
<option value="関ケ原2410">関ケ原-2024年10月</option>
|
||||
<option value="養老2410">養老-2024年10月</option>
|
||||
<option value="大垣2410">大垣-2024年10月</option>
|
||||
<option value="各務原2410">各務原-2024年10月</option>
|
||||
<option value="多治見2410">多治見-2024年10月</option>
|
||||
<option value="美濃加茂2410">美濃加茂-2024年10月</option>
|
||||
<option value="下呂2410">下呂-2024年10月</option>
|
||||
<option value="郡上2410">郡上-2024年10月</option>
|
||||
<option value="高山2410">高山-2024年10月</option>
|
||||
|
||||
<option value="関ケ原2409">関ケ原-2024年9月</option>
|
||||
|
||||
<option value="養老2409">養老-2024年9月</option>
|
||||
<option value="大垣2409">大垣-2024年9月</option>
|
||||
<option value="各務原2409">各務原-2024年9月</option>
|
||||
<option value="多治見2409">多治見-2024年9月</option>
|
||||
<option value="美濃加茂2409">美濃加茂-2024年9月</option>
|
||||
<option value="下呂2409">下呂-2024年9月</option>
|
||||
<option value="郡上2409">郡上-2024年9月</option>
|
||||
<option value="高山2409">高山-2024年9月</option>
|
||||
|
||||
-->
|
||||
<option value="美濃加茂">岐阜ロゲin美濃加茂</option>
|
||||
<option value="養老ロゲ">養老町</option>
|
||||
<option value="岐阜市">岐阜市</option>
|
||||
<option value="大垣2">岐阜ロゲin大垣@イオンモール大垣</option>
|
||||
<option value="大垣">岐阜ロゲin大垣</option>
|
||||
<option value="多治見">岐阜ロゲin多治見</option>
|
||||
<option value="各務原">岐阜ロゲin各務原</option>
|
||||
<option value="下呂">岐阜ロゲin下呂温泉</option>
|
||||
<option value="郡上">岐阜ロゲin郡上</option>
|
||||
<option value="高山">岐阜ロゲin高山</option>
|
||||
</select>
|
||||
<div class="arrow2">
|
||||
<select v-model="selectedClass">
|
||||
<option selected value="top3">top3</option>
|
||||
<option value="3時間一般">3時間一般</option>
|
||||
<option value="3時間ファミリー">3時間ファミリー</option>
|
||||
<option value="3時間自転車">3時間自転車</option>
|
||||
<option value="3時間ソロ男子">3時間ソロ男子</option>
|
||||
<option value="3時間ソロ女子">3時間ソロ女子</option>
|
||||
<option value="3時間パラロゲ">3時間パラロゲ</option>
|
||||
<option value="5時間一般">5時間一般</option>
|
||||
<option value="5時間ファミリー">5時間ファミリー</option>
|
||||
<option value="5時間自転車">5時間自転車</option>
|
||||
<option value="5時間ソロ男子">5時間ソロ男子</option>
|
||||
<option value="5時間ソロ女子">5時間ソロ女子</option>
|
||||
</div>
|
||||
</select>
|
||||
<button class="button3" type="submit">CLICK</button>
|
||||
</div>
|
||||
</form>
|
||||
<ol v-if="top_three_flag == false" >
|
||||
<div id="best" v-for="team in team_list">
|
||||
<li>
|
||||
{{ team.team_name }}({{ team.zekken_number }})<br/><p class="score"><span class="span2">合計得点:{{ team.point }}</span> <span class="span2">内減点: {{ team.late_point }}</span></p>
|
||||
</li>
|
||||
</div>
|
||||
</ol>
|
||||
<div v-if="top_three_flag == true">
|
||||
<div class="box2" v-for="(teams, index) in team_list">
|
||||
<h3> {{ index }} </h3>
|
||||
<ol>
|
||||
<div class="best3" v-for="team in teams">
|
||||
<span class="span6"><li class="best2"></span>
|
||||
{{ team.team_name }}({{ team.zekken_number }})<br/><p class="score">合計得点:{{ team.point }} 内減点: {{ team.late_point }}</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.1/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
|
||||
<script>
|
||||
axios.defaults.baseURL = 'https://rogaining.sumasen.net/gifuroge';
|
||||
axios.default.withCridentials = true;
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#ranking',
|
||||
data: {
|
||||
selectedEvent: "FC岐阜",
|
||||
selectedClass: "top3",
|
||||
team_list: [],
|
||||
top_three_flag: false,
|
||||
three_pop: [],
|
||||
interval: 1
|
||||
},
|
||||
mounted : function(){
|
||||
|
||||
var int = this.interval * 60 * 1000
|
||||
setInterval(function() {this.ranking_view()}.bind(this), int);
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
ranking_view: function(){
|
||||
if (this.selectedClass == 'top3'){
|
||||
this.top_three_flag = true
|
||||
var url = "/all_ranking_top3?event=" + this.selectedEvent
|
||||
axios
|
||||
.get(url)
|
||||
.then(response => ( this.team_list = response.data))}
|
||||
else {
|
||||
this.top_three_flag = false
|
||||
var url = "/get_ranking?class=" + this.selectedClass + '&event=' + this.selectedEvent
|
||||
|
||||
axios
|
||||
.get(url)
|
||||
.then(response => ( this.team_list = response.data ))}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
1298
supervisor/html/realtime_monitor.html
Normal file
1298
supervisor/html/realtime_monitor.html
Normal file
File diff suppressed because it is too large
Load Diff
1298
supervisor/html/realtime_monitor_bck.html
Normal file
1298
supervisor/html/realtime_monitor_bck.html
Normal file
File diff suppressed because it is too large
Load Diff
293
supervisor/html/view_photo_list2.html
Normal file
293
supervisor/html/view_photo_list2.html
Normal file
@ -0,0 +1,293 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>岐阜ロゲwith FC岐阜 Myアルバム|岐阜aiネットワーク</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<link rel="stylesheet" href="./css/reset.css">
|
||||
<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
|
||||
<script src="https://kit.fontawesome.com/94e0c17dd1.js" crossorigin="anonymous"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet">
|
||||
<!-- drawer.css -->
|
||||
<link rel="stylesheet" href="./css/drawer.min.css">
|
||||
|
||||
<!-- Meta -->
|
||||
<meta name="description" content="岐阜ロゲin岐阜市のMyアルバムページです。">
|
||||
<meta name="keywords" content="FC岐阜ロゲイニング map 岐阜aiネットワーク ran">
|
||||
<meta name="robot" content="index,follow,noarchive">
|
||||
<meta name="author" content="岐阜aiネットワーク">
|
||||
<meta name="language" content="ja">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<style>
|
||||
/* ここにCSSスタイルを記述 */
|
||||
.view {
|
||||
max-width: 1000px;
|
||||
margin: 50px auto 20px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
section.view input,
|
||||
button,
|
||||
select {
|
||||
font-size: 16px;
|
||||
}
|
||||
select,input{
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
div#photoList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-width: 1200px;
|
||||
/* margin: 0 10px; */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.event-photo {
|
||||
width: 100%;
|
||||
max-width: calc(32.33% - 2px);
|
||||
margin: 5px;
|
||||
/* その他のスタイル */
|
||||
}
|
||||
.viewtop{
|
||||
min-height: calc(50vh - 50px);
|
||||
}
|
||||
@media screen and (max-width:768px) {
|
||||
.event-photo {
|
||||
width: 100%;
|
||||
max-width: calc(31% - 2px);
|
||||
margin: 5px;
|
||||
/* その他のスタイル */
|
||||
}
|
||||
.viewtop{
|
||||
min-height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div id="headerContent">
|
||||
<div id="sp_headerContent">
|
||||
<h1><a href="https://www.gifuai.net/"><img src="./img/rogo.png"></a></h1>
|
||||
<a id="headerMenuBtn" href="#"><img src="./img/menu_open.svg" alt="メニューを開く"></a>
|
||||
</div><!--sp_headerContent-->
|
||||
<div id="headerMenu">
|
||||
<h2>メニュー<a id="headerMenuClose" href="#"><img class="close" src="./img/btn_close_02.svg" alt="閉じる"></a>
|
||||
</h2>
|
||||
<!--ナビバー左側-->
|
||||
<div class="left">
|
||||
<ul class="utility">
|
||||
<h1><a href="https://www.gifuai.net/"><img src="./img/rogo.png"></a></h1>
|
||||
</ul>
|
||||
</div><!--left-->
|
||||
|
||||
<!--ナビバー右側-->
|
||||
<div class="right">
|
||||
<ul class="utility">
|
||||
<li><a href="https://www.gifuai.net/">ホーム</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=60043">岐阜ロゲ</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=4427">自治会SNS</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=9370">会員・寄付金募集</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=12434">フォトギャラリー</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=52511">プレスリリース</a></li>
|
||||
</ul>
|
||||
</div><!--right-->
|
||||
</div><!--headerMenu-->
|
||||
</div><!--headerContent-->
|
||||
</header>
|
||||
<div class="to_classification">
|
||||
<div class="to_class_box">
|
||||
<div class="to_class_tebox">
|
||||
<div class="to_class_text">
|
||||
<h1>Myアルバム</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="to_class_img">
|
||||
<img src="./img/title_event.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="viewtop">
|
||||
<section class="view">
|
||||
<!-- イベント選択 -->
|
||||
<select id="eventSelect">
|
||||
<option disabled="disabled" value="">イベント一覧</option>
|
||||
<option selected="selected" value="FC岐阜">FC岐阜</option>
|
||||
<!-- 他のイベントオプションを追加 -->
|
||||
</select>
|
||||
|
||||
<!-- ゼッケン番号入力 -->
|
||||
<input type="text" id="zekkenInput" placeholder="ゼッケン番号">
|
||||
|
||||
<!-- パスワード入力 -->
|
||||
<input type="password" id="passwordInput" placeholder="パスワード">
|
||||
|
||||
<!-- 検索ボタン -->
|
||||
<button onclick="searchPhotos()">写真リストを検索</button>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- 結果表示エリア -->
|
||||
<div id="photoList"></div>
|
||||
</section>
|
||||
<footer class="gifu_fotter">
|
||||
<div class="footer_menubox">
|
||||
<div><a href="https://www.gifuai.net/"><img src="./img/rogo.png"></a></div>
|
||||
<div class="footer_menu">
|
||||
<ul class="footer_menulink">
|
||||
<li><a href="https://www.gifuai.net/">ホーム</a></li>
|
||||
<li><a href="https://www.gifuai.net/?page_id=4806">information</a></li>
|
||||
<li><a
|
||||
href="https://docs.google.com/forms/d/e/1FAIpQLScEXBGEZroAR6F8z2OKhjXn74PhZ5bcSheZVlGlGjz12Iu1JA/viewform">お問い合わせ</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="footer_menulogo">
|
||||
<li><a href="https://twitter.com/GifuK7"><img src="./img/Xlogo.svg" alt="Xロゴ"></a></li>
|
||||
<li><a href="https://www.facebook.com/gifu.ai.network/"><img src="./img/facebook_logo.svg"
|
||||
alt="Facebookロゴ"></a></li>
|
||||
<li><a href="https://www.instagram.com/gifuainetwork/?igshid=MzMyNGUyNmU2YQ%3D%3D"><img
|
||||
src="./img/Instagram_logo.svg" alt="instagramロゴ"></a></li>
|
||||
<li><a href=""><img></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="f_copy">Copyright©NPO岐阜aiネットワーク</div>
|
||||
</footer>
|
||||
<script>
|
||||
|
||||
function searchPhotos() {
|
||||
var selectedEvent = document.getElementById('eventSelect').value;
|
||||
var selectedZekken = document.getElementById('zekkenInput').value;
|
||||
var inputedPassword = document.getElementById('passwordInput').value;
|
||||
|
||||
// login関数を実行して写真リストを取得
|
||||
login(selectedEvent, selectedZekken, inputedPassword);
|
||||
}
|
||||
|
||||
async function Login(selectedEvent, selectedZekken, inputedPassword) {
|
||||
const event = selectedEvent;
|
||||
const identifier = selectedZekken;
|
||||
const password = inputedPassword;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/login/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: identifier, // メールアドレスまたはゼッケン番号
|
||||
password: password
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'ログインに失敗しました');
|
||||
}
|
||||
|
||||
// ログイン成功時の処理
|
||||
localStorage.setItem('authToken', data.token);
|
||||
localStorage.setItem('userData', JSON.stringify(data.user));
|
||||
|
||||
var URL = "https://rogaining.sumasen.net/api/get-photolist?event=" + selectedEvent + "&zekken=" + selectedZekken + "&pw=" + inputedPassword;
|
||||
|
||||
axios.get(URL)
|
||||
.then(function (response) {
|
||||
displayPhotos(response.data); // 写真リストを表示する関数にレスポンスオブジェクトを渡す
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("login function error: ", error);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// エラーメッセージを表示
|
||||
errorMessage.textContent = error.message;
|
||||
errorMessage.style.display = 'block';
|
||||
} finally {
|
||||
// 送信ボタンを再度有効化
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'ログイン';
|
||||
}
|
||||
}
|
||||
|
||||
// login関数内で写真リストをDOMに表示する処理を追加
|
||||
function login_old(selectedEvent, selectedZekken, inputedPassword) {
|
||||
var URL = "https://rogaining.sumasen.net/api/get-photolist?event=" + selectedEvent + "&zekken=" + selectedZekken + "&pw=" + inputedPassword;
|
||||
|
||||
axios.get(URL)
|
||||
.then(function (response) {
|
||||
displayPhotos(response.data); // 写真リストを表示する関数にレスポンスオブジェクトを渡す
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("login function error: ", error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 写真リストを表示する関数
|
||||
function displayPhotos(response) {
|
||||
var photoListDiv = document.getElementById('photoList');
|
||||
photoListDiv.innerHTML = ''; // 既存の内容をクリア
|
||||
|
||||
// レスポンス全体をログに出力
|
||||
console.log('Response object:', response);
|
||||
|
||||
// レスポンスオブジェクトからphoto_list配列を取得
|
||||
var photos = response.photo_list;
|
||||
|
||||
// photo_listの内容をログに出力
|
||||
console.log('Photo list array:', photos);
|
||||
|
||||
// 'photos'が配列であることを確認
|
||||
if (Array.isArray(photos)) {
|
||||
photos.forEach(function (photodata,index) {
|
||||
// 各写真のURLをインデックス付きでログに出力
|
||||
console.log(`Photo ${index + 1} data:`, photodata);
|
||||
|
||||
// photodataのプロパティの存在確認とcp_numberの条件チェック
|
||||
if (!photodata.hasOwnProperty('photo_url') ||
|
||||
!photodata.hasOwnProperty('cp_number') ||
|
||||
photodata.cp_number <= 0) { // cp_numberが0以下の場合はスキップ
|
||||
console.log(`Skipping photo at index ${index}. cp_number: ${photodata.cp_number}`);
|
||||
return; // この写真をスキップ
|
||||
}
|
||||
|
||||
// img要素を作成
|
||||
var img = document.createElement('img');
|
||||
img.src = photodata.photo_url; // 写真のURLをsrc属性に設定
|
||||
img.className = 'event-photo'; // クラス名を設定
|
||||
img.alt = 'Photo cp=${photodata.cp_number}'; // 代替テキストを設定
|
||||
|
||||
// 画像の読み込みエラーをキャッチ
|
||||
img.onerror = function() {
|
||||
console.error(`Failed to load image ${index + 1}:`, photodata.photorl);
|
||||
};
|
||||
|
||||
// 画像を表示エリアに追加
|
||||
photoListDiv.appendChild(img);
|
||||
});
|
||||
} else {
|
||||
photoListDiv.innerHTML = 'ゼッケン番号とパスワードが一致していません。もう一度入力をお願いします。'
|
||||
console.error('Expected photos to be an array, but received:', photos);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src="jquery-2.1.3.min.js"></script>
|
||||
|
||||
<script src="./js/main.js"></script>
|
||||
<link rel="stylesheet" href="./js/drawer.min.js">
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user