This commit is contained in:
05412 2024-08-02 16:03:30 +08:00
parent 2707070af5
commit db3872cc3a
5 changed files with 59 additions and 27 deletions

12
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "surl-front", "name": "surl-front",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"ky": "^1.5.0",
"pinia": "^2.2.0", "pinia": "^2.2.0",
"vite-plugin-vuetify": "^2.0.3", "vite-plugin-vuetify": "^2.0.3",
"vue": "^3.4.31", "vue": "^3.4.31",
@ -890,6 +891,17 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/ky": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/ky/-/ky-1.5.0.tgz",
"integrity": "sha512-bkQo+UqryW6Zmo/DsixYZE4Z9t2mzvNMhceyIhuMuInb3knm5Q+GNGMKveydJAj+Z6piN1SwI6eR/V0G+Z0BtA==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sindresorhus/ky?sponsor=1"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.10", "version": "0.30.10",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz", "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.10.tgz",

View File

@ -9,6 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"ky": "^1.5.0",
"pinia": "^2.2.0", "pinia": "^2.2.0",
"vite-plugin-vuetify": "^2.0.3", "vite-plugin-vuetify": "^2.0.3",
"vue": "^3.4.31", "vue": "^3.4.31",

View File

@ -59,7 +59,6 @@ function toggleTheme () {
theme.global.name.value = currentTheme theme.global.name.value = currentTheme
localStorage.setItem('theme', currentTheme) localStorage.setItem('theme', currentTheme)
} }
</script> </script>
<template> <template>
@ -67,21 +66,29 @@ function toggleTheme () {
<v-app> <v-app>
<v-app-bar> <v-app-bar>
<v-container class="mx-auto d-flex align-center justify-start"> <v-container class="mx-auto d-flex align-center justify-start">
<v-btn class="home-menu-button" :text="t('$vuetify.menu.home')" to="/" :active="false" prepend-icon="mdi-home"></v-btn> <v-btn class="home-menu-button" :text="t('$vuetify.menu.home')" to="/" :active="false"
<v-btn class="home-menu-button" :text="t('$vuetify.menu.services')" to="/services" :active="false"></v-btn> prepend-icon="mdi-home"></v-btn>
<v-btn class="home-menu-button" :text="t('$vuetify.menu.services')" to="/services" :active="false"
prepend-icon="mdi-database"></v-btn>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-text-field class="home-search-box" density="compact" :label="t('$vuetify.menu.search')" rounded="lg" <v-text-field class="home-search-box" density="compact" :label="t('$vuetify.menu.search')" rounded="lg"
variant="solo-filled" variant="solo-filled"
flat hide-details single-line append-inner-icon="mdi-magnify"></v-text-field> flat hide-details single-line append-inner-icon="mdi-magnify"></v-text-field>
<v-btn icon="mdi-theme-light-dark" class="ml-10" @click="toggleTheme"></v-btn> <v-btn icon="mdi-theme-light-dark" class="ml-10" @click="toggleTheme"
<v-btn icon="mdi-translate" class="ml-2" @click="toogleLocale"></v-btn> v-tooltip="t('$vuetify.menu.themeSwitchTitle')"></v-btn>
<v-btn icon="mdi-translate" class="ml-2" @click="toogleLocale" v-tooltip="t('$vuetify.menu.languageSwitchTitle')"></v-btn>
<v-badge dot color="red">
<v-btn icon="mdi-account" class="ml-2" id="avatar"></v-btn> <v-btn icon="mdi-account" class="ml-2" id="avatar"></v-btn>
</v-badge>
<v-menu open-on-hover activator="#avatar" open-delay="0" close-delay="500" location="bottom" width="150"> <v-menu open-on-hover activator="#avatar" open-delay="0" close-delay="500" location="bottom" width="150">
<v-list> <v-list>
<v-list-item value="1" @click="showLoginDialog = true; isLoginFaild = false" v-if="!userinfoStore.isLogin" append-icon="mdi-login"> <v-list-item value="1" @click="showLoginDialog = true; isLoginFaild = false" v-if="!userinfoStore.isLogin"
append-icon="mdi-login">
<v-list-item-title>{{ t('$vuetify.user.login') }}</v-list-item-title> <v-list-item-title>{{ t('$vuetify.user.login') }}</v-list-item-title>
</v-list-item> </v-list-item>
<v-list-item value="2" :active="false" @click="logout" v-if="userinfoStore.isLogin" append-icon="mdi-logout"> <v-list-item value="2" :active="false" @click="logout" v-if="userinfoStore.isLogin"
append-icon="mdi-logout">
<v-list-item-title>{{ t('$vuetify.user.logout') }}</v-list-item-title> <v-list-item-title>{{ t('$vuetify.user.logout') }}</v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
@ -91,32 +98,39 @@ function toggleTheme () {
<v-main class="px-10"> <v-main class="px-10">
<router-view/> <router-view/>
</v-main> </v-main>
<v-footer>footer</v-footer>
</v-app> </v-app>
</v-responsive> </v-responsive>
<v-dialog v-model="showLoginDialog" persistent width="400"> <v-dialog v-model="showLoginDialog" persistent width="400">
<v-card prepend-icon="mdi-login" :title="t('$vuetify.login.title')" class="px-10"> <v-card prepend-icon="mdi-login" :title="t('$vuetify.login.title')" class="px-10">
<v-text-field class="login-dialong-input" flat prepend-inner-icon="mdi-account-outline" :label="t('$vuetify.login.username')" <v-text-field class="login-dialong-input" flat prepend-inner-icon="mdi-account-outline"
:label="t('$vuetify.login.username')"
density="compact" @input="resetLoginError" density="compact" @input="resetLoginError"
hide-details single-line rounded="lg" variant="solo-filled" v-model="username"></v-text-field> hide-details single-line rounded="lg" variant="solo-filled" v-model="username"></v-text-field>
<v-text-field class="login-dialong-input" flat prepend-inner-icon="mdi-lock-outline" :label="t('$vuetify.login.password')" <v-text-field class="login-dialong-input" flat prepend-inner-icon="mdi-lock-outline"
:label="t('$vuetify.login.password')"
density="compact" hide-details single-line v-model="password" rounded="lg" variant="solo-filled" density="compact" hide-details single-line v-model="password" rounded="lg" variant="solo-filled"
:type="passwordVisibleState ? 'text' :'password'" title="password" @input="resetLoginError" :type="passwordVisibleState ? 'text' :'password'" title="password" @input="resetLoginError"
:append-inner-icon="passwordVisibleState ? 'mdi-eye-off' : 'mdi-eye'" :append-inner-icon="passwordVisibleState ? 'mdi-eye-off' : 'mdi-eye'"
@click:append-inner="passwordVisibleState = !passwordVisibleState"></v-text-field> @click:append-inner="passwordVisibleState = !passwordVisibleState"></v-text-field>
<v-alert class="text-medium-emphasis text-caption mb-2" type="warning" icon="mdi-alert" closable <v-alert class="text-medium-emphasis text-caption mb-2" type="warning" icon="mdi-alert" closable
v-model="isLoginFaild">{{ t('$vuetify.login.failedNote') }}</v-alert> v-model="isLoginFaild">{{ t('$vuetify.login.failedNote') }}
</v-alert>
<v-card color="surface-variant mt-5" variant="tonal"> <v-card color="surface-variant mt-5" variant="tonal">
<v-card-text class="text-medium-emphasis text-caption"> <v-card-text class="text-medium-emphasis text-caption">
{{ t('$vuetify.login.agreement') }} {{ t('$vuetify.login.agreement') }}
<v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.login.termsOfService') }}</a></v-label> <v-label class="text-caption"><a href="/input" target="_blank">{{ t('$vuetify.login.termsOfService') }}</a>
</v-label>
</v-card-text> </v-card-text>
</v-card> </v-card>
<v-checkbox class="login-dialong-input" :label="t('$vuetify.login.rememberMe')" hide-details color="primary"></v-checkbox> <v-checkbox class="login-dialong-input" :label="t('$vuetify.login.rememberMe')" hide-details
color="primary"></v-checkbox>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn :text="t('$vuetify.login.button.cancel')" @click="showLoginDialog = false"></v-btn> <v-btn :text="t('$vuetify.login.button.cancel')" @click="showLoginDialog = false"></v-btn>
<v-btn :loading="loading" color="primary" :text="t('$vuetify.login.button.login')" @click="tryLogin" :icon="loginIcon" <v-btn :loading="loading" color="primary" :text="t('$vuetify.login.button.login')" @click="tryLogin"
:icon="loginIcon"
:disabled="loginDisabled"> :disabled="loginDisabled">
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
@ -134,6 +148,6 @@ function toggleTheme () {
} }
.home-menu-button { .home-menu-button {
width: 100px; width: 120px;
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import {request} from "../../script/functions.ts"; import {request} from "../../script/functions.ts";
import {ref} from "vue"; import {ref, watch} from "vue";
interface Urls { interface Urls {
key: string, key: string,
@ -14,14 +14,12 @@ interface Surls {
} }
const urls = ref<Urls[]>([]) const urls = ref<Urls[]>([])
const selected = ref([])
watch(selected, () => console.log(selected.value))
async function getSurl() { async function getSurl() {
const requestPromise = request('api/surl/get', { const requestPromise = request('api/surl/get')
method: 'GET',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token') || ''
}
})
try { try {
const request = await requestPromise const request = await requestPromise
const body: Surls = await request.json() const body: Surls = await request.json()
@ -34,8 +32,9 @@ async function getSurl() {
<template> <template>
<v-btn @click="getSurl">get</v-btn> <v-btn @click="getSurl">get</v-btn>
<v-data-table :items="urls" <v-data-table :items="urls" :item-value="item => item" show-select v-model="selected"
:headers="[{title: 'KEY', value: 'key', key: 'key'}, {title: 'URL', value: 'value', key: 'value'}]"> v-slot:header.data-table-select height="400" fixed-header
:headers="[{title: 'KEY', value: 'key', key: 'key', align: 'center'}, {title: 'URL', value: 'value', key: 'value'}]">
</v-data-table> </v-data-table>
</template> </template>

View File

@ -16,12 +16,18 @@ interface Msg {
value?: any value?: any
} }
async function request(path: string, init: RequestInit): Promise<Response> { async function request(path: string, init: RequestInit = {}): Promise<Response> {
const useSiteConfig = useSiteConfigStore() const useSiteConfig = useSiteConfigStore()
const siteConfig = useSiteConfig.siteConfig const siteConfig = useSiteConfig.siteConfig
const protocol = siteConfig.protocol const protocol = siteConfig.protocol
const host = siteConfig.host const host = siteConfig.host
return fetch(`${protocol}://${host}/${path}`, init) return fetch(`${protocol}://${host}/${path}`, {
...init,
headers: {
...init.headers,
'Authorization': `Bearer ${useUserinfoStore().token}`
}
})
} }
async function login(username: string, password: string): Promise<boolean> { async function login(username: string, password: string): Promise<boolean> {