You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

519 lines
19 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. <template>
  2. <div :class="['tw-h-full tw-p-[30px] tw-bg-white md:tw-bg-primary-pale']">
  3. <div
  4. class="xl:tw-grid xl:tw-grid-cols-[auto_414px] xl:tw-gap-[96px] xl:tw-max-w-[1246px] xl:tw-mx-auto xl:tw-px-[60px]">
  5. <div class="tw-hidden xl:tw-block xl:tw-mt-[30px]">
  6. <p class="welcome tw-text-primary-light">{{ $t("Welcome to ShowEasy") }}</p>
  7. <p class="sub tw-text-neutral-400">
  8. {{
  9. $t(
  10. "Your best guide from trip to show and from show to the world."
  11. )
  12. }}
  13. </p>
  14. <img class="mt-11 ml-15" :src="require('@/assets/img/UserLoginMain.png')" />
  15. </div>
  16. <div class="md:tw-w-[414px] md:tw-mx-auto">
  17. <div class="tw-bg-white tw-mt-[30px] md:tw-rounded-[24px] md:tw-pt-[60px] md:tw-pb-[150px] md:tw-px-[30px]">
  18. <h1 class="tw-text-[40px] tw-font-bold tw-mb-[20px] md:tw-text-[24px]">{{ $t('Login') }}</h1>
  19. <p class="tw-body-3 tw-text-neutral-400 tw-mb-[16px] md:tw-mb-[12px]">
  20. {{ $t('Welcome back! Please enter your details') }}
  21. </p>
  22. <p class="tw-body-3 tw-mb-[24px] md:tw-mb-[32px]">{{ $t('Not a member?') }} <nuxt-link
  23. :to="localePath('/user/signup')" class="primary--text text-decoration-none">{{ $t('Sign up') }}</nuxt-link>
  24. </p>
  25. <!-- <div class="tw-grid tw-grid-cols-1 tw-gap-[10px] tw-mb-[30px] md:tw-gap-[12px] md:tw-mb-[12px]">
  26. <button @click="googleLogin"
  27. class="tw-block tw-w-full tw-py-[8px] tw-rounded-[16px] tw-border tw-border-solid tw-text-black tw-border-neutrals-200"><img
  28. width="20px" height="20px" src="~/assets/img/g-normal.png" class="mr-5" />{{ $t('Login with Google')
  29. }}</button>
  30. <button @click="facebookLogin"
  31. class="tw-block tw-w-full tw-py-[8px] tw-rounded-[16px] tw-border tw-border-solid tw-text-black tw-border-neutrals-200"><img
  32. width="20px" height="20px" src="~/assets/img/f_logo_RGB-Blue_72.png" class="mr-5" />
  33. {{ $t("Login with Facebook")
  34. }}</button>
  35. </div>
  36. <div class="tw-grid tw-grid-cols-[auto_16px_auto] tw-gap-[28px] tw-mb-[6px]">
  37. <div class="tw-flex tw-justify-center tw-items-center">
  38. <div class="tw-w-full tw-h-[1px] tw-bg-neutral-200"></div>
  39. </div>
  40. <div class="tw-text-neutral-400 tw-body-3 tw-font-normal">{{ $t("or") }}</div>
  41. <div class="tw-flex tw-justify-center tw-items-center">
  42. <div class="tw-w-full tw-h-[1px] tw-bg-neutral-200"></div>
  43. </div>
  44. </div> -->
  45. <div v-if="serviceExceptionActive" class="warning--text text-size-14 ps-1 mb-3">
  46. {{ $t('Service exception') }}
  47. </div>
  48. <div v-if="wrongMessageActive" class="warning--text text-size-14 ps-1 mb-3">
  49. {{ $t('The username or password entered is incorrect') }}
  50. </div>
  51. <v-form ref="loginFormRef" lazy-validation>
  52. <!-- <div v-if="$auth.$storage.getUniversal('userAccount') && $auth.$storage.getUniversal('userPassword')"></div>
  53. <div v-else></div> -->
  54. <v-text-field
  55. v-model="userData.Account" background-color="neutrals darken-1" :label="this.$t('Email')"
  56. :placeholder="this.$t('Email')" height="40px" filled rounded dense single-line persistent-placeholder
  57. :rules="[rules.email,rules.require]" v-on:input="updateValid"
  58. />
  59. <v-text-field
  60. v-model="userData.Password" background-color="neutrals darken-1" :label="this.$t('Password')"
  61. :type=" showPass ? 'text' : 'password'" :placeholder="this.$t('Password')" filled rounded dense single-line
  62. persistent-placeholder :append-icon=" showPass ? 'mdi-eye' : 'mdi-eye-off'" @click:append="showPass = !showPass"
  63. :rules="[rules.checkPassword,rules.require]" v-on:input="updateValid" @keyup.enter="userLogin"
  64. />
  65. </v-form>
  66. <div class="tw-flex tw-justify-between tw-items-center tw-mb-[28px] md:tw-mb-[32px]">
  67. <div class="tw-flex tw-items-center">
  68. <div class="tw-flex tw-items-center">
  69. <div class="tw-flex tw-flex-col">
  70. <label class="tw-body-4 container-checkbox" for="remember">
  71. {{ $t('Remember me')
  72. }}
  73. <input id="remember" type="checkbox" value="remember" v-model="remember" />
  74. <span class="checkmark"></span>
  75. </label>
  76. </div>
  77. </div>
  78. </div>
  79. <div class="tw-flex tw-items-center">
  80. <nuxt-link class="complementary--text text-decoration-none text-size-14" :to="localePath('/user/forgot')">{{
  81. $t('Forgot Password?') }}</nuxt-link>
  82. </div>
  83. </div>
  84. <div class="md:tw-flex md:tw-justify-center md:tw-items-center"> <button @click="userLogin" :disabled="!valid"
  85. :class="['tw-block tw-w-full tw-py-[10px] tw-rounded-[16px] tw-border tw-border-solid tw-body-3 tw-font-normal tw-transition-all tw-duration-200 tw-ease-in-out',valid?'tw-text-white tw-bg-primary-default tw-border-primary-default':'tw-text-base-disable tw-bg-neutral-100 tw-border-neutral-100']">{{
  86. $t("Login")
  87. }}</button></div>
  88. <!-- 認證會員Modal(連結) -->
  89. <v-dialog v-model="dialog" :width="$vuetify.breakpoint.smAndUp ? 423 : 294"
  90. @click:outside="colseDialog()">
  91. <v-card class="tw-p-[30px]" :height="$vuetify.breakpoint.smAndUp ? 289 : 290">
  92. <v-spacer class="d-flex align-center tw-justify-between tw-mb-[30px]">
  93. <div class="tw-text-[20px] tw-font-bold tw-text-black">
  94. {{ $t("Verify your email") }}
  95. </div>
  96. <v-btn @click="colseDialog()" icon>
  97. <v-icon> mdi-close </v-icon>
  98. </v-btn>
  99. </v-spacer>
  100. <v-spacer class="tw-mb-[40px]">
  101. <div class="tw-text-[16px] tw-text-neutrals-800">
  102. {{ $t("Oops! Seems like you haven't verified your email,please click the button below to resend a verification email.")}}
  103. </div>
  104. </v-spacer>
  105. <button @click="resendVerifyEmail()" :class="['tw-text-white tw-bg-primary-1 hover:tw-bg-primary-light tw-text-[18px] tw-rounded-[16px] tw-px-[77px] tw-py-[13px]']">
  106. {{ $t("Resend verification email") }}
  107. </button>
  108. </v-card>
  109. </v-dialog>
  110. <!-- 重啟帳號Modal -->
  111. <v-dialog v-model="deleteDialog" :width="$vuetify.breakpoint.smAndUp ? 423 : 294"
  112. @click:outside="colseDialog()">
  113. <v-card class="tw-p-[30px]" :height="$vuetify.breakpoint.smAndUp ? 289 : 290">
  114. <v-spacer class="d-flex tw-justify-between align-center tw-mb-[30px]">
  115. <div class="tw-text-[20px] tw-text-black tw-font-bold">
  116. {{ $t("Reactivate your account") }}
  117. </div>
  118. <v-btn @click="colseDialog()" icon>
  119. <v-icon> mdi-close </v-icon>
  120. </v-btn>
  121. </v-spacer>
  122. <v-spacer class="tw-mb-[40px]">
  123. <div class="tw-text-[16px] tw-text-neutrals-800 tw-font-bold">
  124. {{ $t("Welcome back!")}}
  125. </div>
  126. <div class="tw-text-[16px] tw-text-neutrals-800">
  127. {{ $t("Looks like your account has been deactivated.")}}
  128. <br />
  129. {{ $t("Would you like to reactivate this account?")}}
  130. </div>
  131. </v-spacer>
  132. <button @click="ReactivateAccount()" class="tw-bg-primary-1 tw-text-white hover:tw-bg-primary-light tw-text-[18px] tw-rounded-[16px] tw-px-[86px] tw-py-[13px]">
  133. {{ $t("Reactivate my account") }}
  134. </button>
  135. <!-- <v-btn class="primary tw-w-auto" rounded
  136. :disabled="countdown > 0">
  137. <span v-if="countdown > 0">
  138. {{ `(${this.countdown})` }}
  139. </span>
  140. </v-btn> -->
  141. </v-card>
  142. </v-dialog>
  143. <VerifyCode @user-verifyCode="userVerifyCode" @resend-mail="resendVerifyEmail" :error="error" :account="userData.Account"></VerifyCode>
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. </template>
  149. <script>
  150. import { getFirstName } from "~/utils/common";
  151. import VerifyCode from "@/components/newcomponent/modal/VerifyCodeModal.vue";
  152. export default {
  153. name: "login",
  154. layout: "login",
  155. auth: false,
  156. components: {
  157. VerifyCode
  158. },
  159. data() {
  160. return {
  161. timer: null,
  162. showPass: false,
  163. resendDialog: false,
  164. wrongMessageActive: false,
  165. verifyEmailSent: false,
  166. serviceExceptionActive: false,
  167. valid: false,
  168. remember: false,
  169. countdown: 60,
  170. userData: {
  171. Account: '',
  172. Password: '',
  173. },
  174. rules: {
  175. require: value => !!value || this.$t('Required.'),
  176. email: v => /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(v) || this.$t('Invalid email'),
  177. // checkPassword: v => (/(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])/.test(v) && v.length >= 8 && v.length <= 20) || '請輸入密碼',
  178. checkPassword: v => v.length >= 1 || '請輸入密碼',
  179. },
  180. deleteDialog: false,
  181. dialog: false,
  182. expireTime: new Date(),
  183. error: false,
  184. userInfo: {},
  185. }
  186. },
  187. watch: {
  188. remember: {
  189. handler: function () {
  190. if (this.remember == true) {
  191. this.$auth.$storage.setUniversal("userPassword", this.userData.Password);
  192. this.$auth.$storage.setUniversal("userAccount", this.userData.Account);
  193. }else{
  194. this.$auth.$storage.removeUniversal("userPassword");
  195. this.$auth.$storage.removeUniversal("userAccount", this.userData.Account);
  196. }
  197. },
  198. },
  199. },
  200. created(){
  201. if(this.$auth.$storage.getUniversal('userAccount') && this.$auth.$storage.getUniversal('userPassword')){
  202. this.checkExpireTime();
  203. }
  204. },
  205. mounted(){
  206. if(this.$auth.$storage.getUniversal('userAccount') && this.$auth.$storage.getUniversal('userPassword')){
  207. this.remember = true;
  208. }else{
  209. this.remember = false;
  210. }
  211. var userAccount = this.$auth.$storage.getUniversal('userAccount');
  212. var userPassword = this.$auth.$storage.getUniversal('userPassword');
  213. this.userData.Account = userAccount ? userAccount : '';
  214. this.userData.Password = userPassword ? userPassword : '';
  215. if(this.userData.Account !=='' && this.userData.Password !==''){
  216. this.valid = true;
  217. }else{
  218. this.valid = false;
  219. }
  220. },
  221. methods: {
  222. colseDialog(){
  223. this.dialog = false;
  224. this.deleteDialog = false;
  225. },
  226. //登入
  227. async userLogin() {
  228. let that = this;
  229. if (!that.$refs.loginFormRef.validate()) return;
  230. try {
  231. const response =await that.$auth.loginWith('local', { data: that.userData });
  232. that.serviceExceptionActive = false;
  233. if(response.data.STATUSCODE==200){
  234. let data = response.data.DATA;
  235. if(data.authtoken){
  236. const authtoken = data.authtoken;
  237. that.$auth.$storage.removeUniversal('authtoken');
  238. that.$auth.$storage.setUniversal('authtoken', authtoken);
  239. let path = that.$auth.$storage.getUniversal('userBeforePath');
  240. if(path == "/user"){
  241. path = "/";
  242. }
  243. that.$router.push({path: path});
  244. }
  245. }else if(response.data.STATUSCODE==500){
  246. that.$auth.redirect('login');
  247. }else if(response.data.STATUSCODE==401){
  248. if(response.data.MSG == "0"){
  249. this.resendVerifyEmail();
  250. this.$modal.show('VerifyCode');
  251. }else if(response.data.MSG == "2"){
  252. that.deleteDialog = true;
  253. }else if(response.data.MSG == "帳號或密碼不正確"){
  254. that.wrongMessageActive = true;
  255. }
  256. }else{
  257. // that.resendDialog = !that.resendDialog;
  258. that.wrongMessageActive = true;
  259. }
  260. } catch(err) {
  261. console.log(err);
  262. that.serviceExceptionActive = true;
  263. // if(err && err.response && err.response.status){
  264. // err.response.status === 403 ? that.resendDialog = !that.resendDialog : err.response.status === 401 ? that.wrongMessageActive = true : that.resendDialog;
  265. // }
  266. }
  267. },
  268. //重新寄出認證信
  269. resendVerifyEmail() {
  270. this.$axios
  271. .post(
  272. `/trending/api/Signup/ReSendVerifyMail?Email=${this.userData.Account}`
  273. )
  274. .then((response) => {
  275. if(response && response.data && response.data.DATA && response.data.DATA.rel){
  276. let data = response.data.DATA.rel
  277. if(data){
  278. this.userInfo = data
  279. this.$modal.show('VerifyCode');
  280. }
  281. }
  282. })
  283. .catch((error) => {
  284. console.log(error);
  285. });
  286. },
  287. //認證會員
  288. userVerifyCode(value){
  289. this.error = false;
  290. this.$axios
  291. .get(
  292. `/trending/api/Signup/VerifyAccount?OrgID=${this.userInfo.OrgID}&MemberID=${this.userInfo.MemberID}&Code=${value}`
  293. )
  294. .then((response) => {
  295. //console.log(response);
  296. if(response.data.STATUSCODE == 200){
  297. this.$modal.hide('VerifyCode');
  298. this.Login();
  299. }
  300. else{
  301. this.error = true;
  302. }
  303. })
  304. .catch((err) => {
  305. console.log(err);
  306. });
  307. },
  308. //重啟帳號
  309. ReactivateAccount(){
  310. this.$axios
  311. .post(
  312. `/trending/api/Members/ReactivateAccountByAcc?Account=${this.userData.Account}`
  313. )
  314. .then((response) => {
  315. //console.log(JSON.stringify(response))
  316. if(response && response.data && response.data.DATA && response.data.DATA.rel){
  317. let data = response.data.DATA.rel
  318. if(data){
  319. this.deleteDialog = false;
  320. }
  321. }
  322. })
  323. .catch((error) => {
  324. console.log(error);
  325. });
  326. },
  327. updateValid(){
  328. if(this.userData.Account !=='' && this.userData.Password !==''){
  329. this.valid = true;
  330. }else{
  331. this.valid = false;
  332. }
  333. },
  334. checkExpireTime(){
  335. this.$axios
  336. .get(`/trending/api/System/RememberMe?Account=${this.$auth.$storage.getUniversal('userAccount')}`)
  337. .then((response) => {
  338. //console.log(response);
  339. const data = response.data.DATA.authtoken;
  340. this.expireTime = new Date(data.ExpireTime);
  341. const now = new Date(Date.now());
  342. const saveTime = this.expireTime - now;
  343. if(saveTime >= 0){
  344. const hour = saveTime / (60 * 60 * 1000);
  345. if(hour == 0){
  346. this.$auth.$storage.removeUniversal('userPassword');
  347. this.$auth.$storage.removeUniversal('userAccount');
  348. }
  349. }else{
  350. this.$auth.$storage.removeUniversal('userPassword');
  351. this.$auth.$storage.removeUniversal('userAccount');
  352. }
  353. })
  354. .catch((error) => {
  355. console.log(error);
  356. });
  357. }
  358. },
  359. beforeUnmount() {
  360. if(this.timer!=null){
  361. clearInterval(this.timer);
  362. }
  363. },
  364. beforeRouteEnter(to, from, next) {
  365. next(vm => {
  366. if (from.name) {
  367. if (!from.path.includes('user')) {
  368. vm.$auth.$storage.setUniversal("userBeforePath", from.fullPath, true)
  369. }
  370. }
  371. if (vm.$auth.loggedIn) {
  372. vm.$router.push({path: vm.$auth.$storage.getUniversal('userBeforePath') || '/'})
  373. }
  374. else {
  375. next()
  376. }
  377. })
  378. },
  379. }
  380. </script>
  381. <style lang="scss" scoped>
  382. $border-style: 1px solid #E5E5E5;
  383. #app {
  384. overflow-y: hidden;
  385. }
  386. .welcome {
  387. color: #f5cda8;
  388. font-family: Damion;
  389. font-size: 48px;
  390. margin-bottom: 0px;
  391. font-weight: 400;
  392. line-height: 66px;
  393. letter-spacing: 0.02em;
  394. }
  395. .sub {
  396. color: #9c9c9c;
  397. font-style: italic;
  398. font-weight: 400;
  399. font-size: 22px;
  400. line-height: 29px;
  401. letter-spacing: 0.02em;
  402. }
  403. .title {
  404. font-weight: 700;
  405. font-size: 26px;
  406. line-height: 34px;
  407. letter-spacing: 0.02em;
  408. color: #232323;
  409. }
  410. .btn-border {
  411. border: $border-style;
  412. }
  413. .seperator {
  414. border-bottom: $border-style;
  415. text-align: center;
  416. height: 12px;
  417. margin: 20px 0 30px;
  418. }
  419. :deep(.v-text-field.v-text-field--enclosed .v-text-field__details) {
  420. margin-bottom: 0;
  421. padding: 0;
  422. .v-messages {
  423. display: none;
  424. }
  425. .v-messages.error--text {
  426. display: block;
  427. margin-top: 4px;
  428. margin-bottom: 16px;
  429. }
  430. }
  431. /* The container */
  432. .container-checkbox {
  433. display: block;
  434. position: relative;
  435. padding-left: 28px;
  436. cursor: pointer;
  437. -webkit-user-select: none;
  438. -moz-user-select: none;
  439. -ms-user-select: none;
  440. user-select: none;
  441. }
  442. /* Hide the browser's default checkbox */
  443. .container-checkbox input {
  444. position: absolute;
  445. opacity: 0;
  446. cursor: pointer;
  447. }
  448. /* Create a custom checkbox */
  449. .container-checkbox .checkmark {
  450. position: absolute;
  451. top: 0;
  452. left: 0;
  453. height: 18px;
  454. width: 18px;
  455. background-color: transparent;
  456. border: 1px solid #f48800;
  457. border-radius: 4px;
  458. }
  459. /* On mouse-over, add a grey background color */
  460. .container-checkbox:hover input~.checkmark {
  461. border-color: #f48800;
  462. }
  463. /* When the checkbox is checked, add a blue background */
  464. .container-checkbox input:checked~.checkmark {
  465. background-color: #f48800;
  466. border-color: #f48800;
  467. }
  468. /* Create the checkmark/indicator (hidden when not checked) */
  469. .container-checkbox .checkmark:after {
  470. content: "";
  471. position: absolute;
  472. display: none;
  473. }
  474. /* Show the checkmark when checked */
  475. .container-checkbox input:checked~.checkmark:after {
  476. display: block;
  477. }
  478. /* Style the checkmark/indicator */
  479. .container-checkbox .checkmark:after {
  480. left: 5px;
  481. top: 2px;
  482. width: 5px;
  483. height: 10px;
  484. border: solid #ffffff;
  485. border-width: 0 2px 2px 0;
  486. -webkit-transform: rotate(45deg);
  487. -ms-transform: rotate(45deg);
  488. transform: rotate(45deg);
  489. }
  490. </style>