firestoreからランキングを作成する
firestoreに保存した時間から経過時間を計測し、ランキングを表示します。
手順
1.ログイン情報(ユーザID、氏名)と同時に開始時間をデータベース(以下DB)に保管する
2.終了ボタンを押し、終了時間をDBに保管する
3.終了時間から開始時間を引き算し、経過時間を計算する
4.経過時間の順にランキングを表示する
実装について
・Nuxt.js(v2.14.5)、Node.js(v13.14.0)、firebase(v8.2.6)を使用
・順位はCSSのlist-style: decimalを使用する
・手順1については下記を参考にしていただければと思います。
コード
<template>
<div class="page01">
<div>
<p class="page01__info">{{ userInfo.number }} {{ userInfo.name }}</p>
</div>
<div class="page01__top">
<div class="page01__last">
<button @click="finishBtn" class="page01__last--submit" v-if="submitBtn">終了ボタン</button>
</div>
</div>
</div>
</template>
<script>
import firebase from '@/plugins/firebase';
const db = firebase.firestore();
const usersCollection = db.collection('users');
export default {
data() {
return {
submitBtn: true,
};
},
computed: {
userInfo() {
return this.$store.getters['userInfo'];
},
},
methods: {
async finishBtn() {
//全て回答した時
this.submitBtn = false;
const inputNmber = this.$store.state.userInfo.number;
let promise = new Promise((resolve) => {
// 終了時間をDBに保存する
resolve(
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
usersCollection.doc(doc.id).set(
{
finishTime: firebase.firestore.FieldValue.serverTimestamp(), //終了時間
},
{ merge: true }
);
});
})
.catch(function (error) {
console.log('Error getting documents: ', error);
})
);
});
promise.then(() => {
// 経過時間を計算し、DBに保存する
setTimeout(() => {
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
const finishUserTime = doc.get('finishTime'); //DBに保存された終了時間を取得する
const startUserTime = doc.get('startTime'); //DBに保存された開始時間を取得する
const totalTime = finishUserTime - startUserTime;
//分秒へ変換
const displayMinutes = Math.floor(((totalTime % (24 * 60 * 60)) % (60 * 60)) / 60); //分
const displaySeconds = Math.floor(((totalTime % (24 * 60 * 60)) % (60 * 60)) % 60); //秒
const seconds = ((totalTime % (24 * 60 * 60)) % (60 * 60)) % 60; //秒(小数点以下を含む)
const few = String(seconds).split('.')[1]; //小数点以下を取得
const displayFew = few.substr(0, 2); //少数を左から2つ切り取る
const displayTime = displayMinutes + '分' + displaySeconds + '秒' + displayFew;
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
usersCollection.doc(doc.id).set(
{
totalTime: totalTime, //経過時間
displayTime: displayTime, //経過時間を分秒に変換した値
},
{ merge: true }
);
});
})
.catch(function (error) {
console.log('Error getting documents: ', error);
});
});
})
.catch(function (error) {
console.log('Error getting documents: ', error);
});
this.$router.push('/ranking/');
}, 3000);
});
},
},
};
</script>
<template>
<div class="ranking">
<ol class="ranking__list">
<li v-for="(ranking, index) in rankings" :key="index" class="ranking__list--item">
<p class="ranking__list--item-text">位 {{ ranking.number }} {{ ranking.name }}: {{ ranking.displayTime }}</p>
</li>
</ol>
</div>
</template>
<script>
import firebase from '@/plugins/firebase';
const db = firebase.firestore();
const usersCollection = db.collection('users');
export default {
data() {
return {
rankings: [],
};
},
mounted() {
usersCollection
.orderBy('totalTime')
.get()
.then((snap) => {
const array = [];
snap.forEach((doc) => {
array.push(doc.data());
});
this.rankings = array;
});
},
};
</script>
.ranking {
&__list {
margin: 0 auto 0 130px;
&--item {
list-style: decimal;
font-size: 30px;
&-text {
margin: 0 0 0 -10px;
}
}
}
}
説明
page/page01.vue
userInfo.numberとuserInfo.nameでstoreに保存してあるログイン情報を表示
<template>
<div class="page01">
<div>
<p class="page01__info">{{ userInfo.number }} {{ userInfo.name }}</p>
</div>
</div>
</template>
page/page01.vue
storeからuserInfoの情報を取得
<script>
:
:
computed: {
userInfo() {
return this.$store.getters['userInfo'];
},
},
:
:
page/page01.vue
一度しか終了ボタンを押せないように「this.submitBtn = false;」とする。
<script>
:
this.submitBtn = false;
:
:
page/page01.vue
DBに保存されているnumberフィールドに対して、ユーザID(inputNmber)で検索し、
一致するものに「finishTime」として終了時間を追加する
<script>
let promise = new Promise((resolve) => {
// 終了時間をDBに保存する
resolve(
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
usersCollection.doc(doc.id).set(
{
finishTime: firebase.firestore.FieldValue.serverTimestamp(), //終了時間
},
{ merge: true }
);
});
})
.catch(function (error) {
console.log('Error getting documents: ', error);
})
);
});
:
:
page/page01.vue
再度DBに保存されているnumberフィールドに対して、ユーザID(inputNmber)で検索し、
一致するものから「startTime」と「finishTime」を取得する。
:
:
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
const finishUserTime = doc.get('finishTime'); //DBに保存された終了時間を取得する
const startUserTime = doc.get('startTime'); //DBに保存された開始時間を取得する
:
:
page/page01.vue
取得した終了時間(finishUserTime)から開始時間(startUserTime)を引き算し、経過時間(totalTime)を求める。
求めた値のままだと見にくいので分秒に直します。(displayTime)
小数点は第2位まで表示する。
:
:
const totalTime = finishUserTime - startUserTime;
//分秒へ変換
const displayMinutes = Math.floor(((totalTime % (24 * 60 * 60)) % (60 * 60)) / 60); //分
const displaySeconds = Math.floor(((totalTime % (24 * 60 * 60)) % (60 * 60)) % 60); //秒
const seconds = ((totalTime % (24 * 60 * 60)) % (60 * 60)) % 60; //秒(小数点以下を含む)
const few = String(seconds).split('.')[1]; //小数点以下を取得
const displayFew = few.substr(0, 2); //少数を左から2つ切り取る
const displayTime = displayMinutes + '分' + displaySeconds + '秒' + displayFew;
:
:
page/page01.vue
「totalTime」と「displayTime」をDBに追加する
:
:
usersCollection
.where('number', '==', inputNmber)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
usersCollection.doc(doc.id).set(
{
totalTime: totalTime, //経過時間
displayTime: displayTime, //経過時間を分秒に変換した値
},
{ merge: true }
);
});
})
.catch(function (error) {
console.log('Error getting documents: ', error);
});
});
})
:
:
page/page01.vue
DBに追加したら/rankingに遷移する。
this.$router.push('/ranking/');
page/ranking.vue
順位はリストで表示する。
その際にランキングにはユーザID、氏名、経過時間を表示する。
<template>
<div class="ranking">
<ol class="ranking__list">
<li v-for="(ranking, index) in rankings" :key="index" class="ranking__list--item">
<p class="ranking__list--item-text">位 {{ ranking.number }} {{ ranking.name }}: {{ ranking.displayTime }}</p>
</li>
</ol>
</div>
</template>
page/ranking.vue
DBに保存されているtotalTimeフィールドに対して、経過時間の少ない(早い)順で並び替えをする。
そのデータを配列に入れ、表示する
<script>
:
:
export default {
data() {
return {
rankings: [],
};
},
mounted() {
usersCollection
.orderBy('totalTime')
.get()
.then((snap) => {
const array = [];
snap.forEach((doc) => {
array.push(doc.data());
});
this.rankings = array;
});
},
};
</script>
以上となります。
最後に
初めてFirebaseを使用して作成したので、もっと良い記述方法あれば教えてください。
良ければ記事をLike、Twitterのフォローをお願いします。
参考
Discussion