mirror of
https://github.com/linux-odyssey/linux-odyssey.git
synced 2026-11-06 03:20:13 +08:00
Merge branch 'main' into quest-file
This commit is contained in:
commit
21ca083628
46 files changed
+653
-220
No files matched your search
@@ -41,7 +41,7 @@ const hasSocialLogins = computed(() => {
|
||||
})
|
||||
|
||||
const success = () => {
|
||||
router.push({ name: 'game', params: { questId: 'helloworld' } })
|
||||
router.push({ name: 'map' })
|
||||
}
|
||||
|
||||
const error = (err) => {
|
||||
@@ -100,6 +100,7 @@ const handleChange = () => {
|
||||
<form @submit.prevent="handleSubmit()" class="w-full">
|
||||
<div v-if="socialLogin && hasSocialLogins">
|
||||
<a
|
||||
id="GoogleLogin"
|
||||
v-if="availableMethods.google"
|
||||
class="inline-flex justify-center items-center rounded-lg py-2 mt-3 bg-bg-primary text-text-secondary w-full border-text-secondary border-2"
|
||||
href="/api/v1/auth/google"
|
||||
@@ -108,9 +109,13 @@ const handleChange = () => {
|
||||
:icon="['fab', 'google']"
|
||||
class="text-text-secondary px-3 text-lg"
|
||||
/>
|
||||
<span class="font-medium">Continue with Google</span>
|
||||
<span class="font-medium">
|
||||
<!-- Continue with Google -->
|
||||
以 Google 繼續
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
id="GitHubLogin"
|
||||
v-if="availableMethods.github"
|
||||
class="inline-flex justify-center items-center rounded-lg py-2 mt-3 bg-bg-primary text-text-secondary w-full border-text-secondary border-2"
|
||||
href="/api/v1/auth/github"
|
||||
@@ -119,17 +124,22 @@ const handleChange = () => {
|
||||
:icon="['fab', 'github']"
|
||||
class="text-text-secondary px-3 text-lg"
|
||||
/>
|
||||
<span class="font-medium">Continue with GitHub</span>
|
||||
<span class="font-medium">
|
||||
<!-- Continue with GitHub -->
|
||||
以 GitHub 繼續
|
||||
</span>
|
||||
</a>
|
||||
<p
|
||||
id="or"
|
||||
class="text-text-secondary flex justify-center mt-3"
|
||||
v-if="type === 'login' || type === 'register'"
|
||||
>
|
||||
or
|
||||
<!-- or -->
|
||||
或
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<input
|
||||
<!-- <input
|
||||
type="text"
|
||||
id="username"
|
||||
ref="usernameInput"
|
||||
@@ -139,6 +149,17 @@ const handleChange = () => {
|
||||
required
|
||||
@input="handleChange()"
|
||||
autocomplete="username"
|
||||
/> -->
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
ref="usernameInput"
|
||||
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
|
||||
:placeholder="type === 'login' ? '電子郵件 / 帳號名稱' : '帳號名稱'"
|
||||
v-model="username"
|
||||
required
|
||||
@input="handleChange()"
|
||||
autocomplete="username"
|
||||
/>
|
||||
<label
|
||||
class="text-sm font-normal text-text-secondary"
|
||||
@@ -149,7 +170,7 @@ const handleChange = () => {
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-6" v-if="type === 'register'">
|
||||
<input
|
||||
<!-- <input
|
||||
type="email"
|
||||
id="email"
|
||||
ref="emailInput"
|
||||
@@ -159,10 +180,21 @@ const handleChange = () => {
|
||||
required
|
||||
@input="handleChange()"
|
||||
autocomplete="email"
|
||||
/> -->
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
ref="emailInput"
|
||||
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
|
||||
placeholder="電子郵件"
|
||||
v-model="email"
|
||||
required
|
||||
@input="handleChange()"
|
||||
autocomplete="email"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-6" v-if="type === 'login' || type === 'register'">
|
||||
<input
|
||||
<!-- <input
|
||||
type="password"
|
||||
id="password"
|
||||
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
|
||||
@@ -170,30 +202,66 @@ const handleChange = () => {
|
||||
v-model="password"
|
||||
required
|
||||
@input="handleChange()"
|
||||
/> -->
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
class="my-4 bg-bg-primary text-text-primary bg- rounded-md block w-full px-3 h-10 shadow-sm focus:outline-none placeholder:text-text-line focus:ring-2 focus:ring-text-primary ring-1 ring-bg-secondary"
|
||||
placeholder="密碼"
|
||||
v-model="password"
|
||||
required
|
||||
@input="handleChange()"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-error flex justify-center" v-if="errorMessage">
|
||||
<p
|
||||
id="ErrorDisplay"
|
||||
class="text-error flex justify-center"
|
||||
v-if="errorMessage"
|
||||
>
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<button
|
||||
id="LogInOrSignUp"
|
||||
class="inline-flex justify-center rounded-lg font-black py-2 bg-text-primary text-bg w-full"
|
||||
type="submit"
|
||||
>
|
||||
<span v-if="type === 'login'">Log In</span>
|
||||
<span v-else>Sign Up</span>
|
||||
<span v-if="type === 'login'">
|
||||
<!-- Log In -->
|
||||
登入
|
||||
</span>
|
||||
<span v-else>
|
||||
<!-- Sign Up -->
|
||||
註冊
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<p class="text-text flex justify-center mt-3">
|
||||
<span v-if="type === 'register'"
|
||||
>Already have an account?
|
||||
<RouterLink class="text-text-primary font-bold" to="/login">
|
||||
<u>Log in</u>
|
||||
<p id="SwitchLoginRegister" class="text-text flex justify-center mt-3">
|
||||
<span id="CheckRegistered" v-if="type === 'register'">
|
||||
<!-- Already have an account? -->
|
||||
已經有帳號了嗎?
|
||||
<RouterLink
|
||||
id="SwitchtoLogin"
|
||||
class="text-text-primary font-bold"
|
||||
to="/login"
|
||||
>
|
||||
<u>
|
||||
<!-- Log in -->
|
||||
登入
|
||||
</u>
|
||||
</RouterLink></span
|
||||
>
|
||||
<span v-else-if="type === 'login'"
|
||||
>Don't have an account?
|
||||
<RouterLink class="text-text-primary font-bold" to="/register">
|
||||
<u>Sign up</u>
|
||||
<span id="CheckRegistered" v-else-if="type === 'login'">
|
||||
<!-- Don't have an account? -->
|
||||
還沒有帳號嗎?
|
||||
<RouterLink
|
||||
id="SwitchtoRegister"
|
||||
class="text-text-primary font-bold"
|
||||
to="/register"
|
||||
>
|
||||
<u>
|
||||
<!-- Sign up -->
|
||||
註冊
|
||||
</u>
|
||||
</RouterLink></span
|
||||
>
|
||||
</p>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<font-awesome-icon :icon="['fas', 'list']" class="text-text-primary p-1" />
|
||||
<h1 class="inline text-text-primary font-xl">Command Cheatsheets</h1>
|
||||
<h1 id="CommandBook" class="inline text-text-primary font-xl">
|
||||
<!-- Command Cheatsheets -->
|
||||
指令之書
|
||||
</h1>
|
||||
<div id="cheatsheets">
|
||||
<p class="text-text-secondary">--Command List--</p>
|
||||
<p class="text-text-secondary">
|
||||
<!-- --Command List-- -->
|
||||
-- --指令列表-- --
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -18,14 +18,19 @@ setTimeout(() => {
|
||||
<div v-if="showModal" class="modal">
|
||||
<div class="modal-content bg-bg-secondary rounded-lg">
|
||||
<span class="close text-text" @click="closeModal">×</span>
|
||||
<h2 class="text-2xl text-text mb-5 w-full inline-flex justify-center">
|
||||
Quest Completed!
|
||||
<h2
|
||||
id="QuestCompleted"
|
||||
class="text-2xl text-text mb-5 w-full inline-flex justify-center"
|
||||
>
|
||||
<!-- Quest Completed! -->
|
||||
關卡完成!
|
||||
</h2>
|
||||
<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"
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
<script setup>
|
||||
import { createSession } from '../store/session'
|
||||
|
||||
const reset = async () => {
|
||||
console.log('resetting...')
|
||||
await createSession()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="h-[10%] p-3 grid grid-cols-3 gap-4 place-content-between">
|
||||
<button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
|
||||
@@ -14,21 +5,55 @@ const reset = async () => {
|
||||
:icon="['far', 'circle-question']"
|
||||
class="text-text-disabled"
|
||||
/>
|
||||
<p class="text-text-disabled inline ml-1">Solution</p>
|
||||
<p class="text-text-disabled inline ml-1">
|
||||
<!-- Solution -->
|
||||
解答
|
||||
</p>
|
||||
</button>
|
||||
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'arrow-rotate-left']"
|
||||
class="text-text-primary"
|
||||
/>
|
||||
<p class="text-text-primary inline ml-1">Reset</p>
|
||||
<p class="text-text-primary inline ml-1">
|
||||
<!-- Reset -->
|
||||
重來
|
||||
</p>
|
||||
</button>
|
||||
<button disabled id="continue" class="bg-bg-disabled rounded-lg p-2">
|
||||
<button
|
||||
id="continue"
|
||||
class="bg-bg-secondary rounded-lg p-2"
|
||||
@click="continuePlay"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['far', 'circle-right']"
|
||||
class="text-text-disabled"
|
||||
class="text-text-primary"
|
||||
/>
|
||||
<p class="text-text-disabled inline ml-1">Continue</p>
|
||||
<p class="text-text-primary inline ml-1">
|
||||
<!-- Continue -->
|
||||
繼續
|
||||
</p>
|
||||
</button>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useToast } from 'vue-toastification'
|
||||
import { createSession } from '../store/session'
|
||||
|
||||
const router = useRouter()
|
||||
const reset = async () => {
|
||||
console.log('resetting...')
|
||||
await createSession()
|
||||
}
|
||||
const continuePlay = async () => {
|
||||
try {
|
||||
router.push({ name: 'map' })
|
||||
} catch (err) {
|
||||
// useToast().error('Failed to load map')
|
||||
useToast().error('無法讀取地圖')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -15,7 +15,17 @@ const handleLogout = async () => {
|
||||
reset()
|
||||
router.push({ name: 'login' })
|
||||
} catch (err) {
|
||||
toast.error('Failed to logout')
|
||||
// toast.error('Failed to logout')
|
||||
toast.error('登出失敗')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
const handleMap = async () => {
|
||||
try {
|
||||
router.push({ name: 'map' })
|
||||
} catch (err) {
|
||||
// toast.error('Failed to load map')
|
||||
toast.error('無法讀取地圖')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +48,9 @@ const handleLogout = async () => {
|
||||
/>
|
||||
</button> -->
|
||||
<p
|
||||
id="HeaderText"
|
||||
class="text-text-primary inline-block font-bold whitespace-nowrap"
|
||||
style="font-size: 2vh"
|
||||
v-if="sessionStore.quest"
|
||||
>
|
||||
{{ sessionStore.quest.title }}
|
||||
@@ -63,6 +75,12 @@ const handleLogout = async () => {
|
||||
class="text-text-primary h-full w-full"
|
||||
/>
|
||||
</button> -->
|
||||
<button title="Map" @click="handleMap" class="h-5 w-5">
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'map']"
|
||||
class="text-text-primary h-full w-full"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
title="Bug Report"
|
||||
:href="bugReportUrl"
|
||||
|
||||
@@ -30,8 +30,11 @@ const right = () => {
|
||||
:icon="['far', 'lightbulb']"
|
||||
class="text-yellow-200 p-2 content-center"
|
||||
/>
|
||||
<h1 class="inline text-text font-xl p-2 m-1">Hint</h1>
|
||||
<div v-if="current !== -1" class="w-full flex justify-end">
|
||||
<h1 class="inline text-text w-1/2 font-xl p-2 m-1">
|
||||
<!-- Hint -->
|
||||
提示
|
||||
</h1>
|
||||
<div v-if="current !== -1" class="flex w-full justify-end items-end">
|
||||
<button
|
||||
class="p-2 m-1 w-1/8"
|
||||
id="Lbutton"
|
||||
|
||||
@@ -14,6 +14,7 @@ const marginX = 100
|
||||
const marginY = 65
|
||||
const chartContainer = ref(null)
|
||||
let chartInstance = null
|
||||
const fullwidth = window.screen.width
|
||||
|
||||
use([GraphChart, SVGRenderer, TitleComponent, TooltipComponent])
|
||||
|
||||
@@ -49,11 +50,11 @@ const genOption = (nodes, edges) => ({
|
||||
symbol: () => {
|
||||
return NodeImage
|
||||
},
|
||||
symbolSize: [200, 100],
|
||||
roam: true,
|
||||
symbolSize: [fullwidth / 10, fullwidth / 20],
|
||||
roam: false,
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 22,
|
||||
fontSize: fullwidth / 90,
|
||||
},
|
||||
edgeSymbol: ['pin', 'arrow'],
|
||||
edgeSymbolSize: [2, 15],
|
||||
@@ -126,7 +127,8 @@ function initChart(option) {
|
||||
if (unlocked) {
|
||||
router.push({ name: 'game', params: { questId: id } })
|
||||
} else {
|
||||
toast.warning('You have not completed the previous quest yet!')
|
||||
// toast.warning('You have not completed the previous quest yet!')
|
||||
toast.warning('你還沒完成前一個關卡!')
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -147,9 +149,11 @@ onMounted(async () => {
|
||||
class="p-2 stroke-2 scale-y-120 justify-center items-center"
|
||||
/>
|
||||
<h1
|
||||
class="text-text-primary text-xl p-2.5 absolute w-fit z-2 font-mono font-bold flex flax-wrap"
|
||||
class="p-4 absolute w-fit z-2 font-mono flex flax-wrap text-xl"
|
||||
style="width: 30%; height: 6%; font-size: 3vh; color: #00ff00"
|
||||
>
|
||||
Get through your linux journey!
|
||||
<!-- Get through your linux journey! -->
|
||||
踏上你的Linux冒險之旅吧!
|
||||
</h1>
|
||||
<div class="flex flex-wrap absolute w-full h-full z-1">
|
||||
<div ref="chartContainer" class="w-full h-full flex flex-wrap"></div>
|
||||
|
||||
@@ -11,8 +11,11 @@ import sessionStore from '../store/session'
|
||||
<div>
|
||||
<div v-if="sessionStore.quest" class="whitespace-pre-wrap">
|
||||
<p class="text-text">{{ sessionStore.quest.instruction }}</p>
|
||||
<hr class="border-border border-2 my-2" />
|
||||
<p class="text-text">Tasks:</p>
|
||||
<br />
|
||||
<p id="tasks" class="text-text">
|
||||
<!-- Tasks: -->
|
||||
任務:
|
||||
</p>
|
||||
<ul v-if="sessionStore.session">
|
||||
<li v-for="task in sessionStore.session.tasks" :key="task.id">
|
||||
<p v-if="task.completed" class="text-text-primary">
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
<template>
|
||||
<font-awesome-icon :icon="['fas', 'terminal']" class="text-text p-1" />
|
||||
<button id="Terminal" class="text-text h-[5%]">
|
||||
<!-- Terminal -->
|
||||
終端機
|
||||
</button>
|
||||
<div
|
||||
id="terminal"
|
||||
ref="terminal"
|
||||
class="bg-bg-secondary h-[calc(100%-5%)] p-0.5"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useTerminal } from '../store/session'
|
||||
@@ -10,11 +23,3 @@ onMounted(() => {
|
||||
window.addEventListener('resize', () => term.resizeScreen())
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-[10%] w-full max-h-full">
|
||||
<font-awesome-icon :icon="['fas', 'terminal']" class="text-text p-1" />
|
||||
<button class="text-text h-full">Terminal</button>
|
||||
</div>
|
||||
<div id="terminal" ref="terminal" class="h-[90%] p-2"></div>
|
||||
</template>
|
||||
Reference in new issue
Block a user