Explorar o código

feat: 用户端注册页面调整

周玉环 hai 1 semana
pai
achega
fe3b4970ca

+ 20 - 1
xinkeaboard-web/assets/language/en.js

@@ -36,7 +36,7 @@ export const lang_en = {
     '忘记密码': 'Forgot password',
     '请输入账号/手机号': 'Please enter the account number / mobile phone number',
     '请输入手机号': 'Please enter phone number',
-    '请输入验证码': 'please enter verification code',
+    '请输入验证码': 'Please enter verification code',
     '获取验证码': 'get verification code',
     's后获取': 'After obtaining s',
     '请输入正确的账号/手机号': 'Please enter the correct account number / mobile phone number',
@@ -817,4 +817,23 @@ export const lang_en = {
     '确定要清空吗?':'Are you sure you want to empty?',
     '想得到报价吗?':'Want to get quotations?',
 
+    // 注册页面
+    register: {
+        '获取验证码': 'Get Code',
+        '重新获取': 'Reacquire',
+        '滑动验证': 'Slide to Verify',
+        '请输入邮箱验证码': 'Please enter the mailbox codes',
+        '验证码已发送': 'The verification code has been sent',
+        '校验不通过': 'The verification fails',
+        '注册成功': 'Registration Successful',
+        '我们将向您的邮箱发送验证码,该邮件将用作登录用户名': 'We will send a verification code to your email address, which will be used as your login username',
+        '请确认密码': 'Please confirm the password'
+    },
+
+    // 人机验证
+    humanMachineVerify: {
+        '拖动滑块来填充拼图': "Drag the slider to fill the puzzle",
+        '刷新': 'Refresh'
+    }
+
 };

+ 18 - 0
xinkeaboard-web/assets/language/zh.js

@@ -844,4 +844,22 @@ export const lang_zn = {
     '确定要清空吗?':'确定要清空吗?',
     '想得到报价吗?':'想得到报价吗?',
 
+    // 注册页面
+    register: {
+      '获取验证码': '获取验证码',
+      '重新获取': '重新获取',
+      '滑动验证': '滑动验证',
+      '请输入邮箱验证码': '请输入邮箱验证码',
+      '验证码已发送': '验证码已发送',
+      '校验不通过': '校验不通过',
+      '注册成功': '注册成功',
+      '我们将向您的邮箱发送验证码,该邮件将用作登录用户名': '我们将向您的邮箱发送验证码,该邮件将用作登录用户名',
+      '请确认密码': '请确认密码'
+    },
+
+    // 人机验证
+    humanMachineVerify: {
+        '拖动滑块来填充拼图': "拖动滑块来填充拼图",
+        '刷新': '刷新'
+    }
 };

+ 1 - 187
xinkeaboard-web/assets/style/register.scss

@@ -34,7 +34,7 @@
         font-size: 14px;
         width: 50%;
         text-align: right;
-        .go_register_btn {
+        .go_login_btn {
           font-size: 13px;
           display: inline-block;
           padding: 6px 22px;
@@ -96,192 +96,6 @@
           }
         }
 
-        .center {
-          padding: 30px 30px 40px;
-
-          .item {
-            position: relative;
-            margin-top: 15px;
-            border-radius: 2px;
-
-            &:first-child {
-              margin-top: 0;
-            }
-
-            .icon {
-              position: absolute;
-              left: 1px;
-              top: 1px;
-              width: 50px;
-              text-align: center;
-              height: 38px;
-              background: #f8f8f8;
-
-              .input {
-                border: 1px solid #e8e8e8;
-                height: 40px;
-                padding: 0 44px 0 60px;
-                width: 326px;
-              }
-            }
-
-            .input {
-              border: 1px solid #e8e8e8;
-              height: 40px;
-              padding: 0 44px 0 60px;
-              width: 326px;
-            }
-
-            &.code {
-              .input {
-                padding-right: 10px;
-                width: 150px;
-              }
-            }
-          }
-
-          .login-verify {
-              height: 220px;
-              margin-top: 10px;
-          }
-
-          .cancel {
-            position: absolute;
-            right: 0;
-            top: 1px;
-            width: 44px;
-            height: 38px;
-            cursor: pointer;
-
-            :before {
-              position: absolute;
-              top: 9px;
-              left: 14px;
-            }
-          }
-
-          .send_code {
-            position: absolute;
-            right: 0;
-            top: 0;
-            background: #f9f9f9;
-            border: 1px solid #eee;
-            border-left: 0;
-            width: 80px;
-            height: 40px;
-            line-height: 40px;
-            text-align: center;
-            color: #000;
-
-            :hover {
-              color: $colorMain;
-            }
-          }
-
-          .img_code {
-            position: absolute;
-            right: 0;
-            top: 0;
-            border: 1px solid #eee;
-            border-left: 0;
-            width: 80px;
-            height: 40px;
-          }
-
-          .error {
-            margin-top: 10px;
-            position: relative;
-            color: $colorMain;
-            height: 16px;
-            line-height: 16px;
-          }
-
-          .login_btn {
-            display: block;
-            margin-top: 35px;
-            background: $colorMain;
-            color: #fff;
-            text-align: center;
-            border-radius: 2px;
-            height: 45px;
-            line-height: 45px;
-            font-size: 18px;
-            letter-spacing: 0px;
-
-            &:hover {
-              opacity: 0.9;
-            }
-          }
-
-          .agree_wrap {
-            margin-top: 11px;
-            height: 14px;
-            line-height: 14px;
-            color: #999;
-            padding-left: 2px;
-            position: relative;
-            cursor: pointer;
-            .agree_selected {
-              color: #fff;
-              position: absolute;
-              top: 1px;
-              left: 3px;
-              z-index: 2;
-              font-size: 13px;
-            }
-
-            .checkbox {
-              width: 14px;
-              height: 14px;
-              display: inline-block;
-              vertical-align: top;
-              position: relative;
-              outline: none;
-              -webkit-appearance: none;
-              background: none;
-              border: none;
-              box-sizing: border-box;
-              cursor: pointer;
-              box-shadow: none;
-
-              &.checked {
-                &:before {
-                  background: $colorMain;
-                  border-color: $colorMain;
-                }
-              }
-
-              &:before {
-                border: 1px solid #ddd;
-                background: #fff;
-                z-index: 1;
-                position: absolute;
-                top: 0;
-                left: 0;
-                content: " ";
-                display: block;
-                width: 100%;
-                height: 100%;
-                box-sizing: border-box;
-              }
-            }
-
-            .text {
-              margin-left: 5px;
-              display: inline-block;
-              vertical-align: top;
-
-              .agreement {
-                color: #000;
-
-                &:hover {
-                  text-decoration: underline;
-                }
-              }
-            }
-          }
-        }
-
         .bottom {
           height: 51px;
           background: #fcfcfc;

+ 6 - 2
xinkeaboard-web/components/NavTopBar.vue

@@ -16,7 +16,7 @@
           >
         </div>
 
-        <div v-show="!loginFlag">
+        <div v-show="!loginFlag && !isRegisterStatus">
           <span class="register h1" @click="goToByPush('/login')" style="border-right: 1px solid #c1c1c1; padding-right: 15px;">{{
             L["登录"]
           }}</span>
@@ -87,17 +87,21 @@
 </template>
 
 <script setup>
-import { reactive } from "vue";
+import { reactive, computed } from "vue";
 // import { lang_zn } from "@/assets/language/zh";
 import { useFiltersStore } from "@/store/filter.js";
 import { getCurLanguage } from "@/composables/common.js";
 const router = useRouter();
+const route = useRoute()
 // const L = lang_zn;
 const filtersStore = useFiltersStore();
 const loginFlag = filtersStore.getLoginFlag;
 const memberInfo = filtersStore.getMemberInfo;
 const L = getCurLanguage();
 
+// 判断当前页面是否为注册页面
+const isRegisterStatus = computed(() => route.name === 'register')
+
 // 获取系统配置信息
 const configInfo = reactive({ data: {} });
 const getSystemConfigInfo = async () => {

+ 90 - 10
xinkeaboard-web/components/SliderVerify.vue

@@ -2,12 +2,18 @@
   <div class="verify-content">
     <slide-verify
       ref="block"
+      v-bind="props.slideVerifyOptions"
       :slider-text="sliderText"
       @again="onAgain"
       @success="onSuccess"
       @fail="onFail"
-      @refresh="onRefresh"
     ></slide-verify>
+    <div class="verify-content-action">
+      <span class="verify-content-action__refresh" @click="refresh">
+        <img src="/refresh.png" />
+        <span>{{ L["humanMachineVerify"]["刷新"] }}</span>
+      </span>
+    </div>
   </div>
 </template>
 <script lang="ts" setup>
@@ -17,23 +23,34 @@ import type { SlideVerifyInstance } from "vue3-slide-verify";
 import { getCurLanguage } from "@/composables/common.js";
 import "vue3-slide-verify/dist/style.css";
 
+const props = defineProps({
+  slideVerifyOptions: {
+    type: Object,
+    default: () => {},
+  },
+});
+
+const emits = defineEmits(["onSuccess", "onFail"]);
 
 const block = ref<SlideVerifyInstance>();
 const L = getCurLanguage();
 
-
-const sliderText = computed(() => L["向右滑动"])
+const sliderText = computed(
+  () => L["humanMachineVerify"]["拖动滑块来填充拼图"]
+);
 
 const onSuccess = () => {
-  // TODO: 根据权限跳转
+  emits("onSuccess");
 };
 
-const onFail = () => {};
-
-const onRefresh = () => {
+const refresh = () => {
   block.value?.refresh();
 };
 
+const onFail = () => {
+  emits("onFail");
+};
+
 const onAgain = () => {
   // 刷新
   block.value?.refresh();
@@ -42,11 +59,74 @@ const onAgain = () => {
 <style lang="scss" scoped>
 .verify-content {
   position: relative;
-  overflow: hidden;
   height: 100%;
 
-  .slide-verify {
-    // width: 100% !important;
+  :deep(.slide-verify) {
+    canvas {
+      border-radius: 20px;
+    }
+
+    .slide-verify-slider {
+      border-radius: 25px;
+      height: 50px;
+      line-height: 50px;
+      box-sizing: content-box;
+    }
+
+    .slide-verify-slider-mask {
+      width: 50px;
+      height: 50px;
+      border-radius: 25px;
+      background-color: $colorMain;
+      border-color: transparent;
+    }
+
+    .slide-verify-slider-mask-item {
+      width: 50px;
+      height: 50px;
+      border-radius: 100%;
+
+      &:hover {
+        background: #fff;
+
+        .iconfont {
+          color: #303030;
+        }
+      }
+    }
+
+    .slide-verify-slider-text {
+      color: #666;
+    }
+
+    .container-success {
+      .slide-verify-slider-mask-item {
+        background-color: $colorMain !important;
+
+        .iconfont {
+          color: #fff;
+        }
+      }
+    }
+  }
+
+  &-action {
+    height: 30px;
+    margin-top: 20px;
+    &__refresh {
+      display: flex;
+      // justify-content: center;
+      align-items: center;
+      cursor: pointer;
+      img {
+        width: 20px;
+        margin-right: 5px;
+      }
+
+      span {
+        font-weight: bold;
+      }
+    }
   }
 }
 </style>

+ 322 - 0
xinkeaboard-web/components/register/RegisterAccount.vue

@@ -0,0 +1,322 @@
+<template>
+  <div class="center">
+    <div class="item">
+      <span
+        style="color: #bbb; font-size: 21px; padding-top: 7px"
+        class="icon iconfont icon-wode"
+      ></span>
+      <input
+        type="text"
+        v-model="name"
+        :placeholder="L['请输入用户名']"
+        class="input"
+      />
+      <div data-type="userName" class="cancel" @click="clearInputVal">
+        <span style="color: #bbb" class="iconfont icon-cuowu"></span>
+      </div>
+      <div class="error" v-if="nameErrorMsg">
+        <span
+          style="color: #e1251b; font-size: 14px"
+          class="iconfont icon-jubao"
+        ></span>
+        {{ nameErrorMsg }}
+      </div>
+    </div>
+    <div class="item password">
+      <span
+        style="color: #bbb; font-size: 21px; padding-top: 7px"
+        class="icon iconfont icon-mima1"
+      ></span>
+      <input
+        :type="showPwdFlag ? 'text' : 'password'"
+        v-model="password"
+        :placeholder="L['请输入6~20位英文、数字、符号']"
+        class="input"
+      />
+      <div class="cancel" @click="isShowPwd">
+        <span
+          :style="{
+            color: '#bbb',
+            fontSize: showPwdFlag ? '20px' : '16px',
+          }"
+          :class="{
+            iconfont: true,
+            'icon-bukejian11': !showPwdFlag,
+            'icon-kejian': showPwdFlag,
+            show_pwd: showPwdFlag,
+          }"
+        ></span>
+      </div>
+    </div>
+    <div class="item confirm">
+      <span
+        style="color: #bbb; font-size: 21px; padding-top: 7px"
+        class="icon iconfont icon-mima1"
+      ></span>
+      <input
+        :type="showConfirmPwdFlag ? 'text' : 'password'"
+        v-model="confirmPassword"
+        :placeholder="L['register']['请确认密码']"
+        class="input"
+      />
+      <div class="cancel" @click="isShowConfirmPwd">
+        <span
+          :style="{
+            color: '#bbb',
+            fontSize: showConfirmPwdFlag ? '20px' : '16px',
+          }"
+          :class="{
+            iconfont: true,
+            'icon-bukejian11': !showConfirmPwdFlag,
+            'icon-kejian': showConfirmPwdFlag,
+            show_pwd: showConfirmPwdFlag,
+          }"
+        ></span>
+      </div>
+    </div>
+    <a
+      href="javascript:void(0)"
+      @click="joinForFree"
+      :class="{ submit: true, disabled: joinForFreeDisabled }"
+      >{{ L["去注册"] }}</a
+    >
+    <div class="agree_wrap">
+      <input
+        type="checkbox"
+        :class="{ checkbox: true, default: true, checked: agreeFlag }"
+      />
+      <span class="agree_selected iconfont icon-finish" @click="agree" />
+      <span class="text">
+        {{ L["我同意"]
+        }}<nuxt-link
+          target="_blank"
+          class="agreement"
+          :to="`/member/login/agreement?type=1`"
+        >
+          {{ L["《用户注册协议》"] }}</nuxt-link
+        >
+        <nuxt-link
+          target="_blank"
+          class="agreement"
+          :to="`/member/login/agreement?type=2`"
+          >{{ L["《隐私政策》"] }}
+        </nuxt-link>
+      </span>
+    </div>
+    <div class="error" v-if="agreeErrorMsg">
+      <span
+        style="color: #e1251b; font-size: 14px"
+        class="iconfont icon-jubao"
+      ></span>
+      {{ agreeErrorMsg }}
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { getCurLanguage } from "@/composables/common.js";
+
+const L = getCurLanguage();
+
+const agreeFlag = ref(false); //同意注册协议标识,默认不同意
+const agreeErrorMsg = ref(); // 协议错误提示
+const name = ref(""); //用户名
+const nameErrorMsg = ref(""); //用户名错误提示
+const password = ref(""); //密码
+const confirmPassword = ref("") //二次确认密码
+const showPwdFlag = ref(false); //密码是否明文显示,默认密文
+const showConfirmPwdFlag = ref(false) // 二次密码是否明文显示,默认密文
+
+const joinForFreeDisabled = computed(() => false);
+
+const clearInputVal = () => {
+  name.value = "";
+};
+
+//密码是否显示
+const isShowPwd = () => {
+  showPwdFlag.value = !showPwdFlag.value;
+};
+
+// 二次确认密码是否显示
+const isShowConfirmPwd = () => {
+  showConfirmPwdFlag.value = !showConfirmPwdFlag.value;
+}
+
+//是否同意用户注册协议
+const agree = () => {
+  agreeFlag.value = !agreeFlag.value;
+};
+
+const joinForFree = () => {
+  if (!agreeFlag.value) {
+    agreeErrorMsg.value = L["请同意用户注册协议及隐私政策"];
+    return false;
+  }
+  agreeErrorMsg.value = "";
+};
+
+watch(name, (val) => {
+  nameErrorMsg.value = val ? "" : L["请输入用户名"];
+});
+</script>
+
+<style lang="scss" scoped>
+.center {
+  padding: 30px 30px 40px;
+
+  .item {
+    position: relative;
+    margin-top: 15px;
+    border-radius: 2px;
+
+    &:first-child {
+      margin-top: 0;
+    }
+
+    .icon {
+      position: absolute;
+      left: 1px;
+      top: 1px;
+      width: 50px;
+      text-align: center;
+      height: 38px;
+      background: #f8f8f8;
+
+      .input {
+        border: 1px solid #e8e8e8;
+        height: 40px;
+        padding: 0 44px 0 60px;
+        width: 326px;
+      }
+    }
+
+    .input {
+      border: 1px solid #e8e8e8;
+      height: 40px;
+      padding: 0 44px 0 60px;
+      width: 326px;
+    }
+
+    &.code {
+      .input {
+        padding-right: 10px;
+        width: 150px;
+      }
+    }
+  }
+
+  .cancel {
+    position: absolute;
+    right: 0;
+    top: 1px;
+    width: 44px;
+    height: 38px;
+    cursor: pointer;
+
+    :before {
+      position: absolute;
+      top: 9px;
+      left: 14px;
+    }
+  }
+
+  .error {
+    margin-top: 10px;
+    position: relative;
+    color: $colorMain;
+    height: 16px;
+    line-height: 16px;
+  }
+
+  .submit {
+    display: block;
+    margin-top: 35px;
+    background: $colorMain;
+    color: #fff;
+    text-align: center;
+    border-radius: 2px;
+    height: 45px;
+    line-height: 45px;
+    font-size: 18px;
+    letter-spacing: 0px;
+
+    &:hover {
+      opacity: 0.9;
+    }
+
+    &.disabled {
+      background-color: #909399;
+      pointer-events: none;
+    }
+  }
+
+  .agree_wrap {
+    margin-top: 11px;
+    height: 14px;
+    line-height: 14px;
+    color: #999;
+    padding-left: 2px;
+    position: relative;
+    cursor: pointer;
+    .agree_selected {
+      color: #fff;
+      position: absolute;
+      top: 1px;
+      left: 3px;
+      z-index: 2;
+      font-size: 13px;
+    }
+
+    .checkbox {
+      width: 14px;
+      height: 14px;
+      display: inline-block;
+      vertical-align: top;
+      position: relative;
+      outline: none;
+      -webkit-appearance: none;
+      background: none;
+      border: none;
+      box-sizing: border-box;
+      cursor: pointer;
+      box-shadow: none;
+
+      &.checked {
+        &:before {
+          background: $colorMain;
+          border-color: $colorMain;
+        }
+      }
+
+      &:before {
+        border: 1px solid #ddd;
+        background: #fff;
+        z-index: 1;
+        position: absolute;
+        top: 0;
+        left: 0;
+        content: " ";
+        display: block;
+        width: 100%;
+        height: 100%;
+        box-sizing: border-box;
+      }
+    }
+
+    .text {
+      margin-left: 5px;
+      display: inline-block;
+      vertical-align: top;
+
+      .agreement {
+        color: #000;
+
+        &:hover {
+          text-decoration: underline;
+        }
+      }
+    }
+  }
+}
+</style>

+ 388 - 0
xinkeaboard-web/components/register/RegisterMail.vue

@@ -0,0 +1,388 @@
+<template>
+  <div class="center">
+    <p style="width: 296px">
+      {{ L["register"]["我们将向您的邮箱发送验证码,该邮件将用作登录用户名"] }}
+    </p>
+    <div class="item account">
+      <span
+        style="color: #bbb; font-size: 19px; padding-top: 7px"
+        class="icon iconfont icon-wode"
+      ></span>
+      <input
+        type="text"
+        v-model="email"
+        :placeholder="L['请输入邮箱']"
+        class="input"
+        autocomplete="off"
+      />
+      <div
+        data-type="userName"
+        class="cancel"
+        @click="clearInputVal"
+      >
+        <span style="color: #bbb" class="iconfont icon-cuowu"></span>
+      </div>
+    </div>
+    <div class="error" v-if="emailErrorMsg">
+      <span
+        style="color: #e1251b; font-size: 14px"
+        class="iconfont icon-jubao"
+      ></span>
+      {{ emailErrorMsg }}
+    </div>
+    <!-- 邮箱验证码 -->
+    <div class="verify-code">
+      <span class="verify-code-icon">
+        <img src="/mail_success.png" alt="" />
+      </span>
+      <input
+        type="text"
+        v-model="emailCode"
+        :placeholder="L['请输入验证码']"
+        class="input"
+        autocomplete="off"
+      />
+      <span class="verify-code-accept">
+        <el-button
+          @click="getVerifyCode"
+          :disabled="countDownNumer"
+          :loading="getVerifyCodeLoading"
+        >
+          <span>{{ codeText }}</span>
+          <span v-if="countDownNumer">{{ `(${countDownNumer})` }}</span>
+        </el-button>
+      </span>
+    </div>
+    <div class="error" v-if="emailCodeErrorMsg">
+      <span
+        style="color: #e1251b; font-size: 14px"
+        class="iconfont icon-jubao"
+      ></span>
+      {{ emailCodeErrorMsg }}
+    </div>
+    <a
+      href="javascript:void(0)"
+      @click="next"
+      :class="{ submit: true, disabled: nextDisabled }"
+      >{{ L["下一步"] }}</a
+    >
+    <el-dialog
+      :title="L['register']['滑动验证']"
+      destroy-on-close
+      width="500px"
+      center
+      modal-class="register-verify-model"
+      v-model="modalVisible"
+    >
+      <SliderVerify
+        :slideVerifyOptions="{ show: false, w: 450, h: 220 }"
+        @onSuccess="verifySuccess"
+        @onFail="verifyFail"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { getCurLanguage } from "@/composables/common.js";
+import { showMessage, startCountdown } from "@/utils/common";
+const L = getCurLanguage();
+
+const emailCalc = ref();
+const email = ref(""); //邮箱
+const emailCode = ref(""); // 邮箱验证码
+const emailErrorMsg = ref(); //错误提示
+const emailCodeErrorMsg = ref(); //验证码错误提示
+
+// 人机验证弹窗显示标识
+const modalVisible = ref(false);
+
+// 重新获取验证码标识
+const isReacquireCode = ref(false);
+
+// 人机校验通过标识符
+const isHMVerifySuccess = ref(false);
+
+const getVerifyCodeLoading = ref(false);
+
+// 倒计时展示
+const countDownNumer = ref(0);
+
+// 获取验证码的文案
+const codeText = computed(() => {
+  return isReacquireCode.value
+    ? L["register"]["重新获取"]
+    : L["register"]["获取验证码"];
+});
+
+// 注册邮箱时按钮置灰状态
+const nextDisabled = computed(() => !email.value || !emailCode.value);
+
+// 获取验证码
+const getVerifyCode = () => {
+  // 校验邮箱非空以及格式
+  if (!validateEmail()) return;
+  if (!isHMVerifySuccess.value) {
+    modalVisible.value = true;
+  } else {
+    getVerifyCodeLoading.value = true;
+    post("v3/member/front/active/verification/code", {
+      email: email.value,
+      source: 1,
+      type: 1,
+    })
+      .then((res) => {
+        if (res.state === 200) {
+          showMessage({
+            message: L["register"]["验证码已发送"],
+            type: "success",
+          });
+          // 设置倒计时
+          startCountdown(60, (time) => (countDownNumer.value = time));
+          isReacquireCode.value = true;
+        } else {
+          showMessage({
+            message: res.msg,
+            type: "warning",
+          });
+        }
+      })
+      .finally(() => {
+        getVerifyCodeLoading.value = false;
+      });
+  }
+};
+
+// 人机验证成功
+const verifySuccess = () => {
+  isHMVerifySuccess.value = true;
+  modalVisible.value = false;
+  getVerifyCode();
+};
+
+// 人机校验失败
+const verifyFail = () => {
+  showMessage({
+    message: L["register"]["校验不通过"],
+    type: "warning",
+  });
+};
+
+// 校验邮箱
+const validateEmail = () => {
+  //邮箱非空的验证
+  if (!email.value) {
+    emailErrorMsg.value = L["请输入邮箱"];
+    return false;
+  }
+
+  // 邮箱格式验证
+  emailCalc.value = checkEmail(email.value);
+  if (emailCalc.value !== true) {
+    emailErrorMsg.value = emailCalc.value;
+    return false;
+  }
+  emailErrorMsg.value = "";
+
+  return true;
+};
+
+// 邮箱验证码验证
+const validateEmailCode = () => {
+  if (!emailCode.value) {
+    emailCodeErrorMsg.value = L["register"]["请输入邮箱验证码"];
+    return false;
+  }
+  emailCodeErrorMsg.value = "";
+
+  return true;
+};
+
+const next = () => {
+  if (!validateEmail() || !validateEmailCode()) return;
+  post("v3/member/front/active/check/verification/code", {
+    email: email.value,
+    type: 1,
+    verificationCode: emailCode.value,
+  }).then((res) => {
+    if (res.state == 200) {
+      showMessage({
+        message: L["register"]["注册成功"],
+        type: "success",
+      });
+      //成功提示,并返回到登录页面
+      currentStep.value = "registerAccount";
+      // setTimeout(() => {
+      //   goToPage("/login");
+      // }, 500);
+    } else {
+      //提示错误
+      showMessage({
+        message: res.msg,
+        type: "warning",
+      });
+    }
+  });
+};
+
+//清空输入框内容
+const clearInputVal = (type) => {
+   email.value = "";
+};
+</script>
+<style lang="scss">
+.register-verify-model {
+  .el-dialog__title {
+    font-weight: 900 !important;
+  }
+
+  .el-dialog__body {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+</style>
+<style lang="scss" scoped>
+.center {
+  padding: 30px 30px 40px;
+
+  .item {
+    position: relative;
+    margin-top: 15px;
+    border-radius: 2px;
+
+    &:first-child {
+      margin-top: 0;
+    }
+
+    .icon {
+      position: absolute;
+      left: 1px;
+      top: 1px;
+      width: 50px;
+      text-align: center;
+      height: 38px;
+      background: #f8f8f8;
+
+      .input {
+        border: 1px solid #e8e8e8;
+        height: 40px;
+        padding: 0 44px 0 60px;
+        width: 326px;
+      }
+    }
+
+    .input {
+      border: 1px solid #e8e8e8;
+      height: 40px;
+      padding: 0 44px 0 60px;
+      width: 326px;
+    }
+
+    &.code {
+      .input {
+        padding-right: 10px;
+        width: 150px;
+      }
+    }
+  }
+
+  .cancel {
+    position: absolute;
+    right: 0;
+    top: 1px;
+    width: 44px;
+    height: 38px;
+    cursor: pointer;
+
+    :before {
+      position: absolute;
+      top: 9px;
+      left: 14px;
+    }
+  }
+
+  .error {
+    margin-top: 10px;
+    position: relative;
+    color: $colorMain;
+    height: 16px;
+    line-height: 16px;
+  }
+
+  .verify-code {
+    display: flex;
+    width: 326px;
+    height: 40px;
+    margin-top: 10px;
+
+    &-icon {
+      width: 50px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      border: 1px solid #e8e8e8;
+      border-right: none;
+      background: #f8f8f8;
+
+      img {
+        width: 20px;
+      }
+    }
+
+    input {
+      border: 1px solid #e8e8e8;
+      border-left: none;
+      border-right: none;
+      height: 40px;
+      padding: 0 0 0 10px;
+      width: 190px;
+    }
+
+    &-accept {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 90px;
+
+      .el-button {
+        width: 100%;
+        height: 100%;
+        color: #666;
+        background: #f8f8f8;
+        border-left: none;
+        border-radius: 0;
+        font-size: 12px;
+
+        &:hover {
+          border-color: #e8e8e8;
+          color: $colorMain;
+        }
+      }
+    }
+  }
+
+  .submit {
+    display: block;
+    margin-top: 35px;
+    background: $colorMain;
+    color: #fff;
+    text-align: center;
+    border-radius: 2px;
+    height: 45px;
+    line-height: 45px;
+    font-size: 18px;
+    letter-spacing: 0px;
+
+    &:hover {
+      opacity: 0.9;
+    }
+
+    &.disabled {
+      background-color: #909399;
+      pointer-events: none;
+    }
+  }
+}
+</style>

+ 5 - 1
xinkeaboard-web/composables/index.js

@@ -1,5 +1,9 @@
-// export const apiUrl = 'https://www.b2btopsite.com/api/';
+// 测试环境
 export const apiUrl = 'http://54.46.9.88:8001/';
+
+// 本地后端
+// export const apiUrl = 'http://192.168.0.158:8001/';
+
 export const defaultUrl = 'http://54.46.9.88:8001/';
 export const supplierUrl = 'http://54.46.9.88:82/user/login';
 export const curLang = 'en'

+ 13 - 1
xinkeaboard-web/composables/request.js

@@ -27,6 +27,10 @@ export const get = async (url, data = {}) => {
                 resolve(response.data)
             }
         }, err => {
+            showMessage({
+                message: err.message,
+                type: 'error'
+            })
             reject(err)
         })
     })
@@ -43,7 +47,15 @@ export const post = async (url, data = {}, timeout,type = 'urlencoded') => {
         }, err => {
             console.log(err)
             if(err.code == 'ECONNABORTED'){
-                ElMessage.warning('The request timed out. Please try again later')
+                showMessage({
+                    message: 'The request timed out. Please try again later',
+                    type: 'error'
+                })
+            } else {
+                showMessage({
+                    message: err.message,
+                    type: 'error'
+                })
             }
             reject(err)
         }).finally(()=>{

+ 1 - 1
xinkeaboard-web/pages/member/login/forget.vue

@@ -14,7 +14,7 @@
           {{ L["想起密码?"] }}
           <a
             href="javascript:void(0)"
-            class="go_register_btn"
+            class="go_login_btn"
             @click="goToPage('/login')"
           >
             {{ L["去登录"] }}

+ 11 - 132
xinkeaboard-web/pages/register.vue

@@ -13,12 +13,12 @@
           />
         </nuxt-link>
         <div class="r_register_wrap">
-          {{ L["还没注册?"] }}
+          {{ L["已有账号?"] }}
           <a
-            class="go_register_btn"
+            class="go_login_btn"
             href="javascript:void(0)"
-            @click="goToPage('/register')"
-            >{{ L["去注册"] }}</a
+            @click="goToPage('/login')"
+            >{{ L["去登录"] }}</a
           >
         </div>
       </div>
@@ -35,76 +35,8 @@
           <div class="top">
             <div class="item1">{{ L["注册账号"] }}</div>
           </div>
-          <div class="center">
-            <p style="width: 296px">
-              {{
-                L["我们将发送一封验证邮件到你的邮箱,此邮箱将作为登录用户名"]
-              }}
-            </p>
-            <div class="item account">
-              <span
-                style="color: #bbb; font-size: 19px; padding-top: 7px"
-                class="icon iconfont icon-wode"
-              ></span>
-              <input
-                type="text"
-                v-model="memberEmail"
-                :placeholder="L['请输入邮箱']"
-                class="input"
-                autocomplete="off"
-              />
-              <div
-                data-type="userName"
-                class="cancel"
-                @click="clearInputVal('memberEmail')"
-              >
-                <span style="color: #bbb" class="iconfont icon-cuowu"></span>
-              </div>
-            </div>
-            <div class="error" v-if="errorMsg">
-              <span
-                style="color: #e1251b; font-size: 14px"
-                class="iconfont icon-jubao"
-              ></span>
-              {{ errorMsg }}
-            </div>
-            <!-- 人机验证组件 -->
-            <!-- <div class="login-verify">
-              <SliderVerify />
-            </div> -->
-            <a
-              href="javascript:void(0)"
-              @click="sendEmailLink"
-              class="login_btn"
-              >{{ L["发送验证邮件"] }}</a
-            >
-            <div class="agree_wrap">
-              <input
-                type="checkbox"
-                :class="{ checkbox: true, default: true, checked: agreeFlag }"
-              />
-              <span
-                class="agree_selected iconfont icon-finish"
-                @click="agree"
-              />
-              <span class="text">
-                {{ L["我同意"]
-                }}<nuxt-link
-                  target="_blank"
-                  class="agreement"
-                  :to="`/member/login/agreement?type=1`"
-                >
-                  {{ L["《用户注册协议》"] }}</nuxt-link
-                >
-                <nuxt-link
-                  target="_blank"
-                  class="agreement"
-                  :to="`/member/login/agreement?type=2`"
-                  >{{ L["《隐私政策》"] }}
-                </nuxt-link>
-              </span>
-            </div>
-          </div>
+          <RegisterMail v-if="currentStep === 'registerMail'" />
+          <RegisterAccount v-if="currentStep === 'registerAccount'" />
           <div :class="{ bottom: true, flex_row_between_center: true }">
             <a href="javascript:void(0)" @click="goToPage('/login')">{{
               L["已有账号,去登录"]
@@ -121,7 +53,7 @@
 
 <script setup>
 import { useRoute, useRouter } from "vue-router";
-import { ref, getCurrentInstance, onMounted, watch } from "vue";
+import { ref, onMounted } from "vue";
 import { ElMessage } from "element-plus";
 // import { lang_zn } from "@/assets/language/zh";
 import { getCurLanguage } from "@/composables/common.js";
@@ -132,17 +64,15 @@ const configInfo = useUserInfo();
 // const L = lang_zn;
 const L = getCurLanguage();
 const route = useRoute();
-const agreeFlag = ref(false); //同意注册协议标识,默认不同意
-const memberEmail = ref(""); //邮箱
-const errorMsg = ref(); //错误提示
 const router = useRouter();
-const { proxy } = getCurrentInstance();
 const defaultImg = ref("/common_top_logo.png");
 const defaultBgImg = ref("/login_bg.png");
 const fromurl = ref("");
-const emailCalc = ref();
 const ImgBG = ref("");
 
+// 当前步骤标识
+const currentStep = ref('registerAccount')
+
 useHead({
   title: "Register",
   meta: [
@@ -167,53 +97,7 @@ const getBg = () => {
 };
 getBg();
 
-const sendEmailLink = () => {
-  let param = {};
-  param.email = memberEmail.value;
-  param.source = 1;
-
-  //邮箱的验证
-  if (!param.email) {
-    errorMsg.value = L["请输入邮箱"];
-    return false;
-  } else {
-    emailCalc.value = checkEmail(param.email);
-    if (emailCalc.value !== true) {
-      errorMsg.value = emailCalc.value;
-      return;
-    } else {
-      errorMsg.value = "";
-    }
-  }
-
-  //同意用户注册协议及隐私政策
-  if (!agreeFlag.value) {
-    errorMsg.value = L["请同意用户注册协议及隐私政策"];
-    return false;
-  }
-
-  post("v3/member/front/active/link", param).then((res) => {
-    if (res.state == 200) {
-      // 如果没有收到,请检查垃圾邮件
-      // 如果还是没有收到,请重新填写邮箱
-      ElMessage.success(L["注册验证邮件已发送到"] + param.email);
-      //成功提示,并返回到登录页面
-      setTimeout(() => {
-        goToPage("/login");
-      }, 500);
-    } else {
-      //提示错误
-      errorMsg.value = res.msg;
-    }
-  });
-};
 
-//清空输入框内容
-const clearInputVal = (type) => {
-  if (type == "memberEmail") {
-    memberEmail.value = "";
-  }
-};
 
 //是否同意用户注册协议
 const agree = () => {
@@ -227,12 +111,6 @@ const goToPage = (type) => {
   });
 };
 
-watch([memberEmail], () => {
-  if (memberEmail.value) {
-    errorMsg.value = "";
-  }
-});
-
 onMounted(() => {
   if (route.query.redirectUrl) {
     fromurl.value =
@@ -246,6 +124,7 @@ onMounted(() => {
   }
 });
 </script>
+
 <style lang="scss" scoped>
 @import "@/assets/style/register.scss";
 

BIN=BIN
xinkeaboard-web/public/mail_success.png


BIN=BIN
xinkeaboard-web/public/refresh.png


+ 44 - 0
xinkeaboard-web/utils/common.ts

@@ -0,0 +1,44 @@
+import { ElMessage } from "element-plus";
+
+let msgBoxInstance: any = null;
+/**
+ * [showMessage 统一封装消息]
+ *
+ * @return  {[params]}  [return description]
+ */
+export const showMessage = (params: {
+  type: string;
+  message: string;
+  icon?: string;
+  duration?: number;
+}) => {
+  if (msgBoxInstance) {
+    msgBoxInstance.close();
+  }
+  msgBoxInstance = ElMessage(params as any);
+};
+
+/**
+ * [倒计时]
+ *
+ * @return  {[params]}  [return description]
+ */
+export function startCountdown(
+  duration: number,
+  onTick?: (args: number) => void,
+  onComplete?: () => void
+) {
+  let time = duration;
+  const timer = setInterval(() => {
+    time--;
+    if (typeof onTick === "function") {
+      onTick(time);
+    }
+    if (time <= 0) {
+      clearInterval(timer);
+      if (typeof onComplete === "function") {
+        onComplete();
+      }
+    }
+  }, 1000);
+}