Files
skt-vuetify-templates/src/components/login/LoginForm.vue
T
2026-05-22 10:43:17 +08:00

180 lines
3.8 KiB
Vue

<template>
<v-form @submit.prevent="handleSubmit">
<v-text-field
v-model="username"
bg-color="surface"
class="mb-6 mb-md-4"
color="primary"
density="comfortable"
hide-details
:placeholder="props.accPlaceholder"
variant="outlined"
></v-text-field>
<v-text-field
v-model="password"
:append-inner-icon="showPassword ? mdiEyeOff : mdiEye"
bg-color="surface"
class="mb-6 mb-md-4"
color="primary"
density="comfortable"
hide-details
:placeholder="props.passwPlaceholder"
:type="showPassword ? 'text' : 'password'"
variant="outlined"
@click:append-inner="showPassword = !showPassword"
></v-text-field>
<slot name="verify"></slot>
<div
v-if="props.withRememberAccount || props.withForgotPassword"
class="d-flex align-center justify-space-between mb-6 mb-md-4"
>
<v-checkbox
v-if="props.withRememberAccount"
v-model="rememberMe"
color="primary"
density="compact"
hide-details
:label="props.rememberMeLabel"
></v-checkbox>
<a
v-if="props.withForgotPassword"
class="text-body-2 text-primary text-decoration-none"
:href="props.forgotPasswordHref || '#'"
:target="props.forgotPasswordTarget"
@click="handleForgotPasswordClick"
>
{{ props.forgotPasswordText }}
</a>
</div>
<v-btn
block
class="mb-6 font-weight-bold"
color="primary"
elevation="0"
height="48"
size="large"
type="submit"
>
{{ props.submitText }}
</v-btn>
</v-form>
</template>
<script setup lang="ts">
import { mdiEye, mdiEyeOff } from '@mdi/js'
import { onMounted, ref, watch } from 'vue'
const username = ref('')
const password = ref('')
const showPassword = ref(false)
const rememberMe = ref(false)
const props = defineProps({
passwPlaceholder: {
type: String,
default: '請輸入6位數密碼',
},
accPlaceholder: {
type: String,
default: '請輸入帳號',
},
rememberMeLabel: {
type: String,
default: '記住帳號',
},
forgotPasswordText: {
type: String,
default: '忘記密碼?',
},
forgotPasswordHref: {
type: String,
default: '',
},
forgotPasswordTarget: {
type: String,
default: undefined,
},
submitText: {
type: String,
default: '登入',
},
rememberStorageKey: {
type: String,
default: 'sklogin.remember.username',
},
withRememberAccount: {
type: Boolean,
default: true,
},
withForgotPassword: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['submit', 'forgot-password'])
onMounted(() => {
if (!props.withRememberAccount) return
const saved = localStorage.getItem(props.rememberStorageKey)
if (saved) {
username.value = saved
rememberMe.value = true
}
})
watch([rememberMe, username], ([nextRemember, nextUsername]) => {
if (!props.withRememberAccount) return
if (!nextRemember) {
localStorage.removeItem(props.rememberStorageKey)
return
}
if (!nextUsername) {
localStorage.removeItem(props.rememberStorageKey)
return
}
localStorage.setItem(props.rememberStorageKey, nextUsername)
})
function handleSubmit() {
emit('submit', {
username: username.value,
password: password.value,
rememberMe: props.withRememberAccount ? rememberMe.value : false,
})
}
function handleForgotPasswordClick(e: MouseEvent) {
if (!props.withForgotPassword) return
emit('forgot-password', e)
if (!props.forgotPasswordHref) {
e.preventDefault()
}
}
</script>
<style scoped>
:deep(.v-field--variant-outlined) {
border-radius: 8px;
}
:deep(.v-btn) {
text-transform: none;
border-radius: 8px;
letter-spacing: 0;
}
:deep(.v-checkbox .v-label) {
font-size: 14px;
opacity: 1;
}
</style>