loginView.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. <script setup lang="ts">
  2. import { useRouter, useRoute } from 'vue-router'
  3. import ErrorTips from './components/ErrorTips.vue'
  4. import { useUserStore } from '@/stores/modules/user'
  5. import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
  6. const router = useRouter()
  7. const route = useRoute()
  8. const loginForm = ref({
  9. username: 'ra.admin',
  10. password: 'abc123456789',
  11. email: '',
  12. code: ''
  13. })
  14. const status = ref(route.query.status || 'login')
  15. watch(status, () => {
  16. loginForm.value = {
  17. username: 'ra.admin',
  18. password: 'abc123456789',
  19. email: '',
  20. code: ''
  21. }
  22. loginError.value = {
  23. username: false,
  24. password: false,
  25. email: false,
  26. code: false
  27. }
  28. verificationCode.value = ''
  29. getCode()
  30. })
  31. const loginError: any = ref({
  32. username: false,
  33. password: false,
  34. email: false,
  35. code: false
  36. })
  37. const verificationCode = ref()
  38. const loading = ref(false)
  39. // 获取验证码
  40. const getCode = () => {
  41. loading.value = true
  42. $api
  43. .getVerifcationCode()
  44. .then((res: any) => {
  45. if (res.code === 200) {
  46. verificationCode.value = `data:image/png;base64,${res.data.imagePngBase64}`
  47. } else {
  48. verificationCode.value = ''
  49. }
  50. })
  51. .finally(() => {
  52. loading.value = false
  53. })
  54. }
  55. getCode()
  56. // 验证当前用户是否存在
  57. const handleCheckUser = () => {
  58. if (!loginForm.value.username) {
  59. return
  60. }
  61. // 这里是验证用户是否存在的逻辑
  62. $api.isUserNameExit({ uname: loginForm.value.username }).then((res: any) => {
  63. if (res.code === 200) {
  64. if (res.data.msg !== 'no_exist') {
  65. isUserNameExit.value = true
  66. } else {
  67. loginError.value.username = true
  68. isUserNameExit.value = false
  69. }
  70. } else {
  71. isUserNameExit.value = false
  72. }
  73. })
  74. }
  75. const userStore = useUserStore()
  76. // 点击登录按钮
  77. const handleLogin = () => {
  78. // 这里是登录逻辑
  79. $api
  80. .login({
  81. uname: loginForm.value.username,
  82. psw: loginForm.value.password,
  83. verifcation_code: loginForm.value.code
  84. })
  85. .then((res: any) => {
  86. if (res.code === 200) {
  87. const { data } = res
  88. if (data.msg === 'today') {
  89. ElMessageBox.alert('Your password will expire today, please reset', 'Prompt', {
  90. confirmButtonText: 'OK',
  91. type: 'warning',
  92. confirmButtonClass: 'el-button--dark'
  93. })
  94. } else if (data.msg === 'last') {
  95. ElMessageBox.alert(
  96. `Your password will expire in${data.data}days, please reset`,
  97. 'Prompt',
  98. {
  99. confirmButtonText: 'OK',
  100. type: 'warning',
  101. confirmButtonClass: 'el-button--dark'
  102. }
  103. )
  104. }
  105. userStore.setUsername(res.data.uname || '')
  106. router.push('/')
  107. } else if (res.code === 400) {
  108. // 验证码错误
  109. if (res.data.msg === 'password_error') {
  110. loginError.value.password = true
  111. } else if (res.data.msg === 'verifcation_error') {
  112. loginError.value.code = true
  113. } else if (res.data.msg === 'error_times') {
  114. errorTipsRef.value.openDialog()
  115. }
  116. }
  117. })
  118. .finally(() => {
  119. getCode()
  120. })
  121. }
  122. const isUserNameExit = ref(false)
  123. const handleForgot = () => {
  124. status.value = 'reset'
  125. isUserNameExit.value = false
  126. handleDeleteEmailTips()
  127. }
  128. const handleSendPassword = () => {
  129. // 这里是发送密码逻辑
  130. $api
  131. .forgotPassword({
  132. login: loginForm.value.username,
  133. email: loginForm.value.email
  134. })
  135. .then((res: any) => {
  136. if (res.code === 200) {
  137. isEmailTips.value = true
  138. }
  139. })
  140. }
  141. const isEmailTips = ref(false)
  142. const handleDeleteEmailTips = (type?: any) => {
  143. isEmailTips.value = false
  144. if (type) {
  145. loginError.value[type] = false
  146. }
  147. }
  148. const errorTipsRef = ref()
  149. const handleChangeStatus = (newStatus: string) => {
  150. status.value = newStatus
  151. }
  152. const test = () => {
  153. status.value = 'changePwd'
  154. router.push('/reset-password')
  155. }
  156. </script>
  157. <template>
  158. <div class="login">
  159. <ScoringSystem class="scoring-system"></ScoringSystem>
  160. <el-button @click="test">测试</el-button>
  161. <el-card class="login-card" v-if="status === 'login'">
  162. <div class="title">
  163. <span class="welcome">Welcome to KLN Portal</span>
  164. <span class="tips">Login to your account</span>
  165. </div>
  166. <div class="send-email-tips" :style="{ display: isEmailTips ? 'block' : 'none' }">
  167. <span class="font_family icon-icon_confirm_b success-icon"></span>
  168. New Password sent to registered email.
  169. <span
  170. @click="handleDeleteEmailTips"
  171. class="font_family icon-icon_reject_b delete-icon"
  172. ></span>
  173. </div>
  174. <div class="login-form">
  175. <div class="label">
  176. <span>User Name</span>
  177. </div>
  178. <el-input
  179. ref="userNameRef"
  180. :class="{ 'is-error': loginError.username }"
  181. v-model="loginForm.username"
  182. class="user-name"
  183. placeholder="Please input user name"
  184. @focus="handleDeleteEmailTips('username')"
  185. @blur="handleCheckUser"
  186. >
  187. <template #prefix>
  188. <span class="font_family icon-icon_username_b"></span>
  189. </template>
  190. <template #suffix>
  191. <span v-if="isUserNameExit" class="font_family icon-icon_confirm_b confirm-icon"></span>
  192. </template>
  193. </el-input>
  194. <div class="error" v-if="loginError.username">This account does not exist.</div>
  195. <div class="label">
  196. <span>Password</span>
  197. <span class="forgot-password" @click="handleForgot">Forgot Password?</span>
  198. </div>
  199. <el-input
  200. ref="passWordRef"
  201. :class="{ 'is-error': loginError.password }"
  202. v-model="loginForm.password"
  203. type="password"
  204. placeholder="Please input password"
  205. show-password
  206. @focus="handleDeleteEmailTips('password')"
  207. ><template #prefix>
  208. <span class="font_family icon-icon_password_b"></span>
  209. </template>
  210. </el-input>
  211. <div class="error" v-if="loginError.password">Incorrect password. Please try again.</div>
  212. <el-input
  213. ref="codeRef"
  214. :class="{ 'is-error': loginError.code }"
  215. class="verification-code"
  216. v-model="loginForm.code"
  217. placeholder="Verification Code"
  218. @focus="handleDeleteEmailTips('code')"
  219. >
  220. <template #append>
  221. <img
  222. v-vloading="loading"
  223. class="verification-code-img"
  224. :src="verificationCode"
  225. alt=""
  226. />
  227. </template>
  228. </el-input>
  229. <div class="error" v-if="loginError.code">Incorrect verification code.</div>
  230. <el-button @click="handleLogin" class="el-button--dark login-btn">Login</el-button>
  231. </div>
  232. <template #footer>
  233. <div class="license">
  234. <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
  235. <span>Version 0.67</span>
  236. </div>
  237. </template>
  238. </el-card>
  239. <el-card class="login-card" v-else-if="status === 'reset'">
  240. <div class="title">
  241. <span class="welcome">Password Retrieval</span>
  242. <span class="tips">We'll send you new password in email</span>
  243. </div>
  244. <div class="login-form">
  245. <div class="label">
  246. <span>User Name</span>
  247. </div>
  248. <el-input
  249. ref="userNameRef"
  250. :class="{ 'is-error': loginError.username }"
  251. v-model="loginForm.username"
  252. class="user-name"
  253. placeholder="Please input user name"
  254. @focus="handleDeleteEmailTips('username')"
  255. @blur="handleCheckUser"
  256. >
  257. <template #prefix>
  258. <span class="font_family icon-icon_username_b"></span>
  259. </template>
  260. <template #suffix>
  261. <span v-if="isUserNameExit" class="font_family icon-icon_confirm_b confirm-icon"></span>
  262. </template>
  263. </el-input>
  264. <div class="error" v-if="loginError.username">This account does not exist</div>
  265. <div class="label">
  266. <span>Email Address</span>
  267. </div>
  268. <el-input
  269. ref="passWordRef"
  270. :class="{ 'is-error': loginError.email }"
  271. v-model="loginForm.email"
  272. placeholder="Please input your email address"
  273. @focus="handleDeleteEmailTips('email')"
  274. ><template #prefix>
  275. <span class="font_family icon-icon_email_b"></span>
  276. </template>
  277. </el-input>
  278. <div class="error" v-if="loginError.email">Incorrect email. Please try again.</div>
  279. <el-input
  280. ref="codeRef"
  281. :class="{ 'is-error': loginError.code }"
  282. class="verification-code"
  283. v-model="loginForm.code"
  284. placeholder="Verification Code"
  285. @focus="handleDeleteEmailTips('code')"
  286. ><template #append>
  287. <img class="verification-code-img" :src="verificationCode" alt="" /> </template
  288. ></el-input>
  289. <div class="error" v-if="loginError.code">Incorrect verification code.</div>
  290. <el-button @click="handleSendPassword" class="el-button--dark login-btn"
  291. >Send Password</el-button
  292. >
  293. <div @click="status = 'login'" class="back-text">
  294. <span class="font_family icon-icon_back_b"></span>
  295. <span class="text"> Back to login</span>
  296. </div>
  297. </div>
  298. <template #footer>
  299. <div class="license">
  300. <span>© 2024 KTreker from <span class="company">Kerry Logistics</span></span>
  301. <span>Version 0.67</span>
  302. </div>
  303. </template>
  304. </el-card>
  305. <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
  306. </div>
  307. </template>
  308. <style lang="scss" scoped>
  309. .login {
  310. position: relative;
  311. display: flex;
  312. justify-content: center;
  313. align-items: center;
  314. height: 100%;
  315. width: 100%;
  316. background: url(../src/image/bg-image.png) no-repeat center center;
  317. background-size: cover;
  318. .scoring-system {
  319. position: absolute;
  320. top: 0;
  321. width: 100%;
  322. background: linear-gradient(251deg, #fff4eb 22.66%, #f0f3ff 44.57%, #e0f7f9 70.46%);
  323. }
  324. }
  325. .login-card {
  326. width: 400px;
  327. .title {
  328. display: flex;
  329. flex-direction: column;
  330. align-items: center;
  331. margin-bottom: 24px;
  332. .welcome {
  333. margin-bottom: 16px;
  334. font-size: 24px;
  335. font-weight: 700;
  336. }
  337. }
  338. .send-email-tips {
  339. display: flex;
  340. align-items: center;
  341. height: 40px;
  342. padding: 12px;
  343. padding-left: 14px;
  344. background-color: #e5f6f1;
  345. border-radius: 6px;
  346. color: var(--color-success);
  347. .success-icon {
  348. display: inline-flex;
  349. align-items: center;
  350. justify-content: center;
  351. height: 16px;
  352. width: 16px;
  353. margin-right: 4px;
  354. font-size: 14px;
  355. color: #fff;
  356. background-color: var(--color-success);
  357. border-radius: 50%;
  358. }
  359. .delete-icon {
  360. margin-left: 6px;
  361. color: var(--color-success);
  362. cursor: pointer;
  363. }
  364. }
  365. :deep(.el-card__body) {
  366. padding: 40px;
  367. padding-bottom: 16px;
  368. }
  369. .login-btn {
  370. width: 100%;
  371. height: 40px;
  372. margin-top: 16px;
  373. }
  374. }
  375. .login-form {
  376. display: flex;
  377. flex-direction: column;
  378. align-items: flex-start;
  379. .el-input {
  380. height: 40px;
  381. .confirm-icon {
  382. display: inline-flex;
  383. align-items: center;
  384. justify-content: center;
  385. height: 16px;
  386. width: 16px;
  387. margin-right: 4px;
  388. font-size: 14px;
  389. color: #fff;
  390. background-color: var(--color-success);
  391. border-radius: 50%;
  392. }
  393. &.is-error {
  394. :deep(.el-input__wrapper) {
  395. box-shadow: 0 0 0 1px var(--color-danger) inset;
  396. }
  397. }
  398. :deep(.el-input__prefix) {
  399. margin: 0 4px;
  400. background-color: transparent;
  401. }
  402. }
  403. .verification-code {
  404. margin-top: 16px;
  405. .verification-code-img {
  406. display: block;
  407. width: 130px;
  408. height: 38px;
  409. object-fit: cover;
  410. }
  411. :deep(.el-input-group__append) {
  412. padding: 0;
  413. padding-right: 1px;
  414. }
  415. }
  416. .el-input.user-name {
  417. :deep(.el-input__wrapper) {
  418. padding-right: 6px;
  419. }
  420. }
  421. .label {
  422. display: flex;
  423. justify-content: space-between;
  424. width: 100%;
  425. margin-top: 16px;
  426. font-size: 12px;
  427. line-height: 18px;
  428. span {
  429. color: var(--color-neutral-2);
  430. }
  431. .forgot-password {
  432. color: var(--color-theme);
  433. cursor: pointer;
  434. }
  435. }
  436. .error {
  437. font-size: 12px;
  438. color: var(--color-danger);
  439. line-height: 14px;
  440. }
  441. .back-text {
  442. width: 100%;
  443. height: 20px;
  444. margin-top: 24px;
  445. margin-bottom: 8px;
  446. text-align: center;
  447. cursor: pointer;
  448. span {
  449. color: var(--color-theme);
  450. }
  451. .text {
  452. display: inline-block;
  453. transform: translateY(-2px);
  454. font-size: 12px;
  455. }
  456. }
  457. }
  458. .license {
  459. display: flex;
  460. flex-direction: column;
  461. align-items: center;
  462. font-size: 12px;
  463. .company {
  464. color: var(--color-theme);
  465. }
  466. span {
  467. color: var(--color-neutral-2);
  468. }
  469. }
  470. </style>