github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/views/PasswordReset/PasswordReset.vue (about) 1 <script setup lang="ts"> 2 import {useI18n} from '@/hooks/web/useI18n' 3 import {underlineToHump} from '@/utils' 4 import {useAppStore} from '@/store/modules/app' 5 import {useDesign} from '@/hooks/web/useDesign' 6 import {reactive, ref, unref} from 'vue' 7 import {FormSchema} from "@/types/form"; 8 import {ElButton, ElCheckbox, ElLink, ElInput} from 'element-plus' 9 import api from "@/api/api"; 10 import {useRouter} from "vue-router"; 11 import {useForm} from "@/hooks/web/useForm"; 12 import {useValidator} from "@/hooks/web/useValidator"; 13 import {Form} from '@/components/Form' 14 15 const {getPrefixCls} = useDesign() 16 const prefixCls = getPrefixCls('login') 17 const appStore = useAppStore() 18 const {t} = useI18n() 19 const {required} = useValidator() 20 const {register, elFormRef, methods} = useForm() 21 const {currentRoute, addRoute, push} = useRouter() 22 23 const newRequestSchema = reactive<FormSchema[]>([ 24 { 25 field: 'title', 26 colProps: { 27 span: 24 28 } 29 }, 30 { 31 field: 'username', 32 label: t('login.username'), 33 value: '', 34 component: 'Input', 35 colProps: { 36 span: 24 37 }, 38 componentProps: { 39 placeholder: t('login.usernamePlaceholder') 40 } 41 }, 42 { 43 field: 'restore', 44 colProps: { 45 span: 24 46 } 47 } 48 ]) 49 const newPasswordSchema = reactive<FormSchema[]>([ 50 { 51 field: 'title', 52 colProps: { 53 span: 24 54 } 55 }, 56 { 57 field: 'password', 58 label: t('login.password'), 59 value: 'admin', 60 component: 'InputPassword', 61 colProps: { 62 span: 24 63 }, 64 componentProps: { 65 style: { 66 width: '100%' 67 }, 68 placeholder: t('login.passwordPlaceholder') 69 } 70 }, 71 { 72 field: 'check_password', 73 label: t('login.checkPassword'), 74 value: '', 75 component: 'InputPassword', 76 colProps: { 77 span: 24 78 }, 79 componentProps: { 80 style: { 81 width: '100%' 82 }, 83 strength: true, 84 placeholder: t('login.passwordPlaceholder') 85 } 86 }, 87 { 88 field: 'update', 89 colProps: { 90 span: 24 91 } 92 } 93 ]) 94 const newRequestRules = { 95 username: [required()], 96 } 97 const newPasswordRules = { 98 password: [required()], 99 check_password: [required()], 100 } 101 102 // NEW_REQUEST 103 // REQUEST_SENDED 104 // UPDATE_PASSWORD 105 const state = ref('NEW_REQUEST') 106 const loading = ref(false) 107 108 if (currentRoute.value.query?.t) { 109 state.value = 'UPDATE_PASSWORD' 110 } 111 112 const toLogin = () => { 113 push('/login') 114 } 115 116 const requestNewPassword = async () => { 117 const formRef = unref(elFormRef) 118 formRef?.validate(async (valid) => { 119 if (valid) { 120 try { 121 loading.value = true 122 const {getFormData} = methods 123 const formData = await getFormData() 124 const {username} = formData 125 const data = await api.v1.authServicePasswordReset({email: username}); 126 state.value = 'REQUEST_SENDED' 127 loading.value = false 128 129 } finally { 130 loading.value = false 131 } 132 } 133 }) 134 } 135 const updatePassword = async () => { 136 const formRef = unref(elFormRef) 137 formRef?.validate(async (valid) => { 138 if (valid) { 139 try { 140 loading.value = true 141 142 const {getFormData} = methods 143 const formData = await getFormData() 144 const {password} = formData 145 const token = currentRoute.value.query.t as string || ''; 146 const data = await api.v1.authServicePasswordReset({newPassword: password, token: token}); 147 148 toLogin() 149 } finally { 150 loading.value = false 151 } 152 } 153 }) 154 } 155 156 </script> 157 158 <template> 159 <div 160 :class="prefixCls" 161 class="h-[100%] relative <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px" 162 > 163 <div class="relative h-full flex mx-auto"> 164 <div 165 :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px <xl:hidden`" 166 > 167 <!-- <div class="flex items-center relative text-white">--> 168 <!-- <img src="../../assets/svgs/logo-w.svg" alt="" class="w-48px h-48px mr-10px"/>--> 169 <!-- <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>--> 170 <!-- </div>--> 171 <div class="flex justify-center items-center h-[calc(100%-60px)]"> 172 <TransitionGroup 173 appear 174 tag="div" 175 enter-active-class="animate__animated animate__bounceInLeft" 176 > 177 <img src="@/assets/svgs/banner-v4.svg" key="1" alt="" class="w-400px" /> 178 <!-- <div class="text-3xl text-white" key="2">{{ t('login.welcome') }}</div>--> 179 <!-- <div class="mt-5 font-normal text-white text-14px" key="3">--> 180 <!-- {{ t('login.message') }}--> 181 <!-- </div>--> 182 </TransitionGroup> 183 </div> 184 </div> 185 <div class="flex-1 p-30px <sm:p-10px dark:bg-v-dark relative"> 186 <div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end"> 187 <div class="flex items-center @2xl:hidden @xl:hidden"> 188 <img src="../../assets/svgs/logo-w.svg" alt="" class="w-48px h-48px mr-10px"/> 189 <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> 190 </div> 191 192 <div class="flex justify-end items-center space-x-10px"> 193 <ThemeSwitch/> 194 <LocaleDropdown class="<xl:text-white dark:text-white"/> 195 </div> 196 </div> 197 198 <Transition appear enter-active-class="animate__animated animate__bounceInRight"> 199 <div 200 class="h-full flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px"> 201 202 <div v-if="state==='REQUEST_SENDED'" class="dark:(border-1 border-[var(--el-border-color)] border-solid)" style="padding: 20px"> 203 <p style="padding-bottom: 20px"> 204 Check your email for a link to reset your password. If it doesn’t appear within a few minutes, check your 205 spam 206 folder. 207 </p> 208 <el-button 209 type="primary" 210 class="w-[100%]" 211 @click="toLogin" 212 > 213 {{ $t('login.returnToSignIn') }} 214 </el-button> 215 </div> 216 <div v-else-if="state==='NEW_REQUEST'" class="dark:(border-1 border-[var(--el-border-color)] border-solid)"> 217 <Form 218 :schema="newRequestSchema" 219 :rules="newRequestRules" 220 label-position="top" 221 hide-required-asterisk 222 size="large" 223 @register="register" 224 style="margin: 20px" 225 > 226 227 <template #title> 228 <h2 class="text-2xl font-bold text-center w-[100%]">{{ t('login.restorePassword') }}</h2> 229 </template> 230 231 <template #code="form"> 232 <div class="w-[100%] flex"> 233 <ElInput v-model="form['code']" :placeholder="t('login.codePlaceholder')"/> 234 </div> 235 </template> 236 237 <template #restore> 238 <div class="w-[100%]"> 239 <ElButton type="primary" class="w-[100%]" :loading="loading" @click="requestNewPassword"> 240 {{ t('login.restorePassword') }} 241 </ElButton> 242 </div> 243 <div class="w-[100%] mt-15px"> 244 <ElButton class="w-[100%]" @click="toLogin"> 245 {{ t('login.hasUser') }} 246 </ElButton> 247 </div> 248 </template> 249 </Form> 250 251 </div> 252 <div v-else-if="state==='UPDATE_PASSWORD'" 253 class="dark:(border-1 border-[var(--el-border-color)] border-solid)"> 254 255 <Form 256 :schema="newPasswordSchema" 257 :rules="newPasswordRules" 258 label-position="top" 259 hide-required-asterisk 260 size="large" 261 @register="register" 262 style="margin: 20px" 263 > 264 265 <template #title> 266 <h2 class="text-2xl font-bold text-center w-[100%]">{{ t('login.updatePassword') }}</h2> 267 </template> 268 269 <template #code="form"> 270 <div class="w-[100%] flex"> 271 <ElInput v-model="form['code']" :placeholder="t('login.codePlaceholder')"/> 272 </div> 273 </template> 274 275 <template #update> 276 <div class="w-[100%]"> 277 <ElButton type="primary" class="w-[100%]" :loading="loading" @click="updatePassword"> 278 {{ t('main.update') }} 279 </ElButton> 280 </div> 281 </template> 282 </Form> 283 284 </div> 285 286 </div> 287 </Transition> 288 289 </div> 290 </div> 291 </div> 292 </template> 293 294 <style lang="less" scoped> 295 @prefix-cls: ~'@{namespace}-login'; 296 297 .@{prefix-cls} { 298 &__left { 299 &::before { 300 position: absolute; 301 top: 0; 302 left: 0; 303 z-index: -1; 304 width: 100%; 305 height: 100%; 306 background-image: url('@/assets/svgs/login-bg.svg'); 307 background-position: center; 308 background-repeat: no-repeat; 309 content: ''; 310 } 311 } 312 } 313 </style>