start multi language support
This commit is contained in:
parent
35705aa868
commit
c9d8811203
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="navbar bg-base-100 min-h-[52px] p-0 shadow">
|
||||
<NuxtLink class="navbar-brand p-2" href="/">
|
||||
<NuxtLink class="navbar-brand min-w-[46px] p-2" href="/">
|
||||
<img src="~/assets/images/ffplayout-small.png" class="img-fluid" alt="Logo" width="30" height="30" />
|
||||
</NuxtLink>
|
||||
<div class="navbar-end w-1/5 grow">
|
||||
@ -25,7 +25,7 @@
|
||||
<details tabindex="0" @focusout="closeDropdown">
|
||||
<summary>
|
||||
<div class="h-[19px] text-base">
|
||||
<span> Channels </span>
|
||||
<span> {{ $t('button.channels') }} </span>
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="p-2">
|
||||
@ -39,13 +39,13 @@
|
||||
</li>
|
||||
<li class="bg-base-100 rounded-md">
|
||||
<button class="h-[27px] text-base" exactActiveClass="is-active" @click="logout()">
|
||||
Logout
|
||||
{{ $t('button.logout') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-end hidden md:flex w-4/5 min-w-[600px]">
|
||||
<div class="navbar-end hidden md:flex w-4/5 min-w-[750px]">
|
||||
<ul class="menu menu-sm menu-horizontal px-1">
|
||||
<li v-for="item in menuItems" :key="item.name" class="bg-base-100 rounded-md p-0">
|
||||
<NuxtLink
|
||||
@ -62,7 +62,7 @@
|
||||
<details tabindex="0" @focusout="closeDropdown">
|
||||
<summary>
|
||||
<div class="h-[19px] text-base">
|
||||
<span> Channels </span>
|
||||
<span> {{ $t('button.channels') }} </span>
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="p-2 bg-base-100 rounded-md !mt-1 w-36" tabindex="0">
|
||||
@ -75,7 +75,9 @@
|
||||
</details>
|
||||
</li>
|
||||
<li class="bg-base-100 rounded-md p-0">
|
||||
<button class="h-[27px] pt-[4px] text-base" @click="logout()">Logout</button>
|
||||
<button class="h-[27px] pt-[4px] text-base" @click="logout()">
|
||||
{{ $t('button.logout') }}
|
||||
</button>
|
||||
</li>
|
||||
<li class="p-0">
|
||||
<label class="swap swap-rotate">
|
||||
@ -91,18 +93,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
const colorMode = useColorMode()
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
const router = useRouter()
|
||||
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
const router = useRouter()
|
||||
|
||||
const menuItems = ref([
|
||||
{ name: 'Home', link: '/' },
|
||||
{ name: 'Player', link: '/player' },
|
||||
{ name: 'Media', link: '/media' },
|
||||
{ name: 'Message', link: '/message' },
|
||||
{ name: 'Logging', link: '/logging' },
|
||||
{ name: 'Configure', link: '/configure' },
|
||||
{ name: t('button.home'), link: localePath({ name: 'index' }) },
|
||||
{ name: t('button.player'), link: localePath({ name: 'player' }) },
|
||||
{ name: t('button.media'), link: localePath({ name: 'media' }) },
|
||||
{ name: t('button.message'), link: localePath({ name: 'message' }) },
|
||||
{ name: t('button.logging'), link: localePath({ name: 'logging' }) },
|
||||
{ name: t('button.configure'), link: localePath({ name: 'configure' }) },
|
||||
])
|
||||
|
||||
if (colorMode.value === 'dark') {
|
||||
@ -117,7 +122,7 @@ function closeDropdown($event: any) {
|
||||
|
||||
function logout() {
|
||||
authStore.removeToken()
|
||||
router.push({ path: '/' })
|
||||
router.push(localePath({ name: 'index' }))
|
||||
}
|
||||
|
||||
function selectChannel(index: number) {
|
||||
|
@ -9,19 +9,19 @@
|
||||
</div>
|
||||
<div class="p-4 bg-base-100 flex items-center">
|
||||
<span v-if="sysStat.system.ffp_version">
|
||||
<strong>ffplayout version:</strong>
|
||||
{{ sysStat.system.ffp_version }}
|
||||
<strong>ffplayout:</strong>
|
||||
v{{ sysStat.system.ffp_version }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">CPU</div>
|
||||
<div class="text-xl">{{ $t('system.cpu') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Cores:</strong> {{ sysStat.cpu.cores }}</div>
|
||||
<div><strong>Usage:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
|
||||
<div><strong>{{ $t('system.cores') }}:</strong> {{ sysStat.cpu.cores }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ sysStat.cpu.usage.toFixed(2) }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Load</div>
|
||||
<div class="text-xl">{{ $t('system.load') }}</div>
|
||||
<div class="grid grid-cols-3">
|
||||
<div>{{ sysStat.load.one }}</div>
|
||||
<div>{{ sysStat.load.five }}</div>
|
||||
@ -29,37 +29,37 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Memory</div>
|
||||
<div class="text-xl">{{ $t('system.memory') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Total:</strong> {{ fileSize(sysStat.memory.total) }}</div>
|
||||
<div><strong>Usage:</strong> {{ fileSize(sysStat.memory.used) }}</div>
|
||||
<div><strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.memory.total) }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.memory.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">Swap</div>
|
||||
<div class="text-xl">{{ $t('system.swap') }}</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>Total:</strong> {{ fileSize(sysStat.swap.total) }}</div>
|
||||
<div><strong>Usage:</strong> {{ fileSize(sysStat.swap.used) }}</div>
|
||||
<div><strong>{{ $t('system.total') }}:</strong> {{ fileSize(sysStat.swap.total) }}</div>
|
||||
<div><strong>{{ $t('system.usage') }}:</strong> {{ fileSize(sysStat.swap.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 border border-primary">
|
||||
<div class="text-xl">
|
||||
Network <span v-if="sysStat.network" class="fs-6">{{ sysStat.network?.name }}</span>
|
||||
{{ $t('system.network') }} <span v-if="sysStat.network" class="fs-6">{{ sysStat.network?.name }}</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2">
|
||||
<div><strong>In:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
|
||||
<div><strong>Out:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
|
||||
<div><strong>{{ $t('system.in') }}:</strong> {{ fileSize(sysStat.network?.current_in) }}</div>
|
||||
<div><strong>{{ $t('system.out') }}:</strong> {{ fileSize(sysStat.network?.current_out) }}</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_in) }}</div>
|
||||
<div>{{ fileSize(sysStat.network?.total_out) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="sysStat.storage?.path" class="p-4 border border-primary">
|
||||
<div class="text-xl">Storage</div>
|
||||
<div class="text-xl">{{ $t('system.storage') }}</div>
|
||||
<div v-if="sysStat.storage"><strong>Device:</strong> {{ sysStat.storage?.path }}</div>
|
||||
|
||||
<div class="grid grid-cols-2" v-if="sysStat.storage">
|
||||
<div><strong>Size:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
|
||||
<div><strong>Used:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
|
||||
<div><strong>{{ $t('system.size') }}:</strong> {{ fileSize(sysStat.storage?.total) }}</div>
|
||||
<div><strong>{{ $t('system.used') }}:</strong> {{ fileSize(sysStat.storage?.used) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="col-6 bg-primary p-2 border" />
|
||||
|
17
error.vue
17
error.vue
@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<div class="container d-flex align-items-center justify-content-center">
|
||||
<div class="w-full h-full flex justify-center items-center mt-20">
|
||||
<div v-if="props.error?.statusCode === 404">
|
||||
<h1 class="display-1 text-center">404</h1>
|
||||
<p class="text-center mt-10">Page not found</p>
|
||||
<h1 class="text-center text-6xl">404</h1>
|
||||
<p class="text-center font-bold mt-6">
|
||||
{{ $t('error.notFound') }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="props.error?.statusCode === 500">
|
||||
<h1 class="display-1 text-center">{{ props.error.statusCode }}</h1>
|
||||
<p class="text-center mt-10">Internal server error</p>
|
||||
<h1 class="text-center text-6xl">{{ props.error.statusCode }}</h1>
|
||||
<p class="text-center font-bold mt-6">
|
||||
{{ $t('error.serverError') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
@ -15,6 +19,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { NuxtError } from '#app'
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const props = defineProps({
|
||||
error: Object as () => NuxtError,
|
||||
@ -26,7 +31,7 @@ onMounted(() => {
|
||||
if (statusCode >= 400) {
|
||||
setTimeout(() => {
|
||||
reloadNuxtApp({
|
||||
path: '/',
|
||||
path: localePath({ name: 'index' }),
|
||||
ttl: 1000,
|
||||
})
|
||||
}, 3000)
|
||||
|
44
lang/README.md
Normal file
44
lang/README.md
Normal file
@ -0,0 +1,44 @@
|
||||
## Add Language
|
||||
|
||||
You are very welcome to add more languages! Just copy en-US.js to the correct target Country code and modify the content.
|
||||
|
||||
When you are done with the translation add the filename to [nuxt.config.ts](../nuxt.config.ts), in section:
|
||||
|
||||
```DIFF
|
||||
i18n: {
|
||||
locales: [
|
||||
{
|
||||
code: 'de',
|
||||
name: 'Deutsch',
|
||||
file: 'de-DE.js',
|
||||
},
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English',
|
||||
file: 'en-US.js',
|
||||
},
|
||||
+ {
|
||||
+ code: '<SHORT CODE>',
|
||||
+ name: '<NAME>',
|
||||
+ file: '<CODE>.js',
|
||||
+ },
|
||||
],
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
And also add the new link paths:
|
||||
|
||||
```DIFF
|
||||
i18n: {
|
||||
locales: [...],
|
||||
...
|
||||
pages: {
|
||||
'player': {
|
||||
de: '/wiedergabe',
|
||||
en: '/player',
|
||||
+ <CODE>: /<PATH>,
|
||||
},
|
||||
|
||||
...
|
||||
```
|
40
lang/de-DE.js
Normal file
40
lang/de-DE.js
Normal file
@ -0,0 +1,40 @@
|
||||
export default {
|
||||
alert: {
|
||||
wrongLogin: 'Falsche Anmeldedaten!',
|
||||
},
|
||||
button: {
|
||||
login: 'Anmelden',
|
||||
home: 'Start',
|
||||
player: 'Wiedergabe',
|
||||
media: 'Medien',
|
||||
message: 'Nachrichten',
|
||||
logging: 'Protokollierung',
|
||||
channels: 'Kanäle',
|
||||
configure: 'Einstellungen',
|
||||
logout: 'Abmelden',
|
||||
},
|
||||
error: {
|
||||
notFound: 'Seite nicht gefunden',
|
||||
serverError: 'Interner Server Fehler',
|
||||
},
|
||||
input: {
|
||||
username: 'Benutzername',
|
||||
password: 'Passwort',
|
||||
},
|
||||
system: {
|
||||
cpu: 'CPU',
|
||||
cores: 'Kerne',
|
||||
load: 'Auslastung',
|
||||
memory: 'Arbeitsspeicher',
|
||||
swap: 'Auslagerungsspeicher',
|
||||
total: 'Gesamt',
|
||||
usage: 'Verwendung',
|
||||
network: 'Netzwerk',
|
||||
in: 'Eingehend',
|
||||
out: 'Ausgehend',
|
||||
storage: 'Speicher',
|
||||
device: 'Gerät',
|
||||
size: 'Größe',
|
||||
used: 'Genutzt',
|
||||
},
|
||||
}
|
40
lang/en-US.js
Normal file
40
lang/en-US.js
Normal file
@ -0,0 +1,40 @@
|
||||
export default {
|
||||
alert: {
|
||||
wrongLogin: 'Incorrect login data!',
|
||||
},
|
||||
button: {
|
||||
login: 'Login',
|
||||
home: 'Home',
|
||||
player: 'Player',
|
||||
media: 'Media',
|
||||
message: 'Message',
|
||||
logging: 'Logging',
|
||||
channels: 'Channels',
|
||||
configure: 'Configure',
|
||||
logout: 'Logout',
|
||||
},
|
||||
error: {
|
||||
notFound: 'Page not found',
|
||||
serverError: 'Internal server error',
|
||||
},
|
||||
input: {
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
},
|
||||
system: {
|
||||
cpu: 'CPU',
|
||||
cores: 'Cores',
|
||||
load: 'Load',
|
||||
memory: 'Memory',
|
||||
swap: 'Swap',
|
||||
total: 'Total',
|
||||
usage: 'Usage',
|
||||
network: 'Network',
|
||||
in: 'In',
|
||||
out: 'Out',
|
||||
storage: 'Storage',
|
||||
device: 'Device',
|
||||
size: 'Size',
|
||||
used: 'Used',
|
||||
},
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
export default defineNuxtRouteMiddleware((to, from) => {
|
||||
const auth = useAuth()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
auth.inspectToken()
|
||||
|
||||
if (!auth.isLogin && to.path !== '/') {
|
||||
return navigateTo('/')
|
||||
if (!auth.isLogin && !String(to.name).includes('index_')) {
|
||||
return navigateTo(localePath({ name: 'index' }))
|
||||
}
|
||||
})
|
||||
|
@ -45,7 +45,7 @@ export default defineNuxtConfig({
|
||||
},
|
||||
},
|
||||
|
||||
modules: ['@nuxtjs/color-mode', '@pinia/nuxt', '@vueuse/nuxt', '@nuxtjs/tailwindcss'],
|
||||
modules: ['@nuxtjs/color-mode', '@nuxtjs/i18n', '@pinia/nuxt', '@vueuse/nuxt', '@nuxtjs/tailwindcss'],
|
||||
css: ['@/assets/scss/main.scss'],
|
||||
|
||||
colorMode: {
|
||||
@ -60,6 +60,55 @@ export default defineNuxtConfig({
|
||||
storageKey: 'theme',
|
||||
},
|
||||
|
||||
i18n: {
|
||||
locales: [
|
||||
{
|
||||
code: 'de',
|
||||
name: 'Deutsch',
|
||||
file: 'de-DE.js',
|
||||
},
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English',
|
||||
file: 'en-US.js',
|
||||
},
|
||||
],
|
||||
customRoutes: 'config',
|
||||
pages: {
|
||||
'player': {
|
||||
de: '/wiedergabe',
|
||||
en: '/player',
|
||||
},
|
||||
'media': {
|
||||
de: '/medien',
|
||||
en: '/media',
|
||||
},
|
||||
'message': {
|
||||
de: '/nachrichten',
|
||||
en: '/message',
|
||||
},
|
||||
'logging': {
|
||||
de: '/protokollierung',
|
||||
en: '/logging',
|
||||
},
|
||||
'configure': {
|
||||
de: '/einstellungen',
|
||||
en: '/configure',
|
||||
},
|
||||
},
|
||||
detectBrowserLanguage: {
|
||||
useCookie: true,
|
||||
alwaysRedirect: true,
|
||||
},
|
||||
// debug: true,
|
||||
langDir: 'lang',
|
||||
defaultLocale: 'en',
|
||||
|
||||
compilation: {
|
||||
strictMessage: false,
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
build: {
|
||||
chunkSizeWarningLimit: 800000,
|
||||
|
359
package-lock.json
generated
359
package-lock.json
generated
@ -31,6 +31,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/eslint-config": "^12.0.0",
|
||||
"@nuxtjs/i18n": "^8.3.0",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/video.js": "^7.3.57",
|
||||
@ -1477,6 +1478,180 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/bundle-utils": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-7.5.1.tgz",
|
||||
"integrity": "sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "^9.4.0",
|
||||
"@intlify/shared": "^9.4.0",
|
||||
"acorn": "^8.8.2",
|
||||
"escodegen": "^2.1.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"jsonc-eslint-parser": "^2.3.0",
|
||||
"magic-string": "^0.30.0",
|
||||
"mlly": "^1.2.0",
|
||||
"source-map-js": "^1.0.1",
|
||||
"yaml-eslint-parser": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"petite-vue-i18n": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/bundle-utils/node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@intlify/core": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core/-/core-9.11.1.tgz",
|
||||
"integrity": "sha512-mAWVGArvUgY8jw2WVfBndW77VTqU/JMt36KhZtczYtyw6+W3J4fJ/QeoE2ZHXv14GqR2BCsLGaQfTvVHR2/a/w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "9.11.1",
|
||||
"@intlify/shared": "9.11.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.11.1.tgz",
|
||||
"integrity": "sha512-qWXBBlEA+DC0CsHkfJiQK9ELm11c9I6lDpodY4FoOf99eMas1R6JR4woPhrfAcrtxFHp1UmXWdrQNKDegSW9IA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "9.11.1",
|
||||
"@intlify/shared": "9.11.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/h3": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/h3/-/h3-0.5.0.tgz",
|
||||
"integrity": "sha512-cgfrtD3qu3BPJ47gfZ35J2LJpI64Riic0K8NGgid5ilyPXRQTNY7mXlT/B+HZYQg1hmBxKa5G5HJXyAZ4R2H5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/core": "^9.8.0",
|
||||
"@intlify/utils": "^0.12.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.11.1.tgz",
|
||||
"integrity": "sha512-y/aWx7DkaTKK2qWUw0hVbJpon8+urWXngeqh15DuIXZh6n/V/oPQiO/Ho1hUKbwap6MVMuz0OcnAJvqh3p9YPg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/shared": "9.11.1",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.11.1.tgz",
|
||||
"integrity": "sha512-yuDG82vjgId2oasNRgZ0PKJrF65zlL33MNyITP5itbLcP4AYOR/NcIuD+/DiI+GHXdxASMKJU0ZiITLc6RC+qw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/unplugin-vue-i18n": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-3.0.1.tgz",
|
||||
"integrity": "sha512-q1zJhA/WpoLBzAAuKA5/AEp0e+bMOM10ll/HxT4g1VAw/9JhC4TTobP9KobKH90JMZ4U2daLFlYQfKNd29lpqw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/bundle-utils": "^7.4.0",
|
||||
"@intlify/shared": "^9.4.0",
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"@vue/compiler-sfc": "^3.2.47",
|
||||
"debug": "^4.3.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json5": "^2.2.3",
|
||||
"pathe": "^1.0.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2",
|
||||
"unplugin": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"petite-vue-i18n": "*",
|
||||
"vue-i18n": "*",
|
||||
"vue-i18n-bridge": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"petite-vue-i18n": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-i18n-bridge": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/unplugin-vue-i18n/node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/utils": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/utils/-/utils-0.12.0.tgz",
|
||||
"integrity": "sha512-yCBNcuZQ49iInqmWC2xfW0rgEQyNtCM8C8KcWKTXxyscgUE1+48gjLgZZqP75MjhlApxwph7ZMWLqyABkSgxQA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||
@ -1671,6 +1846,31 @@
|
||||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@miyaneee/rollup-plugin-json5": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@miyaneee/rollup-plugin-json5/-/rollup-plugin-json5-1.2.0.tgz",
|
||||
"integrity": "sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"json5": "^2.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0 || ^2.0.0 || ^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@miyaneee/rollup-plugin-json5/node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@netlify/functions": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-2.6.0.tgz",
|
||||
@ -2532,6 +2732,39 @@
|
||||
"eslint": "^8.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nuxtjs/i18n": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@nuxtjs/i18n/-/i18n-8.3.0.tgz",
|
||||
"integrity": "sha512-/2g4zYwBwHwIVJitu/i5zP73G4F9xH394Uq0RbfOGc34YxscN+B2kMnuPL8XXM9zThdMVj9ctHInQXXtr62CLg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/h3": "^0.5.0",
|
||||
"@intlify/shared": "^9.9.0",
|
||||
"@intlify/unplugin-vue-i18n": "^3.0.1",
|
||||
"@intlify/utils": "^0.12.0",
|
||||
"@miyaneee/rollup-plugin-json5": "^1.1.2",
|
||||
"@nuxt/kit": "^3.10.3",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"debug": "^4.3.4",
|
||||
"defu": "^6.1.2",
|
||||
"estree-walker": "^3.0.3",
|
||||
"is-https": "^4.0.0",
|
||||
"knitwork": "^1.0.0",
|
||||
"magic-string": "^0.30.4",
|
||||
"mlly": "^1.4.2",
|
||||
"pathe": "^1.1.1",
|
||||
"scule": "^1.1.1",
|
||||
"sucrase": "^3.34.0",
|
||||
"ufo": "^1.3.1",
|
||||
"unplugin": "^1.5.0",
|
||||
"vue-i18n": "^9.9.0",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.16.0 || >=16.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nuxtjs/tailwindcss": {
|
||||
"version": "6.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@nuxtjs/tailwindcss/-/tailwindcss-6.11.4.tgz",
|
||||
@ -3077,6 +3310,28 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-yaml": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz",
|
||||
"integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"tosource": "^2.0.0-alpha.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
|
||||
@ -7337,6 +7592,27 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/escodegen": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
||||
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^5.2.0",
|
||||
"esutils": "^2.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
"esgenerate": "bin/esgenerate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.57.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
||||
@ -7828,6 +8104,19 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
||||
@ -9190,6 +9479,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-https": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-https/-/is-https-4.0.0.tgz",
|
||||
"integrity": "sha512-FeMLiqf8E5g6SdiVJsPcNZX8k4h2fBs1wp5Bb6uaNxn58ufK1axBqQZdmAQsqh0t9BuwFObybrdVJh6MKyPlyg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/is-inside-container": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
|
||||
@ -9542,6 +9837,24 @@
|
||||
"json5": "lib/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonc-eslint-parser": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz",
|
||||
"integrity": "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.5.0",
|
||||
"eslint-visitor-keys": "^3.0.0",
|
||||
"espree": "^9.0.0",
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ota-meshi"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonc-parser": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
|
||||
@ -14669,6 +14982,15 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tosource": {
|
||||
"version": "2.0.0-alpha.3",
|
||||
"resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz",
|
||||
"integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
@ -15566,6 +15888,26 @@
|
||||
"eslint": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.11.1.tgz",
|
||||
"integrity": "sha512-S7Xi8DkLQG4xnnbxkxzipJK6CdfLdZkmApn95st89HFGp8LTmTH0Tv+Zw6puhOCZJCFrH73PHo3Ylwd2+Bmdxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "9.11.1",
|
||||
"@intlify/shared": "9.11.1",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-observe-visibility": {
|
||||
"version": "2.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz",
|
||||
@ -15794,6 +16136,23 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml-eslint-parser": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.2.2.tgz",
|
||||
"integrity": "sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"yaml": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ota-meshi"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
|
@ -35,6 +35,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/eslint-config": "^12.0.0",
|
||||
"@nuxtjs/i18n": "^8.3.0",
|
||||
"@nuxtjs/tailwindcss": "^6.11.4",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/video.js": "^7.3.57",
|
||||
|
@ -1,20 +1,42 @@
|
||||
<template>
|
||||
<div class="w-full min-h-screen xs:h-full flex justify-center items-center">
|
||||
<div v-if="authStore.isLogin" class="text-center w-full max-w-[700px] p-5">
|
||||
<div v-if="authStore.isLogin" class="text-center w-full max-w-[800px] p-5">
|
||||
<SystemStats v-if="configStore.configGui.length > 0" />
|
||||
<div class="flex flex-wrap justify-center gap-1 xs:gap-0 xs:join mt-5">
|
||||
<NuxtLink to="/player" class="btn join-item btn-primary">Player</NuxtLink>
|
||||
<NuxtLink to="/media" class="btn join-item btn-primary">Media</NuxtLink>
|
||||
<NuxtLink to="/message" class="btn join-item btn-primary">Message</NuxtLink>
|
||||
<NuxtLink to="/logging" class="btn join-item btn-primary">Logging</NuxtLink>
|
||||
<div class="flex flex-wrap justify-center gap-1 md:gap-0 md:join mt-5">
|
||||
<NuxtLink :to="localePath({ name: 'player' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.player') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'media' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.media') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'message' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.message') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink :to="localePath({ name: 'logging' })" class="btn join-item btn-primary px-2">
|
||||
{{ $t('button.logging') }}
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
v-if="authStore.role.toLowerCase() == 'admin'"
|
||||
to="/configure"
|
||||
class="btn join-item btn-primary"
|
||||
:to="localePath({ name: 'configure' })"
|
||||
class="btn join-item btn-primary px-2"
|
||||
>
|
||||
Configure
|
||||
{{ $t('button.configure') }}
|
||||
</NuxtLink>
|
||||
<button class="btn join-item btn-primary" @click="logout()">Logout</button>
|
||||
<button class="btn join-item btn-primary px-2" @click="logout()">
|
||||
{{ $t('button.logout') }}
|
||||
</button>
|
||||
<select
|
||||
class="select select-primary select-bordered join-item max-w-xs ps-2"
|
||||
v-model="selectedLang"
|
||||
@change="changeLang(selectedLang)"
|
||||
>
|
||||
<option v-for="(loc, index) in locales" :key="index" :value="/* @ts-ignore */ loc.code">
|
||||
{{
|
||||
/* @ts-ignore */
|
||||
loc.name
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
<label class="join-item btn btn-primary swap swap-rotate me-2">
|
||||
<input type="checkbox" @change="toggleDarkTheme" :checked="indexStore.darkMode" />
|
||||
<SvgIcon name="swap-on" classes="w-5 h-5" />
|
||||
@ -29,7 +51,7 @@
|
||||
<input
|
||||
type="text"
|
||||
v-model="formUsername"
|
||||
placeholder="Username"
|
||||
:placeholder="$t('input.username')"
|
||||
class="input input-bordered w-full"
|
||||
required
|
||||
/>
|
||||
@ -37,14 +59,16 @@
|
||||
<input
|
||||
type="password"
|
||||
v-model="formPassword"
|
||||
placeholder="Password"
|
||||
:placeholder="$t('input.password')"
|
||||
class="input input-bordered w-full mt-5"
|
||||
required
|
||||
/>
|
||||
|
||||
<div class="w-full mt-4 grid grid-flow-row-dense grid-cols-12 grid-rows-1 gap-2">
|
||||
<div class="col-span-3">
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{{ $t('button.login') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-span-12 sm:col-span-9">
|
||||
<div
|
||||
@ -64,10 +88,16 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
const colorMode = useColorMode()
|
||||
const { locale, locales, t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
const switchLocalePath = useSwitchLocalePath()
|
||||
const router = useRouter()
|
||||
|
||||
const authStore = useAuth()
|
||||
const configStore = useConfig()
|
||||
const indexStore = useIndex()
|
||||
|
||||
const selectedLang = ref(locale)
|
||||
const formError = ref('')
|
||||
const showLoginError = ref(false)
|
||||
const formUsername = ref('')
|
||||
@ -84,7 +114,7 @@ async function login() {
|
||||
formError.value = ''
|
||||
|
||||
if (status === 401 || status === 400 || status === 403) {
|
||||
formError.value = 'Wrong User/Password!'
|
||||
formError.value = t('alert.wrongLogin')
|
||||
showLoginError.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
@ -115,4 +145,12 @@ async function logout() {
|
||||
formError.value = e as string
|
||||
}
|
||||
}
|
||||
|
||||
async function changeLang(code: string) {
|
||||
const path = switchLocalePath(code)
|
||||
const cookie = useCookie('i18n_redirected')
|
||||
cookie.value = code
|
||||
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user