mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-05 19:20:12 +08:00
Feat landing page - Mobile Optimisation (#112)
This optimisation is for mobile usage and also make header a better component. - Header Restruction - Menu pop up when using smaller screen - Seperate header components and other components
This commit is contained in:
17 files changed
+340
-180
No files matched your search
+1
-1
@@ -2,6 +2,6 @@
|
|||||||
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
|
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
|
||||||
<h1 id="CommandBook" class="inline text-text-primary font-xl">指令之書</h1>
|
<h1 id="CommandBook" class="inline text-text-primary font-xl">指令之書</h1>
|
||||||
<div id="cheatsheets">
|
<div id="cheatsheets">
|
||||||
<p class="text-text-secondary">--指令列表--</p>
|
<p class="text-text-secondary">-- --指令列表-- --</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -4,7 +4,7 @@ import { useToast } from 'vue-toastification'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Vue3Lottie } from 'vue3-lottie'
|
import { Vue3Lottie } from 'vue3-lottie'
|
||||||
// import { surveyUrl } from '../config'
|
// import { surveyUrl } from '../config'
|
||||||
import CompleteAnimation from '../img/complete-animation.json'
|
import CompleteAnimation from '../../img/complete-animation.json'
|
||||||
|
|
||||||
const showModal = ref(true) // Controls whether the modal is visible or not
|
const showModal = ref(true) // Controls whether the modal is visible or not
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -26,7 +26,6 @@ const backtoMap = async () => {
|
|||||||
try {
|
try {
|
||||||
router.push({ name: 'map' })
|
router.push({ name: 'map' })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// useToast().error('Failed to load map')
|
|
||||||
toast.error('無法讀取地圖')
|
toast.error('無法讀取地圖')
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
@@ -60,17 +59,6 @@ setTimeout(() => {
|
|||||||
<p class="text-base text-text text-center">
|
<p class="text-base text-text text-center">
|
||||||
接下來回到地圖,迎接新的挑戰吧!
|
接下來回到地圖,迎接新的挑戰吧!
|
||||||
</p>
|
</p>
|
||||||
<!-- <p class="text-base text-text">
|
|
||||||
您已成功完成挑戰,真的非常感謝您的參與!<br />
|
|
||||||
如果您能撥冗填寫這份問卷,我們將不勝感激,這對我們非常重要!
|
|
||||||
</p> -->
|
|
||||||
<!-- <a
|
|
||||||
id="SurveyButton"
|
|
||||||
:href="surveyUrl"
|
|
||||||
target="_blank"
|
|
||||||
class="inline-flex justify-center rounded-lg text-base font-black py-2 mt-3 bg-text-primary w-full"
|
|
||||||
>填寫問卷</a
|
|
||||||
> -->
|
|
||||||
<a
|
<a
|
||||||
id="BacktoMap"
|
id="BacktoMap"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
+13
-11
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
import { createSession } from '../store/session'
|
import { createSession } from '../../store/session'
|
||||||
import { surveyUrl } from '../config'
|
import { surveyUrl } from '../../config'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const reset = async () => {
|
const reset = async () => {
|
||||||
@@ -20,29 +20,31 @@ const continuePlay = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="h-[10%] p-3 grid grid-cols-4 gap-4 place-content-between">
|
<section
|
||||||
|
class="h-fit w-fit px-1 py-1 grid grid-cols-4 gap-2 place-content-between"
|
||||||
|
>
|
||||||
<button id="survey" class="bg-bg-secondary rounded-lg p-2">
|
<button id="survey" class="bg-bg-secondary rounded-lg p-2">
|
||||||
<a title="Survey" :href="surveyUrl" target="_blank" class="h-5 w-5">
|
<a title="Survey" :href="surveyUrl" target="_blank" class="h-5 w-5">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['fas', 'file-invoice']"
|
:icon="['fas', 'file-invoice']"
|
||||||
class="text-text-primary"
|
class="text-text-primary px-1"
|
||||||
/>
|
/>
|
||||||
<p class="text-text-primary inline ml-1">問卷</p>
|
<p class="text-text-primary inline ml-1 hidden md:flex">問卷</p>
|
||||||
</a>
|
</a>
|
||||||
</button>
|
</button>
|
||||||
<button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
|
<button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['far', 'circle-question']"
|
:icon="['far', 'circle-question']"
|
||||||
class="text-text-disabled"
|
class="text-text-disabled px-1"
|
||||||
/>
|
/>
|
||||||
<p class="text-text-disabled inline ml-1">解答</p>
|
<p class="text-text-disabled inline ml-1 hidden md:flex">解答</p>
|
||||||
</button>
|
</button>
|
||||||
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
|
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['fas', 'arrow-rotate-left']"
|
:icon="['fas', 'arrow-rotate-left']"
|
||||||
class="text-text-primary"
|
class="text-text-primary px-1"
|
||||||
/>
|
/>
|
||||||
<p class="text-text-primary inline ml-1">重來</p>
|
<p class="text-text-primary inline ml-1 hidden md:flex">重來</p>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="continue"
|
id="continue"
|
||||||
@@ -51,9 +53,9 @@ const continuePlay = async () => {
|
|||||||
>
|
>
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['far', 'circle-right']"
|
:icon="['far', 'circle-right']"
|
||||||
class="text-text-primary"
|
class="text-text-primary px-1"
|
||||||
/>
|
/>
|
||||||
<p class="text-text-primary inline ml-1">繼續</p>
|
<p class="text-text-primary inline ml-1 hidden md:flex">繼續</p>
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import sessionStore from '../store/session'
|
import sessionStore from '../../store/session'
|
||||||
import MarkdownText from './MarkdownText.vue'
|
import MarkdownText from '../MarkdownText.vue'
|
||||||
|
|
||||||
const current = ref(-1)
|
const current = ref(-1)
|
||||||
watch(
|
watch(
|
||||||
@@ -27,16 +27,12 @@ const right = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full flex flex-col">
|
<div class="h-full flex flex-col">
|
||||||
<!-- Hint Header -->
|
|
||||||
<div class="flex items-center p-2 h-10">
|
<div class="flex items-center p-2 h-10">
|
||||||
<font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['far', 'lightbulb']"
|
:icon="['far', 'lightbulb']"
|
||||||
class="text-yellow-200 p-2 content-center"
|
class="text-yellow-200 p-2 content-center"
|
||||||
/>
|
/>
|
||||||
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
|
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">提示</h1>
|
||||||
<!-- Hint -->
|
|
||||||
提示
|
|
||||||
</h1>
|
|
||||||
<div v-if="current !== -1" class="flex w-full justify-end items-end">
|
<div v-if="current !== -1" class="flex w-full justify-end items-end">
|
||||||
<button
|
<button
|
||||||
class="p-2 m-1 w-1/8"
|
class="p-2 m-1 w-1/8"
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import MarkdownText from './MarkdownText.vue'
|
import MarkdownText from '../MarkdownText.vue'
|
||||||
import sessionStore from '../store/session'
|
import sessionStore from '../../store/session'
|
||||||
import { Task } from '../types'
|
import { Task } from '../../types'
|
||||||
|
|
||||||
const tasks = computed(() => {
|
const tasks = computed(() => {
|
||||||
return sessionStore.session.tasks.map((task: Task) => {
|
return sessionStore.session.tasks.map((task: Task) => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useTerminal } from '../store/session'
|
import { useTerminal } from '../../store/session'
|
||||||
|
|
||||||
const terminal = ref<HTMLElement | null>(null)
|
const terminal = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FileNode from './FileNode.vue'
|
import FileNode from '../FileNode.vue'
|
||||||
import sessionStore from '../store/session'
|
import sessionStore from '../../store/session'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
File renamed without changes.
@@ -0,0 +1,108 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useToast } from 'vue-toastification'
|
||||||
|
import { logout } from '../../utils/auth'
|
||||||
|
import { bugReportUrl, surveyUrl } from '../../config'
|
||||||
|
import { reset } from '../../store/session'
|
||||||
|
import userProfileStore, {
|
||||||
|
loadUserProfile,
|
||||||
|
resetUserProfile,
|
||||||
|
} from '../../store/userProfile'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
try {
|
||||||
|
await logout()
|
||||||
|
resetUserProfile()
|
||||||
|
reset()
|
||||||
|
router.push({ name: 'login' })
|
||||||
|
} catch (err) {
|
||||||
|
toast.error('登出失敗')
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
await loadUserProfile()
|
||||||
|
} catch (err) {
|
||||||
|
toast.error('無法讀取使用者資料')
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<!-- <button id="warning" class="flex items-center h-5 w-5">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'triangle-exclamation']"
|
||||||
|
class="text-text-primary h-full w-full"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button id="setting" class="flex items-center h-5 w-5">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'gear']"
|
||||||
|
class="text-text-primary h-full w-full"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button id="fullscreen" class="flex items-center h-5 w-5">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'expand']"
|
||||||
|
class="text-text-primary h-full w-full"
|
||||||
|
/>
|
||||||
|
</button> -->
|
||||||
|
<p
|
||||||
|
id="UsernameText"
|
||||||
|
class="text-text inline-block whitespace-nowrap px-1.5"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
{{ userProfileStore.username }}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
title="Survey"
|
||||||
|
:href="surveyUrl"
|
||||||
|
target="_blank"
|
||||||
|
class="md:h-5 md:w-5 px-1.5 w-auto"
|
||||||
|
>
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'file-invoice']"
|
||||||
|
class="text-text-primary inline"
|
||||||
|
/>
|
||||||
|
<span class="text-text sm:hidden px-2">Survey</span>
|
||||||
|
</a>
|
||||||
|
<RouterLink
|
||||||
|
title="LeaderBoard"
|
||||||
|
to="/leaderboard"
|
||||||
|
class="md:h-5 md:w-5 px-1.5 w-auto"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="['fas', 'trophy']" class="text-text-primary" />
|
||||||
|
<span class="text-text sm:hidden px-2">Leader Board</span>
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink title="Map" to="/map" class="md:h-5 md:w-5 px-1.5 w-auto">
|
||||||
|
<font-awesome-icon :icon="['fas', 'map']" class="text-text-primary" />
|
||||||
|
<span class="text-text sm:hidden px-2">Map</span>
|
||||||
|
</RouterLink>
|
||||||
|
<a
|
||||||
|
title="Bug Report"
|
||||||
|
:href="bugReportUrl"
|
||||||
|
target="_blank"
|
||||||
|
class="md:h-5 md:w-5 px-1.5 w-auto"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
|
||||||
|
<span class="text-text sm:hidden px-2">Bug Report</span>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
title="Sign Out"
|
||||||
|
@click="handleLogout"
|
||||||
|
class="md:h-5 md:w-5 px-1.5 w-auto"
|
||||||
|
>
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'arrow-right-from-bracket']"
|
||||||
|
class="text-text-primary"
|
||||||
|
/>
|
||||||
|
<span class="text-text sm:hidden px-2">Sign Out</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, ref } from 'vue'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
headerComponent: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const menuOpen = ref(false)
|
||||||
|
const windowWidth = window.innerWidth < 768
|
||||||
|
const toggleMenu = () => {
|
||||||
|
menuOpen.value = !menuOpen.value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="w-full top-0 px-5 fixed flex flex-row flex-wrap bg-bg-secondary justify-between items-center sm:justify-between"
|
||||||
|
>
|
||||||
|
<div class="flex flex-row items-center gap-3" :class="{ hidden: menuOpen }">
|
||||||
|
<img src="../../img/icon_totem.svg" class="h-8 justify-self-start" />
|
||||||
|
<RouterLink
|
||||||
|
to="/"
|
||||||
|
class="text-text-primary inline-block font-bold whitespace-nowrap pt-1 transition ease-in-out hover:scale-110 duration-300"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
Linux Odyssey
|
||||||
|
</RouterLink>
|
||||||
|
<p
|
||||||
|
id="QuestTitle"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
{{ title }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="w-full flex flex-col items-center justify-end sm:w-auto sm:block sm:ml-6"
|
||||||
|
:class="{ hidden: !menuOpen }"
|
||||||
|
>
|
||||||
|
<component :is="headerComponent" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="windowWidth"
|
||||||
|
:class="{ 'w-full flex justify-end pb-3': menuOpen }"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex items-center h-5 w-5 rounded-md sm:hidden"
|
||||||
|
aria-controls="mobile-menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
@click="toggleMenu"
|
||||||
|
>
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'bars']"
|
||||||
|
class="text-text-primary h-full w-full"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { bugReportUrl } from '../../config'
|
||||||
|
|
||||||
|
const scrollToSection = (sectionId: string) => {
|
||||||
|
const section = document.getElementById(sectionId)
|
||||||
|
if (section) {
|
||||||
|
const container = document.querySelector('.overflow-y-auto') as HTMLElement // Replace '.overflow-y-auto' with the appropriate class or selector for your container
|
||||||
|
if (container) {
|
||||||
|
container.scrollTo({
|
||||||
|
top: section.offsetTop - container.offsetTop,
|
||||||
|
behavior: 'smooth', // Add smooth scrolling behavior if desired
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
id="Intro"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
|
||||||
|
@click="scrollToSection('intro')"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
前言
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="Feature"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
|
||||||
|
@click="scrollToSection('feature')"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
特色</button
|
||||||
|
><button
|
||||||
|
id="Info"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
|
||||||
|
@click="scrollToSection('about')"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
關於
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="Contact"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
|
||||||
|
@click="scrollToSection('contact')"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
聯絡我們
|
||||||
|
</button>
|
||||||
|
<RouterLink
|
||||||
|
title="Sign In"
|
||||||
|
id="SignIn"
|
||||||
|
to="/register"
|
||||||
|
class="text-text inline-block font-bold whitespace-nowrap pt-1 px-2 hover:text-text-primary"
|
||||||
|
style="font-size: 2vh"
|
||||||
|
>
|
||||||
|
登入
|
||||||
|
</RouterLink>
|
||||||
|
<a title="Bug Report" :href="bugReportUrl" target="_blank" class="h-5 w-5">
|
||||||
|
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
+15
-11
@@ -2,14 +2,15 @@
|
|||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
import sessionStore, { init } from '../store/session'
|
import sessionStore, { init } from '../store/session'
|
||||||
import GameHeaderPart from '../components/GameHeaderPart.vue'
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
|
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
|
||||||
// import CommandlistPart from '../components/CommandlistPart.vue'
|
// import CommandlistPart from '../components/CommandlistPart.vue'
|
||||||
import HintPart from '../components/HintPart.vue'
|
import HintPart from '../components/game/HintPart.vue'
|
||||||
import QuestPart from '../components/QuestPart.vue'
|
import QuestPart from '../components/game/QuestPart.vue'
|
||||||
import TerminalPart from '../components/TerminalPart.vue'
|
import TerminalPart from '../components/game/TerminalPart.vue'
|
||||||
import VisualizationPart from '../components/VisualizationPart.vue'
|
import VisualizationPart from '../components/game/VisualizationPart.vue'
|
||||||
import ControlPalette from '../components/ControlPalette.vue'
|
import ControlPalette from '../components/game/ControlPalette.vue'
|
||||||
import CompleteModal from '../components/CompleteModal.vue'
|
import CompleteModal from '../components/game/CompleteModal.vue'
|
||||||
import { LoadQuestError, LoadSessionError } from '../utils/errors'
|
import { LoadQuestError, LoadSessionError } from '../utils/errors'
|
||||||
|
|
||||||
const completed = computed(() => {
|
const completed = computed(() => {
|
||||||
@@ -45,8 +46,11 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- game header -->
|
<!-- game header -->
|
||||||
<div class="h-[6vh] w-full">
|
<div class="w-full">
|
||||||
<GameHeaderPart :title="sessionStore.quest?.title" />
|
<HeaderPart
|
||||||
|
:title="sessionStore.quest?.title"
|
||||||
|
:headerComponent="GameHeaderComponents"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- current status indicator -->
|
<!-- current status indicator -->
|
||||||
<input
|
<input
|
||||||
@@ -55,7 +59,7 @@ onMounted(async () => {
|
|||||||
:value="sessionStore.session.status"
|
:value="sessionStore.session.status"
|
||||||
/>
|
/>
|
||||||
<!-- main -->
|
<!-- main -->
|
||||||
<div id="main" class="h-[94vh] w-full flex p-3 space-x-3">
|
<div id="main" class="h-full pt-10 w-full flex p-3 space-x-3">
|
||||||
<!-- Topic and Command List -->
|
<!-- Topic and Command List -->
|
||||||
<div class="bg-bg-secondary h-full w-1/4 rounded-lg">
|
<div class="bg-bg-secondary h-full w-1/4 rounded-lg">
|
||||||
<section id="quest" class="h-full p-3 overflow-auto">
|
<section id="quest" class="h-full p-3 overflow-auto">
|
||||||
@@ -67,7 +71,7 @@ onMounted(async () => {
|
|||||||
</section> -->
|
</section> -->
|
||||||
</div>
|
</div>
|
||||||
<!-- Terminal and Hint -->
|
<!-- Terminal and Hint -->
|
||||||
<div class="bg-bg h-full w-1/2 rounded-lg">
|
<div class="bg-bg h-full rounded-lg w-1/2">
|
||||||
<section id="hint" class="h-3/5">
|
<section id="hint" class="h-3/5">
|
||||||
<HintPart />
|
<HintPart />
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+45
-119
@@ -1,97 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ContactButton from '../components/ContactButton.vue'
|
import ContactButton from '../components/ContactButton.vue'
|
||||||
import DynamicBackground from '../components/DynamicBackground.vue'
|
import DynamicBackground from '../components/DynamicBackground.vue'
|
||||||
import { bugReportUrl, facebookUrl, githubUrl } from '../config'
|
import LandingHeaderComponents from '../components/header/LandingHeaderComponents.vue'
|
||||||
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
|
|
||||||
const scrollToSection = (sectionId: string) => {
|
import { facebookUrl, githubUrl } from '../config'
|
||||||
const section = document.getElementById(sectionId)
|
|
||||||
if (section) {
|
|
||||||
const container = document.querySelector('.overflow-y-auto') // Replace '.overflow-y-auto' with the appropriate class or selector for your container
|
|
||||||
if (container) {
|
|
||||||
container.scrollTo({
|
|
||||||
top: section.offsetTop,
|
|
||||||
behavior: 'smooth', // Add smooth scrolling behavior if desired
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="w-screen h-screen bg-scroll bg-bg">
|
<div class="w-screen h-screen bg-scroll bg-bg">
|
||||||
<DynamicBackground class="w-full h-full" />
|
<DynamicBackground class="w-full h-full" />
|
||||||
<div class="h-screen w-screen absolute top-0 left-0">
|
<div class="h-screen w-screen absolute top-0 left-0">
|
||||||
<!-- game header -->
|
<!-- game header -->
|
||||||
<div
|
<HeaderPart :headerComponent="LandingHeaderComponents" class="z-50" />
|
||||||
class="w-full h-[6vh] px-5 flex flex-row items-center justify-between bg-bg-secondary"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row items-center gap-3">
|
|
||||||
<img src="../img/icon_totem.svg" class="h-8 justify-self-start" />
|
|
||||||
<button
|
|
||||||
id="GameTitle"
|
|
||||||
class="text-text-primary inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
Linux Odyssey
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-row items-center gap-3">
|
|
||||||
<button
|
|
||||||
id="Intro"
|
|
||||||
class="text-text inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
@click="scrollToSection('intro')"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
Intro
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
id="Feature"
|
|
||||||
class="text-text inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
@click="scrollToSection('feature')"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
Feature</button
|
|
||||||
><button
|
|
||||||
id="Info"
|
|
||||||
class="text-text inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
@click="scrollToSection('about')"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
About
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
id="Contact"
|
|
||||||
class="text-text inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
@click="scrollToSection('contact')"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
Contact Us
|
|
||||||
</button>
|
|
||||||
<RouterLink
|
|
||||||
title="Sign In"
|
|
||||||
id="SignIn"
|
|
||||||
to="/register"
|
|
||||||
class="text-text inline-block font-bold whitespace-nowrap pt-1"
|
|
||||||
style="font-size: 2vh"
|
|
||||||
>
|
|
||||||
Sign In
|
|
||||||
</RouterLink>
|
|
||||||
<a
|
|
||||||
title="Bug Report"
|
|
||||||
:href="bugReportUrl"
|
|
||||||
target="_blank"
|
|
||||||
class="h-5 w-5"
|
|
||||||
>
|
|
||||||
<font-awesome-icon
|
|
||||||
:icon="['fas', 'bug']"
|
|
||||||
class="text-text-primary h-full w-full"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- main content -->
|
<!-- main content -->
|
||||||
<main class="w-full h-[94vh] overflow-y-auto">
|
<main class="w-full h-full overflow-y-auto">
|
||||||
<div class="w-full flex flex-col items-center">
|
<div class="w-full flex flex-col items-center">
|
||||||
<div
|
<div
|
||||||
class="flex flex-col md:flex-row mt-10 justify-around gap-5 items-center my-10"
|
class="flex flex-col md:flex-row mt-10 justify-around gap-5 items-center my-10"
|
||||||
@@ -104,7 +26,6 @@ const scrollToSection = (sectionId: string) => {
|
|||||||
Odyssey
|
Odyssey
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- intro -->
|
<!-- intro -->
|
||||||
<div class="w-full md:w-2/3 px-5">
|
<div class="w-full md:w-2/3 px-5">
|
||||||
<section
|
<section
|
||||||
@@ -228,40 +149,43 @@ const scrollToSection = (sectionId: string) => {
|
|||||||
鈦坦敏捷開發特別獎 第二名
|
鈦坦敏捷開發特別獎 第二名
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container mx-auto">
|
||||||
<!-- <h1
|
<div
|
||||||
class="text-text-primary p-5 font-extrabold font-mono col-span-3 gap-3"
|
class="justify-items-center grid grid-cols-1 sm:grid-cols-3 gap-5"
|
||||||
style="font-size: 3.5vh"
|
>
|
||||||
>
|
<h1
|
||||||
玩家感想
|
class="text-text-primary text-3xl font-extrabold font-mono sm:col-span-3 gap-6"
|
||||||
</h1>
|
>
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
玩家感想
|
||||||
"很好玩很像益智遊戲"
|
<!--可以做成翻頁卡牌一次一個-->
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
<h1 class="text-xl text-center">"很好玩很像益智遊戲"</h1>
|
||||||
"我從來不知道自由軟體的存在,謝謝你們。"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"我從來不知道自由軟體的存在,謝謝你們。"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"還蠻好玩的,讓程式變好玩"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"還蠻好玩的,讓程式變好玩"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"以前不喜歡電腦課現在覺得喜歡一點點"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"以前不喜歡電腦課現在覺得喜歡一點點"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"程式真的對新手很友善,讓我學到了很多東西"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"程式真的對新手很友善,讓我學到了很多東西"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"感覺做得很好很簡明易懂關卡設計很用心"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"感覺做得很好很簡明易懂關卡設計很用心"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"這個體驗我覺得非常有趣,可以體驗到沒辦法體驗的程式,學長也都熱心指導,非常感謝學長的用心的講解,希望以後還可以再體驗這種活動"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"這個體驗我覺得非常有趣,可以體驗到沒辦法體驗的程式,學長也都熱心指導,非常感謝學長的用心的講解,希望以後還可以再體驗這種活動"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"以前我覺得寫程式很難不過現在覺得很有趣"
|
<h1 class="text-xl text-center">
|
||||||
</h1>
|
"以前我覺得寫程式很難不過現在覺得很有趣"
|
||||||
<h1 class="text-text p-3" style="font-size: 2.5vh">
|
</h1>
|
||||||
"以前覺得寫程式是我生活中碰不到而且很難的項目。但通過這堂課之後,覺得寫程式不再那麼難以親近"
|
<h1 class="text-xl text-center">
|
||||||
</h1> -->
|
"以前覺得寫程式是我生活中碰不到而且很難的項目。但通過這堂課之後,覺得寫程式不再那麼難以親近"
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<section
|
<section
|
||||||
@@ -271,7 +195,9 @@ const scrollToSection = (sectionId: string) => {
|
|||||||
<h1 class="text-text-primary font-extrabold font-mono text-3xl">
|
<h1 class="text-text-primary font-extrabold font-mono text-3xl">
|
||||||
聯絡我們
|
聯絡我們
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex flex-row items-center justify-around mt-5 gap-5">
|
<div
|
||||||
|
class="flex flex-row items-center justify-around mt-5 gap-5 mb-5"
|
||||||
|
>
|
||||||
<ContactButton
|
<ContactButton
|
||||||
:url="facebookUrl"
|
:url="facebookUrl"
|
||||||
:icons="['fab', 'facebook']"
|
:icons="['fab', 'facebook']"
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useToast } from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
import DynamicBackground from '../components/DynamicBackground.vue'
|
import DynamicBackground from '../components/DynamicBackground.vue'
|
||||||
import GameHeaderPart from '../components/GameHeaderPart.vue'
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
|
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
|
|
||||||
const leaderboard = ref<
|
const leaderboard = ref<
|
||||||
@@ -35,8 +36,8 @@ onMounted(async () => {
|
|||||||
<div
|
<div
|
||||||
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
||||||
>
|
>
|
||||||
<div class="h-[6vh] w-full">
|
<div class="w-full">
|
||||||
<GameHeaderPart title="排行榜" />
|
<HeaderPart title="排行榜" :headerComponent="GameHeaderComponents" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-fit h-[94vh] m-3">
|
<div class="w-fit h-[94vh] m-3">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AuthForm from '../components/AuthForm.vue'
|
import AuthForm from '../components/AuthForm.vue'
|
||||||
import Background from '../components/DynamicBackground.vue'
|
import DynamicBackground from '../components/DynamicBackground.vue'
|
||||||
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TooManyRequestsError,
|
TooManyRequestsError,
|
||||||
@@ -45,10 +46,11 @@ const handleLogin = async ({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-screen h-screen">
|
<div class="w-screen h-screen">
|
||||||
<Background class="w-full h-full" />
|
<DynamicBackground class="w-full h-full" />
|
||||||
<div
|
<div
|
||||||
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
class="w-screen h-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
||||||
>
|
>
|
||||||
|
<HeaderPart />
|
||||||
<div class="w-fit">
|
<div class="w-fit">
|
||||||
<AuthForm @onSubmit="handleLogin" type="login" />
|
<AuthForm @onSubmit="handleLogin" type="login" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import QuestMap from '../components/QuestMap.vue'
|
import QuestMap from '../components/QuestMap.vue'
|
||||||
import GameHeaderPart from '../components/GameHeaderPart.vue'
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
|
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-[6vh] w-full">
|
<div class="w-fit">
|
||||||
<GameHeaderPart title="關卡地圖" />
|
<HeaderPart
|
||||||
|
title="關卡地圖"
|
||||||
|
:headerComponent="GameHeaderComponents"
|
||||||
|
class="z-50"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-center items-center w-full h-[94vh]">
|
<div class="flex flex-col justify-center items-center w-full h-[94vh]">
|
||||||
<QuestMap />
|
<QuestMap />
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import validator from 'validator'
|
|||||||
import { isValidUsername } from '@linux-odyssey/utils'
|
import { isValidUsername } from '@linux-odyssey/utils'
|
||||||
import { passwordPolicy } from '@linux-odyssey/constants'
|
import { passwordPolicy } from '@linux-odyssey/constants'
|
||||||
import AuthForm from '../components/AuthForm.vue'
|
import AuthForm from '../components/AuthForm.vue'
|
||||||
import Background from '../components/DynamicBackground.vue'
|
import DynamicBackground from '../components/DynamicBackground.vue'
|
||||||
|
import HeaderPart from '../components/header/HeaderPart.vue'
|
||||||
import { checkUsername, register } from '../utils/auth'
|
import { checkUsername, register } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
UnauthorizedError,
|
UnauthorizedError,
|
||||||
@@ -89,11 +89,12 @@ async function check({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-screen h-screen">
|
<div class="w-screen h-screen">
|
||||||
<Background class="w-full h-full" />
|
<DynamicBackground class="w-full h-full" />
|
||||||
<div
|
<div
|
||||||
class="h-screen w-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
class="h-screen w-screen absolute top-0 left-0 flex flex-wrap justify-center content-center"
|
||||||
>
|
>
|
||||||
<div class="w-fit">
|
<HeaderPart />
|
||||||
|
<div class="w-fit mt-8">
|
||||||
<AuthForm
|
<AuthForm
|
||||||
@onSubmit="handleRegister"
|
@onSubmit="handleRegister"
|
||||||
@onChange="check"
|
@onChange="check"
|
||||||
|
|||||||
Reference in new issue
Block a user