手把手带你分析arco-design,并创建属于自己的后台管理(3)

1,749 阅读2分钟

文章已经更新到第三篇了,第三篇开始正式进入我们的业务,首先先编写login,抄Arco Design - 企业级产品的完整设计和开发解决方案雪梨表单 - 让每家企业轻松拥有表单平台 (antdv.com)的登录布局和方式

  1. 手把手带你分析antdv-pro,并创建属于自己的后台管理(1) - 掘金 (juejin.cn)
  2. 手把手带你分析antdv-pro,并创建属于自己的后台管理(2) - 掘金 (juejin.cn)
  3. 手把手带你分析arco-design,并创建属于自己的后台管理(3) - 掘金 (juejin.cn)

第一步:观察arco-design和雪梨表单登录布局

image.png

image.png

image.png

image.png

第二步:编写登录页面

image.png

  • 根据上述观察,可以看出来账号登录和微信登录属于两个业务组件所以把他们进行拆分
  • 编写微信登录业务组件
<template>
  <a-form class="wx-from">
    <a-form-item>
      <div class="flex-center">请使用<a>微信扫码</a>关注公众号即可安全登录</div>
    </a-form-item>
    <a-form-item>
      <div class="flex-center">
        <a-qrcode
          value="http://www.antdv.com"
          status="expired"
          @refresh="() => console.log('refresh')"
        />
      </div>
    </a-form-item>
    <a-form-item>
      <div class="flex-center">
        扫码表示您同意<a href="/terms/license">《服务协议》</a><a
          href="/terms/privacy"
          >《隐私政策》</a
        >
      </div>
    </a-form-item>
  </a-form>
</template>

image.png

  • 编写账号登录业务组件
<template>
  <div class="account-from">
    <a-form>
      <a-form-item v-bind="validateInfos.username">
        <a-input
          v-model:value="modelRef.username"
          :placeholder="usernamePlaceholder"
          size="large"
        />
      </a-form-item>

      <a-form-item v-bind="validateInfosBind">
        <Transition name="fade" mode="out-in">
          <a-input-password
            v-if="loginType === 0 ? true : false"
            v-model:value="modelRef.password"
            placeholder="密码"
            size="large"
          />
          <div v-else class="flex">
            <a-input
              v-model:value="modelRef.captcha"
              placeholder="验证码"
              size="large"
            />
            <a-button
              class="m-l-4"
              size="large"
              :disabled="isActive"
              @click="getCaptcha"
            >
              {{ !isActive ? "获取验证码" : `${time}s 重试` }}
            </a-button>
          </div>
        </Transition>
      </a-form-item>

      <a-form-item>
        <div class="flex-between">
          <a>忘记密码</a>
          <a @click="switchLoginType">{{
            loginType === 0 ? "验证码登录" : "密码登录"
          }}</a>
        </div>
      </a-form-item>

      <a-form-item>
        <a-button
          block
          type="primary"
          html-type="submit"
          size="large"
          @click="handleLogin"
        >
          {{ loginType === 0 ? "登录" : "登录 / 注册" }}
        </a-button>
      </a-form-item>
      <a-form-item>
        <div class="flex-center" v-if="loginType === 0 ? true : false">
          <p>还没有账号?<a @click="switchLoginType">前往注册</a></p>
        </div>
        <div class="flex" v-else>
          <a-checkbox v-model:checked="modelRef.checked" />
          <div class="ml-2">
            未注册的手机号/邮箱将自动注册。勾选即代表您同意并接受
            <a href="/terms/license"> 服务协议 </a><a href="/terms/privacy">隐私政策</a>
          </div>
        </div>
      </a-form-item>
    </a-form>
    <context-holder />
  </div>
</template>

<script setup lang="ts">
import { Form, message } from "ant-design-vue";
import type { Rule } from "ant-design-vue/es/form";

const [messageApi, contextHolder] = message.useMessage();

const useForm = Form.useForm;
interface FormModel {
  username: string;
  password: string;
  checked: boolean;
  captcha: string;
}
const modelRef = reactive<FormModel>({
  username: "",
  password: "",
  checked: false,
  captcha: "",
});
const validateUsername = async (_rule: Rule, value: string) => {
  if (loginType.value === 0) {
    if (!value) {
      return Promise.reject("请输入手机号/邮箱");
    }
  } else {
    if (!value) {
      return Promise.reject("请输入手机号");
    }
    if (!/^(1[3456789]|9[28])\d{9}$/.test(value)) {
      return Promise.reject("请输入正确的手机号");
    }
  }
};

const validatePassword = async (_rule: Rule, value: string) => {
  if (loginType.value === 0) {
    if (!value) {
      return Promise.reject("请输入密码");
    }
  }
};

const validateCaptcha = async (_rule: Rule, value: string) => {
  if (loginType.value !== 0) {
    if (!value) {
      return Promise.reject("请输入验证码");
    }
  }
};

const rulesRef = reactive({
  username: [{ validator: validateUsername, trigger: "change" }],
  password: [{ validator: validatePassword, trigger: "change" }],
  captcha: [{ validator: validateCaptcha, trigger: "change" }],
});

const { clearValidate, resetFields, validate, validateInfos } = useForm(
  modelRef,
  rulesRef
);

const loginType = ref(0);

const switchLoginType = () => {
  resetFields();
  loginType.value = loginType.value === 0 ? 1 : 0;
};

const usernamePlaceholder = computed(() => {
  return loginType.value === 0 ? "手机号/邮箱" : "手机号";
});

const getCaptcha = () => {
  clearValidate(["captcha"]);
  if (modelRef.username) {
    startTime();
  } else {
    validate(["username"])
      .then(() => {})
      .catch(() => {});
  }
};

const { time, startTime, isActive } = useCountDown(5);

const handleLogin = () => {
  validate()
    .then(() => {
      console.log(toRaw(modelRef));
      if (loginType.value !== 0) {
        if (!modelRef.checked) {
          messageApi.error("请勾选服务协议与隐私政策");
        }
      }
    })
    .catch((err) => {
      console.log(err);
    });
};

const validateInfosBind = computed(() => {
  if (loginType.value === 0) {
    return validateInfos.password;
  } else {
    return validateInfos.captcha;
  }
});
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.15s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
:deep(.ant-checkbox) {
  position: relative;
  top: -0.6em;
}
</style>

image.png

  • 编写倒计时composables

image.png

export function useCountDown(countDownTime: number) {
  const time = toRef(countDownTime);
  const { pause, resume, isActive } = useIntervalFn(
    () => {
      time.value--;
      if (time.value <= 0) {
        pause();
      }
    },
    1000,
    { immediate: false }
  );

  const startTime = () => {
    resume();
    time.value = countDownTime;
  };

  return {
    time,
    isActive,
    pause,
    startTime,
  };
}

注意!!!

validate校验可以单独进行

image.png

表单 Form - Ant Design Vue (antdv.com)