程序员的资源宝库

网站首页 > gitee 正文

vue3项目-小兔鲜儿笔记-商品详情页03和登录页01

sanyeah 2024-04-13 16:21:06 gitee 4 ℃ 0 评论

1.封装数量选择组件

功能分析:

  1. 默认值为1

  2. 可限制最大最小值

  3. 点击-就是减1,点击+就是加1

  4. 需要完成v-model的双向数据绑定

  5. 存在无label的情况

<script setup>
// 组件间的v-model =》父组件绑定:modelValue,监听@update:modelValue
import { useVModel } from '@vueuse/core'

const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({
  modelValue: {
    type: Number,
    default: 1
  },
  min: {
    type: Number,
    default: 1
  },
  max: {
    type: Number,
    default: 100
  },
  label: {
    type: String,
    default: ''
  }
})

// 使用useVModel做数据的双向绑定,这样修改值就会自动通知父组件
// 参数1为props,参数2为从父组件接收的参数,参数3是要发送的自定义事件
const count = useVModel(props, 'modelValue', emit)

// 修改值函数
const changeNum = (step) => {
  const newVal = count.value + step
  if (newVal < props.min || newVal > props.max) return
  // 修改count值就会自动通知父组件count值修改
  count.value = newVal
  // 同时发出change通知
  emit('change', count.value)
}
</script>

 

2.封装按钮组件

目的:封装一个通用按钮组件,有大、中、小、超小4种尺寸,有默认、主要、次要、灰色四种类型

<template>
  <button class="xtx-button" :class="[size, type]">
    <slot />
  </button>
</template>

<script setup>
defineProps({
  size: {
    type: String,
    default: 'middle'
  },
  type: {
    type: String,
    default: 'default'
  }
})
</script>
.large {
  width: 240px;
  height: 50px;
  font-size: 16px;
}
.middle {
  width: 180px;
  height: 50px;
  font-size: 16px;
}
.small {
  width: 100px;
  height: 32px;
  font-size: 14px;
}
.mini {
  width: 60px;
  height: 32px;
  font-size: 14px;
}

.default {
  border-color: #e4e4e4;
  color: #666;
}
.primary {
  border-color: @xtxColor;
  background-color: @xtxColor;
  color: #fff;
}
.plain {
  border-color: @xtxColor;
  color: @xtxColor;
  background-color: lighten(@xtxColor, 50%);
}
.gray {
  border-color: #ccc;
  background-color: #ccc;
  color: #fff;
}

 

3. 登录-表单校验

  1. 定义校验规则

    utils/vee-validate.js
    
    // 定义校验规则给vee-validate组件使用
    export default {
      // 校验account
      account(value) {
        // value是将来使用该规则的表单元素的值
        // 1.必填
        // 2.6~20个字符,以字母开头
        // 返回true表示校验成功,其他情况均为失败,返回失败原因
        if (!value) return '请输入用户名'
        if (!/^[a-zA-Z]\w(5,19)$/.test(value)) {
          return '字母开头且6~20个字符'
        }
        return true
      },
      password(value) {
        if (!value) return '请输入密码'
        if (!/^\w(6,24)$/.test(value)) return '密码是6~24个字符'
        return true
      },
      mobile(value) {
        if (!value) return '请输入手机号码'
        if (!/^1[3-9]\d(9)$/.test(value)) return '手机号格式错误'
        return true
      },
      code(value) {
        if (!value) return '请输入验证码'
        if (!/^\d(6)$/.test(value)) return '验证码是6位数字'
        return true
      },
      isAgree(value) {
        if (!value) return '请勾选同意用户协议'
        return true
      }
    }
  2. 使用vee-validate提供的Form组件,绑定validation-schema为上述自定义校验规则
        <Form
            ref="formCom"
            class="form"
            :validation-schema="mySchema"
            autocomplete="off"
            v-slot="{errors}"
        >
                ....
  3.  使用vee-validate提供的Field组件代替input表单元素,指定name属性为自定义校验规则中的某项,比如,自定义校验中:account(){...},这里指定name就为account
                <Field
                    :class="{error: errors.account}"
                    v-model="form.account"
                    name="account"
                    type="text"
                    placeholder="请输入用户名或手机号"
                />
                        ....
  4. Form组件内部使用了默认插槽,这个插槽会返回errors错误对象,用于提示错误信息,比如errors.account就是显示自定义校验中account未通过校验返回的错误信息
              <div class="error" v-if="errors.account">
                <i class="iconfont icon-warning"></i>
                {{ errors.account }}
              </div>
                ...
  5. 切换组价时调用重置表单的方法清除校验结果
    const formCom = ref(null) // Form表单的ref对象
    
    // 表单数据
    const form = reactive({
      isAgree: true,
      account: null,
      password: null,
      mobile: null,
      code: null
    })
    // 表单校验规则
    const mySchema = {
      account: veeSchema.account,
      password: veeSchema.password,
      mobile: veeSchema.mobile,
      code: veeSchema.code,
      isAgree: veeSchema.isAgree
    }
    
    watch(isMsgLogin, () => {
      form.account = null
      form.code = null
      form.mobile = null
      form.isAgree = true
      form.password = null
      // formCom组件提供重置表单的方法resetForm()
      formCom.value.resetForm()
    })
  6. 当点击登录时,提交表单,调用Form组件的ref对象提供的统一校验方法validate()
    // 统一校验,Form组件提供统一校验方法validate()
    const login = async () => {
      const valid = await formCom.value.validate()
      if (valid) {
        // 账号登录
        if (!isMsgLogin.value) {
          try {
            const {result} = await userAccountLogin({account: form.account, password: form.password})
            const {id, avatar, nickname, account, mobile, token} = result
            // 1.获取用户信息并存储到userStore中
            userStore.setUser({
              id,
              avatar,
              nickname,
              account,
              mobile,
              token
            })
            // 2.消息提醒
            Message({type: 'success', text: '登录成功'})
            // 3.跳转
            await router.push(route.query.redirectUrl || '/')
          } catch (e) {
            Message({type: 'error', text: '登录失败'})
          }
        } else {
          // 手机号登录
          try {
            if (codeSpan.value.disabled) {
              const {result} = await userMobileLogin({mobile: form.mobile, code: form.code})
              const {id, avatar, nickname, account, mobile, token} = result
              // 1.获取用户信息并存储到userStore中
              userStore.setUser({
                id,
                avatar,
                nickname,
                account,
                mobile,
                token
              })
              // 2.消息提醒
              Message({type: 'success', text: '登录成功'})
              // 3.跳转
              await router.push(route.query.redirectUrl || '/')
            } else {
              throw Error('未发送验证码')
            }
          } catch (e) {
            Message({type: 'error', text: '登录失败'})
          }
        }
      }
    }

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表