소스 검색

添加登录注册功能

lex 1 년 전
부모
커밋
a8562f151d

+ 0 - 1
src/views/knowledge-library/wroing-book/index.module.less

@@ -15,7 +15,6 @@
 
   .title {
     position: relative;
-
     z-index: 1;
 
     i {

+ 165 - 0
src/views/layout/code.tsx

@@ -0,0 +1,165 @@
+import { defineComponent, nextTick, onMounted, reactive, watch } from 'vue';
+import styles from './login.module.less';
+import loginLogo from './images/login-logo.png';
+import {
+  Button,
+  CellGroup,
+  Icon,
+  NumberKeyboard,
+  PasswordInput,
+  showToast
+} from 'vant';
+import request from '@/helpers/request';
+import { useRouter } from 'vue-router';
+import { storage } from '@/helpers/storage';
+import { ACCESS_TOKEN } from '@/store/mutation-types';
+import { setLogin } from '@/state';
+
+export default defineComponent({
+  name: 'layout-code',
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    },
+    isRegister: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const router = useRouter();
+    const forms = reactive({
+      smsCode: '',
+      showKeyboard: false,
+      countDownStatus: true,
+      countDownTime: 120, // 倒计时时间
+      countTimer: null as any
+    });
+
+    const onSendSms = async () => {
+      try {
+        await request.post('/edu-oauth/open/sendSms', {
+          requestType: 'form',
+          data: {
+            clientId: 'cooleshow-student',
+            type: props.isRegister ? 'REGISTER' : 'LOGIN',
+            mobile: props.phone
+          }
+        });
+        onCountDown();
+        setTimeout(() => {
+          showToast('验证码已发送');
+        }, 100);
+      } catch {
+        forms.countDownStatus = true;
+      }
+    };
+
+    const onCountDown = () => {
+      forms.countDownStatus = false;
+      forms.countTimer = setInterval(() => {
+        if (forms.countDownTime > 0) {
+          forms.countDownTime--;
+        } else {
+          forms.countDownStatus = true;
+          clearInterval(forms.countTimer);
+        }
+      }, 1000);
+    };
+    const onLogin = async () => {
+      try {
+        const params: any = {
+          username: props.phone,
+          client_id: 'cooleshow-student',
+          client_secret: 'cooleshow-student',
+          password: forms.smsCode,
+          grant_type: 'password',
+          loginType: 'SMS'
+        };
+
+        const { data } = await request.post('/edu-oauth/userlogin', {
+          requestType: 'form',
+          data: {
+            ...params
+          }
+        });
+        storage.set(ACCESS_TOKEN, data.token_type + ' ' + data.access_token);
+
+        const userCash = await request.get('/edu-app/user/getUserInfo', {
+          initRequest: true // 初始化接口
+        });
+        setLogin(userCash.data);
+        router.back();
+
+        emit('confirm');
+      } catch {
+        //
+      }
+    };
+
+    watch(
+      () => forms.smsCode,
+      (val: any) => {
+        if (val && val.length === 6) {
+          onLogin();
+        }
+      }
+    );
+
+    onMounted(() => {
+      nextTick(async () => {
+        await onSendSms();
+      });
+    });
+    return () => (
+      <div class={[styles.login]}>
+        <Icon
+          name="arrow-left"
+          class={styles.arrowLeft}
+          onClick={() => emit('close')}
+        />
+        <div class={styles.loginContainer}>
+          <img src={loginLogo} class={styles.logo} />
+
+          <CellGroup class={styles.container} border={false}>
+            <h2 class={styles['code-title']}>输入验证码</h2>
+            <p class={styles.codePhone}>
+              已发送6位验证码至<span>{props.phone}</span>
+            </p>
+            <PasswordInput
+              value={forms.smsCode}
+              onFocus={() => {
+                forms.showKeyboard = true;
+              }}
+              focused={forms.showKeyboard}
+              length={6}
+              gutter={12}
+            />
+
+            <NumberKeyboard
+              v-model={forms.smsCode}
+              show={forms.showKeyboard}
+              maxlength={6}
+              onBlur={() => {
+                forms.showKeyboard = false;
+              }}></NumberKeyboard>
+
+            <div class={styles.btnWrap}>
+              <Button
+                type="primary"
+                round
+                onClick={onSendSms}
+                class={styles.btnSend}
+                disabled={!forms.countDownStatus}>
+                重新发送
+                {!forms.countDownStatus && <>({forms.countDownTime})</>}
+              </Button>
+            </div>
+          </CellGroup>
+        </div>
+      </div>
+    );
+  }
+});

BIN
src/views/layout/images/bottom_manage_bg.png


BIN
src/views/layout/images/bottom_student_bg.png


BIN
src/views/layout/images/bottom_teacher_bg.png


BIN
src/views/layout/images/icon-checked.png


BIN
src/views/layout/images/icon-password.png


BIN
src/views/layout/images/icon-phone.png


BIN
src/views/layout/images/login-bg.png


BIN
src/views/layout/images/login-bottom-bg.png


BIN
src/views/layout/images/login-logo.png


BIN
src/views/layout/images/login-top-bg.png


BIN
src/views/layout/images/top_bg.png


+ 163 - 60
src/views/layout/login.module.less

@@ -1,84 +1,187 @@
 .login {
+  position: relative;
   min-height: 100vh;
-  padding: 0 48px;
-  background: linear-gradient(to bottom, #01c1b5, #1bcbbf);
+  background: linear-gradient(139deg, #0090F6 0%, #31D3F8 100%);
 
-  .codeText {
-    color: #fff;
+  &::before,
+  &::after {
+    content: ' ';
+    position: absolute;
+    left: 0;
+    width: 100%;
+    height: 100%;
   }
 
-  .logo {
-    display: table;
-    padding-top: 100px;
-    padding-bottom: 90px;
-    width: 160px;
-    height: 45px;
-    margin: 0 auto;
-    img {
-      width: inherit;
-      height: inherit;
+  &::before {
+    top: 0;
+    background: url('./images/login-top-bg.png') no-repeat top center;
+    background-size: contain;
+  }
+
+  &::after {
+    bottom: 0;
+    background: url('./images/login-bottom-bg.png') no-repeat bottom center;
+    background-size: contain;
+  }
+
+  .arrowLeft {
+    position: absolute;
+    z-index: 99;
+    top: 26px;
+    left: 18px;
+    font-weight: bold;
+    font-size: 22px;
+    color: #333333;
+  }
+
+
+  .loginContainer {
+    position: relative;
+    z-index: 1;
+    padding: 75px 13px 20px;
+
+    .container {
+      background: url('./images/login-bg.png') no-repeat center;
+      background-size: contain;
+      padding: 69px 22px;
+      min-height: 395px;
+      max-width: 350px;
+      margin: 0 auto;
     }
   }
-  .container {
-    background-color: transparent;
+
+  .logo {
+    display: block;
+    width: 168px;
+    height: 171px;
+    margin: 0 auto -38px;
   }
 
   .input-group {
-    position: relative;
-    border-radius: 50px;
-    border: 2px solid #fff;
-    margin-bottom: 20px;
-    padding-left: 30px;
-    padding-right: 30px;
-    display: flex;
-    align-items: center;
-    background: transparent;
-    input {
-      flex: 1;
-      font-size: 14px;
-      color: #fff;
-
-      background: transparent;
-      border: none;
-      &::placeholder {
-        color: #fff;
-      }
+    height: 48px;
+    background: #F2F4F8;
+    border-radius: 24px;
+    font-size: 16px;
+
+    &+.input-group {
+      margin-top: 18px;
     }
-    .code-text {
-      position: absolute;
-      right: 0;
-      flex: 1;
-      display: block;
-      width: 94px;
-      text-align: center;
-
-      font-size: 14px;
-      color: #fff;
-      line-height: 30px;
-      height: 30px;
+
+    .iconPhone,
+    .iconPassword {
+      width: 20px;
+      height: 20px;
     }
 
     :global {
-      .van-field__button {
-        border-left: 2px solid #fff;
-        margin-left: 12px;
-        margin-right: -18px;
+      .van-field__left-icon {
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+
+  input::placeholder {
+    color: #A6AFCC;
+  }
+
+  .btnGroup {
+    padding-top: 34px;
+  }
+
+  .primaryButton,
+  .login-change {
+    height: 48px;
+    border-radius: 39px;
+    font-size: 16px;
+    border: none;
+  }
+
+  .primaryButton {
+    background: linear-gradient(73deg, #5BECFF 0%, #259CFE 100%);
+    font-weight: 600;
+    color: #FFFFFF;
+  }
+
+  .protocol {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-top: 22px;
+    font-size: 12px;
+    color: #999999;
+    line-height: 17px;
+
+    .iconChecked {
+      display: inline-block;
+      width: 15px;
+      height: 15px;
+      border-radius: 50%;
+      border: 1px solid #ccc;
+      margin-right: 3px;
+
+      &.active {
+        background: url('./images/icon-checked.png') no-repeat center;
+        background-size: contain;
+        border: none;
       }
     }
+
+    span {
+      color: #1BACF1;
+    }
   }
 
   .login-change {
-    padding-top: 8px;
-    font-size: 14px;
-    color: #fff;
-    float: right;
-    cursor: pointer;
+    margin-top: 14px;
+    background: #F5F7FB;
+    font-weight: 500;
+    color: #496AAC;
   }
 
+  .code-title {
+    text-align: center;
+    font-size: 22px;
+    color: #333333;
+    line-height: 30px;
+  }
+
+  .codePhone {
+    font-size: 16px;
+    color: #ABB3CF;
+    line-height: 22px;
+    text-align: center;
+    padding-top: 28px;
+
+    span {
+      padding-left: 6px;
+      color: #086497;
+    }
+  }
+
+  --van-password-input-background: #F2F4F8;
+  --van-password-input-height: 40px;
+
   :global {
-    .van-button--disabled {
-      opacity: 1;
-      color: rgba(0, 0, 0, 0.25);
+    .van-password-input {
+      margin: 40px 0 0;
+    }
+
+    .van-password-input__security li {
+      border-radius: 6px;
+    }
+  }
+
+  .btnWrap {
+    margin-top: 46px;
+    text-align: center;
+
+    .btnSend {
+      padding: 10px 18px;
+      font-size: 16px;
+      font-weight: 600;
+      color: #FFFFFF;
+      line-height: 22px;
     }
   }
-}
+}

+ 109 - 89
src/views/layout/login.tsx

@@ -1,39 +1,35 @@
 import { defineComponent } from 'vue';
-import { CellGroup, Field, Button, CountDown, showToast } from 'vant';
-import ImgCode from '@/components/m-img-code';
+import { CellGroup, Field, Button, showToast } from 'vant';
 import request from '@/helpers/request';
 import { setLogin, state } from '@/state';
 import { checkPhone } from '@/helpers/utils';
+import loginLogo from './images/login-logo.png';
+import iconPhone from './images/icon-phone.png';
+import iconPassword from './images/icon-password.png';
 import { storage } from '@/helpers/storage';
 import { ACCESS_TOKEN } from '@/store/mutation-types';
 import styles from './login.module.less';
+import MPopup from '@/components/m-popup';
+import Code from './code';
+import router from '@/router';
 
 type loginType = 'PWD' | 'SMS';
 export default defineComponent({
   name: 'layout-login',
   data() {
+    const { isRegister } = this.$route.query;
     return {
+      isRegister: isRegister as any,
       loginType: 'SMS' as loginType,
       username: '',
       password: '',
       smsCode: '',
       countDownStatus: true, // 是否发送验证码
       countDownTime: 1000 * 120, // 倒计时时间
-      // countDownRef: null as any, // 倒计时实例
-      imgCodeStatus: false
+      imgCodeStatus: false,
+      isAgree: true
     };
   },
-  computed: {
-    codeDisable() {
-      let status = true;
-      if (this.loginType === 'PWD') {
-        this.username && this.password && (status = false);
-      } else {
-        this.username && this.smsCode && (status = false);
-      }
-      return status;
-    }
-  },
   mounted() {
     storage.remove(ACCESS_TOKEN);
     this.directNext();
@@ -53,7 +49,9 @@ export default defineComponent({
     },
     async onLogin() {
       try {
-        // let res: any
+        if (!checkPhone(this.username)) {
+          return showToast('请输入正确的手机号码');
+        }
         const forms: any = {
           username: this.username,
           client_id: 'cooleshow-student',
@@ -75,7 +73,6 @@ export default defineComponent({
           initRequest: true // 初始化接口
         });
         setLogin(userCash.data);
-
         this.directNext();
       } catch (e: any) {
         //
@@ -84,21 +81,14 @@ export default defineComponent({
     },
     async onSendCode() {
       // 发送验证码
+      if (!this.isAgree) {
+        return showToast('请阅读并同意以下协议');
+      }
       if (!checkPhone(this.username)) {
         return showToast('请输入正确的手机号码');
       }
       this.imgCodeStatus = true;
     },
-    onCodeSend() {
-      this.countDownStatus = false;
-      this.$nextTick(() => {
-        (this.$refs.countDownRef as any).start();
-      });
-    },
-    onFinished() {
-      this.countDownStatus = true;
-      (this.$refs.countDownRef as any).reset();
-    },
     onChange() {
       if (this.loginType === 'PWD') {
         this.loginType = 'SMS';
@@ -110,78 +100,108 @@ export default defineComponent({
   render() {
     return (
       <div class={[styles.login]}>
-        <div class={styles.logo}>{/* <img src={logo} /> */}</div>
-        <CellGroup class={styles.container} border={false}>
-          <Field
-            v-model={this.username}
-            name="手机号"
-            placeholder="请输入您的手机号"
-            type="tel"
-            class={styles['input-group']}
-            maxlength={11}
-            autocomplete="off"
-          />
+        <div class={styles.loginContainer}>
+          <img src={loginLogo} class={styles.logo} />
 
-          {this.loginType === 'PWD' ? (
-            <Field
-              v-model={this.password}
-              type="password"
-              name="密码"
-              class={styles['input-group']}
-              placeholder="请输入密码"
-              autocomplete="off"
-            />
-          ) : (
+          <CellGroup class={styles.container} border={false}>
             <Field
-              v-model={this.smsCode}
-              name="验证码"
-              placeholder="请输入验证码"
+              v-model={this.username}
+              name="手机号"
+              placeholder="请输入您的手机号"
               type="tel"
               class={styles['input-group']}
-              maxlength={6}
+              maxlength={11}
               autocomplete="off"
-              v-slots={{
-                button: () =>
-                  this.countDownStatus ? (
-                    <span class={styles.codeText} onClick={this.onSendCode}>
-                      获取验证码
-                    </span>
-                  ) : (
-                    <CountDown
-                      ref="countDownRef"
-                      auto-start={false}
-                      time={this.countDownTime}
-                      onFinish={this.onFinished}
-                      format="ss秒"
-                    />
-                  )
+              border={false}>
+              {{
+                'left-icon': () => (
+                  <img src={iconPhone} class={styles.iconPhone} />
+                )
               }}
-            />
-          )}
-        </CellGroup>
-        <div class={styles.margin34}>
-          <Button
-            round
-            block
-            disabled={this.codeDisable}
-            onClick={this.onLogin}>
-            提交
-          </Button>
-          <span class={styles['login-change']} onClick={this.onChange}>
-            {this.loginType === 'PWD' ? '验证码登录' : '密码登录'}
-          </span>
+            </Field>
+
+            {this.loginType === 'PWD' && (
+              <Field
+                v-model={this.password}
+                type="password"
+                name="密码"
+                class={styles['input-group']}
+                placeholder="请输入密码"
+                autocomplete="off"
+                border={false}>
+                {{
+                  'left-icon': () => (
+                    <img src={iconPassword} class={styles.iconPassword} />
+                  )
+                }}
+              </Field>
+            )}
+
+            <div class={styles.btnGroup}>
+              <Button
+                round
+                block
+                class={styles.primaryButton}
+                onClick={() => {
+                  if (this.loginType === 'PWD') {
+                    this.onLogin();
+                  } else {
+                    this.onSendCode();
+                  }
+                }}>
+                {this.loginType === 'PWD' ? '登录' : '获取短信验证码'}
+              </Button>
+
+              <Button
+                round
+                block
+                type="default"
+                class={styles['login-change']}
+                onClick={this.onChange}>
+                {this.loginType === 'PWD' ? '短信登录' : '密码登录'}
+              </Button>
+            </div>
+
+            {this.loginType === 'SMS' && (
+              <div
+                class={styles.protocol}
+                onClick={() => (this.isAgree = !this.isAgree)}>
+                <i
+                  class={[
+                    styles.iconChecked,
+                    this.isAgree ? styles.active : ''
+                  ]}></i>
+                我已阅读并同意
+                <span
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+
+                    router.push('/preview-protocol');
+                  }}>
+                  《用户注册协议》
+                </span>
+                和
+                <span
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+
+                    router.push('/privacy-protocol');
+                  }}>
+                  《隐私政策》
+                </span>
+              </div>
+            )}
+          </CellGroup>
         </div>
 
-        {this.imgCodeStatus ? (
-          <ImgCode
-            v-model:value={this.imgCodeStatus}
+        <MPopup v-model:modelValue={this.imgCodeStatus}>
+          <Code
             phone={this.username}
-            onClose={() => {
-              this.imgCodeStatus = false;
-            }}
-            onSendCode={this.onCodeSend}
+            isRegister={this.isRegister}
+            onClose={() => (this.imgCodeStatus = false)}
+            onConfirm={this.directNext}
           />
-        ) : null}
+        </MPopup>
       </div>
     );
   }