vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > DeepSeek Vue 表单验证

DeepSeek 助力 Vue 开发丝滑的表单验证Form Validation功能

作者:宝码香车

文章介绍了如何使用Vue3的组合式API创建一个表单验证组件,并提供了详细的代码示例,组件支持双向绑定、自定义验证规则、样式和布局等功能,还涵盖了组件的调用示例、路由配置和页面展示入口

DeepSeek 助力 Vue 开发:打造丝滑的表单验证(Form Validation)

📚前言

DeepSeek 在电信、汽车等行业也展现出了广阔的应用前景。

在电信行业,三大基础电信企业均全面接入 DeepSeek 开源大模型,实现在多场景、多产品中应用,针对热门的 DeepSeek-R1 模型提供专属算力方案和配套环境,助力国产大模型性能释放。电信企业利用 DeepSeek 的强大能力,结合自身在 5G、云平台、大数据等方面的优势,拓展 AI 应用场景,丰富 AI 应用供给。在智能客服方面,DeepSeek 能够快速准确地回答用户的问题,提供个性化的服务建议,提升用户满意度;在网络优化方面,通过对大量网络数据的分析,DeepSeek 可以帮助电信企业优化网络布局,提高网络性能,降低运营成本。

📚页面效果

📚指令输入

已经创建好了一个基于Vue3的组合式API的项目(Composition API),并能正常运行起来,请帮我用 Vue3的组合式API(Composition API) 生成一个 表单验证(FormValidation) 的功能组件,所有代码都保存在components/FormValidation 下的文件夹中。功能组件的script标签中只有setup属性,使用普通 JavaScript 实现,不使用TypeScript。
功能要有,如下属性:

组件属性定义

1. 表单数据绑定相关
2. 验证规则相关
3. 样式和布局相关
4. 其他

组件事件定义

1. 验证相关
2. 表单提交相关
3. 字段值变化相关

其他可能实现的功能

1. 验证规则扩展
2. 错误提示处理
3. 可访问性
4. 性能优化
5. 组件嵌套

你有更好的建议也可以添加,要注明。组件定义好后给出3个及以上的调用示例。
下面是现有目录
vueAndDeepseek/
├── src/ # 源代码目录
│ ├── assets/ # 静态资源
│ │ ├── base.css
│ │ ├── main.css
│ │ └── logo.svg
│ ├── components/ # 组件目录
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ ├── Progress/
│ │ │ └── Progress.vue
│ │ ├── Accordion/
│ │ ├── BackToTop/
│ │ ├── Card/
│ │ ├── InfiniteScroll/
│ │ ├── Notification/
│ │ ├── Timeline/
│ │ ├── Switch/
│ │ ├── Tabs/
│ │ ├── Sidebar/
│ │ ├── Breadcrumbs/
│ │ ├── MasonryLayout/
│ │ ├── Rating/
│ │ ├── ColorPicker/
│ │ ├── RightClickMenu/
│ │ ├── RangePicker/
│ │ ├── Navbar/
│ │ ├── FormValidation/
│ │ ├── DatePicker/
│ │ └── icons/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # Pinia 状态管理
│ │ └── counter.js
│ ├── views/ # 页面组件
│ │ ├── ProgressView.vue
│ │ ├── TabsView.vue
│ │ ├── SwitchView.vue
│ │ ├── TimelineView.vue
│ │ ├── NotificationView.vue
│ │ ├── CardView.vue
│ │ ├── InfiniteScrollView.vue
│ │ ├── BackToTopView.vue
│ │ ├── AccordionView.vue
│ │ ├── SidebarView.vue
│ │ ├── BreadcrumbsView.vue
│ │ ├── MasonryLayoutView.vue
│ │ ├── RatingView.vue
│ │ ├── ColorPickerView.vue
│ │ ├── RightClickMenuView.vue
│ │ ├── RangePickerView.vue
│ │ ├── NavbarView.vue
│ │ ├── FormValidationView.vue
│ │ ├── DatePickerView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 根组件
│ └── main.js # 应用入口
├── public/ # 公共资源目录
├── index.html # HTML 模板
├── package.json # 项目配置
├── vite.config.js # Vite 配置
└── node_modules/ # 依赖包目录

📚think

你有更好的建议也可以添加,要注明。组件定义好后给出3个及以上的调用示例。
下面是现有目录
vueAndDeepseek/
├── src/ # 源代码目录
│ ├── assets/ # 静态资源
│ │ ├── base.css
│ │ ├── main.css
│ │ └── logo.svg
│ ├── components/ # 组件目录
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ ├── Progress/
│ │ │ └── Progress.vue
│ │ ├── Accordion/
│ │ ├── BackToTop/
│ │ ├── Card/
│ │ ├── InfiniteScroll/
│ │ ├── Notification/
│ │ ├── Timeline/
│ │ ├── Switch/
│ │ ├── Tabs/
│ │ ├── Sidebar/
│ │ ├── Breadcrumbs/
│ │ ├── MasonryLayout/
│ │ ├── Rating/
│ │ ├── ColorPicker/
│ │ ├── RightClickMenu/
│ │ ├── RangePicker/
│ │ ├── Navbar/
│ │ ├── FormValidation/
│ │ ├── DatePicker/
│ │ └── icons/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── stores/ # Pinia 状态管理
│ │ └── counter.js
│ ├── views/ # 页面组件
│ │ ├── ProgressView.vue
│ │ ├── TabsView.vue
│ │ ├── SwitchView.vue
│ │ ├── TimelineView.vue
│ │ ├── NotificationView.vue
│ │ ├── CardView.vue
│ │ ├── InfiniteScrollView.vue
│ │ ├── BackToTopView.vue
│ │ ├── AccordionView.vue
│ │ ├── SidebarView.vue
│ │ ├── BreadcrumbsView.vue
│ │ ├── MasonryLayoutView.vue
│ │ ├── RatingView.vue
│ │ ├── ColorPickerView.vue
│ │ ├── RightClickMenuView.vue
│ │ ├── RangePickerView.vue
│ │ ├── NavbarView.vue
│ │ ├── FormValidationView.vue
│ │ ├── DatePickerView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 根组件
│ └── main.js # 应用入口
├── public/ # 公共资源目录
├── index.html # HTML 模板
├── package.json # 项目配置
├── vite.config.js # Vite 配置
└── node_modules/ # 依赖包目录

📚think

好的,我现在需要帮用户创建一个基于Vue3组合式API的表单验证组件。用户已经有一个项目结构,需要将组件放在components/FormValidation目录下。组件需要使用setup语法糖,且用普通JavaScript实现,不用TypeScript。首先,我得仔细分析用户的需求,确保所有属性和事件都被正确处理。

首先,组件属性部分。modelValue用于双向绑定,所以需要用v-model来处理。fields定义表单字段,每个字段可能有不同的类型,比如文本框或下拉框。rules是验证规则,可能需要内置一些常用规则,如必填、邮箱格式等。validateOnChange控制是否在值变化时立即验证,默认true。样式相关的labelWidth和inline需要处理布局。disabled属性控制整个表单的禁用状态。

接下来是事件。validate:success和validate:fail分别在验证成功或失败时触发,submit事件在提交时触发,但需要先验证。change事件在字段值变化时触发。这些事件需要在适当的时候被emit出去。

然后,其他功能比如验证规则扩展,错误提示,可访问性,性能优化等。内置的验证规则需要实现,同时允许用户自定义。错误信息要实时显示,可能需要在每个字段旁边展示。可访问性方面,可能需要添加aria属性。性能方面,避免不必要的验证,可能用防抖或节流。

现在考虑组件的结构。表单组件需要遍历fields数组生成各个表单项,每个表单项根据类型渲染不同的输入控件。验证逻辑需要在值变化时触发,或者提交时触发。需要收集所有字段的错误信息,并在对应位置显示。

首先,组件内部需要使用reactive或ref来管理表单数据和错误信息。modelValue通过props传入,然后用watch来同步内部表单数据。当内部数据变化时,更新modelValue。对于每个字段,需要绑定对应的值和事件处理,比如input或change事件,触发验证。

验证规则的实现是关键。每个规则可能是一个函数,接收值并返回是否有效,以及错误信息。例如,必填规则检查值是否为空,邮箱规则用正则表达式验证。当字段的值变化时,如果validateOnChange为true,则立即执行验证。否则可能在提交时验证。

在验证过程中,收集每个字段的错误,保存到errors对象里。如果所有字段都通过,触发success事件,否则触发fail事件。submit事件处理函数需要先执行整体验证,再决定是否触发submit事件。

样式方面,labelWidth可以用样式绑定,比如:style=“{ width: labelWidth }”。inline类控制表单是否行内显示。disabled属性应用到所有输入控件上。

调用示例部分,用户需要三个以上的例子。比如登录表单、注册表单和行内表单。每个示例展示不同的配置,如字段类型、规则、布局等。

现在开始编写代码。首先,创建FormValidation.vue文件。在setup函数中处理props和emit。props按用户给的属性定义。modelValue用watch来监听变化,同步到内部表单数据。每个字段的输入控件需要根据type渲染不同的元素,比如input、select等。绑定value和事件,比如@input和@change,处理输入变化,触发验证。

验证函数validateField,根据字段名和值应用对应的规则。规则可以是内置的或自定义的。错误信息存储到errors对象,并更新显示。整体验证函数validate遍历所有字段,收集错误,触发相应事件。

模板部分,用v-for遍历fields,生成表单项。每个项的标签宽度由labelWidth控制,错误信息在输入框下方显示。提交按钮触发handleSubmit函数,执行验证并触发submit事件。

内置验证规则需要定义,比如required, email, minLength等。用户可以通过rules prop传入自定义规则。

在调用示例中,展示如何传递modelValue、fields、rules等属性,并监听事件。例如,登录表单有用户名和密码,注册表单包含邮箱和密码,行内表单可能用不同的布局。

可能遇到的问题:处理嵌套字段,比如对象或数组结构,但用户的需求中modelValue是对象,每个字段名是顶层的。可能不需要处理嵌套。另外,表单提交时阻止默认事件,用@submit.prevent。

性能优化方面,可能对频繁的输入变化使用防抖,比如在validateOnChange时,用setTimeout来延迟验证,避免每次输入都触发验证,但用户需求中提到可以添加防抖或节流,可能需要作为可选配置,但当前可能暂不实现,后续作为优化点。

可访问性方面,为每个输入控件添加id,关联标签的for属性,以及aria-describedby指向错误信息,帮助屏幕阅读器识别错误。

测试各个属性和事件是否正常工作,比如禁用状态下所有输入不可用,inline布局是否正确,验证触发时机是否正确等。

最后,确保代码符合用户要求的目录结构,所有代码保存在components/FormValidation下,可能是一个单独的.vue文件,或者拆分多个文件,但用户可能希望是一个文件。所以将组件代码放在FormValidation.vue中。

总结步骤:

📘组件代码

下面按照您的要求创建表单验证组件,以下是分步实现方案:

components/FormValidation/FormValidation.vue

<template>
  <form 
    class="form-validation" 
    :class="{ 'form-inline': inline }" 
    @submit.prevent="handleSubmit"
  >
    <div 
      v-for="field in fields" 
      :key="field.name" 
      class="form-item"
    >
      <label 
        :for="field.name" 
        class="form-label"
        :style="{ width: labelWidth }"
      >{{ field.label }}</label>
      <!-- 文本输入 -->
      <input
        v-if="field.type === 'text' || field.type === 'password'"
        :id="field.name"
        v-model="formData[field.name]"
        :type="field.type"
        :disabled="disabled"
        class="form-input"
        @input="handleInput(field.name)"
        @change="handleChange(field.name)"
      >
      <!-- 下拉选择 -->
      <select
        v-else-if="field.type === 'select'"
        :id="field.name"
        v-model="formData[field.name]"
        :disabled="disabled"
        class="form-select"
        @change="handleChange(field.name)"
      >
        <option 
          v-for="opt in field.options" 
          :key="opt.value" 
          :value="opt.value"
        >{{ opt.label }}</option>
      </select>
      <!-- 错误提示 -->
      <div 
        v-if="errors[field.name]" 
        class="error-message"
        :aria-live="'polite'"
      >{{ errors[field.name] }}</div>
    </div>
    <slot name="submit">
      <button 
        type="submit" 
        class="submit-btn"
        :disabled="disabled"
      >提交</button>
    </slot>
  </form>
</template>
<script setup>
import { ref, reactive, watch, toRaw } from 'vue'
// 内置验证规则
const builtInRules = {
  required: value => ({
    valid: !!value,
    message: '必填项'
  }),
  email: value => ({
    valid: /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
    message: '邮箱格式不正确'
  }),
  minLength: min => value => ({
    valid: value.length >= min,
    message: `至少需要${min}个字符`
  })
}
const props = defineProps({
  modelValue: {
    type: Object,
    required: true
  },
  fields: {
    type: Array,
    default: () => []
  },
  rules: {
    type: Object,
    default: () => ({})
  },
  validateOnChange: {
    type: Boolean,
    default: true
  },
  labelWidth: {
    type: [String, Number],
    default: '100px'
  },
  inline: Boolean,
  disabled: Boolean
})
const emit = defineEmits([
  'update:modelValue',
  'validate:success',
  'validate:fail',
  'submit',
  'change'
])
// 响应式数据
const formData = reactive({ ...props.modelValue })
const errors = reactive({})
// 同步外部modelValue变化
watch(() => props.modelValue, (newVal) => {
  Object.assign(formData, newVal)
})
// 输入处理
const handleInput = (fieldName) => {
  emit('update:modelValue', { ...formData })
  if (props.validateOnChange) {
    validateField(fieldName)
  }
}
// 字段变化事件
const handleChange = (fieldName) => {
  const value = formData[fieldName]
  emit('change', fieldName, value)
}
// 单个字段验证
const validateField = async (fieldName) => {
  const rules = props.rules[fieldName] || []
  const value = formData[fieldName]
  for (const rule of rules) {
    let validator = rule
    if (typeof rule === 'string' && builtInRules[rule]) {
      validator = builtInRules[rule]
    }
    if (typeof validator === 'function') {
      const result = validator(value)
      if (!result.valid) {
        errors[fieldName] = result.message
        return false
      }
    } else if (typeof validator === 'object' && validator.validator) {
      const customResult = validator.validator(value)
      if (!customResult) {
        errors[fieldName] = validator.message || '验证失败'
        return false
      }
    }
  }
  delete errors[fieldName]
  return true
}
// 整体验证
const validate = async () => {
  let isValid = true
  const errorList = []
  for (const field of props.fields) {
    const valid = await validateField(field.name)
    if (!valid) {
      isValid = false
      errorList.push({
        field: field.name,
        message: errors[field.name]
      })
    }
  }
  if (isValid) {
    emit('validate:success', toRaw(formData))
  } else {
    emit('validate:fail', errorList)
  }
  return isValid
}
// 提交处理
const handleSubmit = async () => {
  const isValid = await validate()
  if (isValid) {
    emit('submit', toRaw(formData))
  }
}
// 暴露验证方法
defineExpose({ validate })
</script>
<style scoped>
.form-validation {
  max-width: 600px;
  margin: 0 auto;
}
.form-item {
  margin-bottom: 1rem;
}
.form-label {
  display: inline-block;
  margin-right: 1rem;
  text-align: right;
}
.form-input,
.form-select {
  padding: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 200px;
}
.form-inline .form-item {
  display: inline-flex;
  margin-right: 1rem;
}
.error-message {
  color: #ff4444;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}
.submit-btn {
  background-color: #42b983;
  color: white;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.submit-btn:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}
</style>

以下是三个调用示例:

登录表单示例(在views/FormValidationView.vue中使用)

<template>
  <FormValidation
    v-model="formData"
    :fields="fields"
    :rules="rules"
    @submit="handleSubmit"
    @validate:success="handleSuccess"
    @validate:fail="handleFail"
  />
</template>
<script setup>
import { ref } from 'vue'
import FormValidation from '@/components/FormValidation/FormValidation.vue'
const formData = ref({
  username: '',
  password: ''
})
const fields = [
  { name: 'username', label: '用户名', type: 'text' },
  { name: 'password', label: '密码', type: 'password' }
]
const rules = {
  username: [
    'required',
    value => ({
      validator: v => v.length >= 4,
      message: '用户名至少4个字符'
    })
  ],
  password: [
    'required',
    'minLength:6'
  ]
}
const handleSubmit = (formData) => {
  console.log('提交数据:', formData)
}
const handleSuccess = (validData) => {
  console.log('验证成功:', validData)
}
const handleFail = (errors) => {
  console.error('验证失败:', errors)
}
</script>

注册表单示例

<template>
  <FormValidation
    v-model="regForm"
    :fields="regFields"
    :rules="regRules"
    label-width="120px"
    @submit="handleRegister"
  >
    <template #submit>
      <button type="submit" class="custom-submit">立即注册</button>
    </template>
  </FormValidation>
</template>
<script setup>
import { ref } from 'vue'
const regForm = ref({
  email: '',
  password: '',
  gender: ''
})
const regFields = [
  { name: 'email', label: '电子邮箱', type: 'text' },
  { name: 'password', label: '设置密码', type: 'password' },
  { 
    name: 'gender', 
    label: '性别', 
    type: 'select',
    options: [
      { value: '', label: '请选择' },
      { value: 'male', label: '男性' },
      { value: 'female', label: '女性' }
    ]
  }
]
const regRules = {
  email: ['required', 'email'],
  password: ['required', 'minLength:8'],
  gender: [v => !!v || '请选择性别']
}
const handleRegister = (formData) => {
  console.log('注册数据:', formData)
}
</script>

行内表单示例

<template>
  <FormValidation
    v-model="inlineForm"
    :fields="inlineFields"
    :rules="inlineRules"
    inline
    label-width="80px"
    @change="handleFieldChange"
  />
</template>
<script setup>
import { ref } from 'vue'
const inlineForm = ref({
  search: '',
  category: ''
})
const inlineFields = [
  { name: 'search', label: '搜索', type: 'text' },
  {
    name: 'category',
    label: '分类',
    type: 'select',
    options: [
      { value: '', label: '全部' },
      { value: '1', label: '电子产品' },
      { value: '2', label: '图书' }
    ]
  }
]
const inlineRules = {
  search: [v => v.length <= 20 || '搜索词过长']
}
const handleFieldChange = (fieldName, value) => {
  console.log(`字段 ${fieldName} 变更为:`, value)
}
</script>

组件特点说明:

使用时可以根据实际需求通过组合不同的属性和验证规则来构建复杂的表单场景。

📚代码测试

运行正常

📚测试代码正常跑通,附其他基本代码

📘编写路由 src\router\index.js

import { createRouter, createWebHistory } from 'vue-router'
import RightClickMenuView from '../views/RightClickMenuView.vue'
import RangePickerView from '../views/RangePickerView.vue'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'progress',
      component:  () => import('../views/ProgressView.vue'),
    },
    {
      path: '/tabs',
      name: 'tabs',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      // 标签页(Tabs)
      component: () => import('../views/TabsView.vue'),
    },
    {
      path: '/accordion',
      name: 'accordion',
      // 折叠面板(Accordion)
      component: () => import('../views/AccordionView.vue'),
    },
    {
      path: '/timeline',
      name: 'timeline',
      // 时间线(Timeline)
      component: () => import('../views/TimelineView.vue'),
    },
    {
      path: '/backToTop',
      name: 'backToTop',
      component: () => import('../views/BackToTopView.vue')
    },
    {
      path: '/notification',
      name: 'notification',
      component: () => import('../views/NotificationView.vue')
    },
    {
      path: '/card',
      name: 'card',
      component: () => import('../views/CardView.vue')
    },
    {
      path: '/infiniteScroll',
      name: 'infiniteScroll',
      component: () => import('../views/InfiniteScrollView.vue')
    },
    {
      path: '/switch',
      name: 'switch',
      component: () => import('../views/SwitchView.vue')
    },
    {
      path: '/sidebar',
      name: 'sidebar',
      component: () => import('../views/SidebarView.vue')
    },
    {
      path: '/breadcrumbs',
      name: 'breadcrumbs',
      component: () => import('../views/BreadcrumbsView.vue')
    },
    {
      path: '/masonryLayout',
      name: 'masonryLayout',
      component: () => import('../views/MasonryLayoutView.vue')
    },
    {
      path: '/rating',
      name: 'rating',
      component: () => import('../views/RatingView.vue')
    },
    {
      path: '/datePicker',
      name: 'datePicker',
      component: () => import('../views/DatePickerView.vue')
    },
    {
      path: '/colorPicker',
      name: 'colorPicker',
      component: () => import('../views/ColorPickerView.vue')
    },
    {
      path: '/rightClickMenu',
      name: 'rightClickMenu',
      component: RightClickMenuView
    },
    {
      path: '/rangePicker',
      name: 'rangePicker',
      component: () => import('../views/RangePickerView.vue')
    },
    {
      path: '/navbar',
      name: 'navbar',
      component: () => import('../views/NavbarView.vue')
    },
    {
      path: '/formValidation',
      name: 'formValidation',
      component: () => import('../views/FormValidationView.vue')
    }
  ],
})
export default router

📘编写展示入口

src\App.vue

<script setup>
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
  <header>
    <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
    <div class="wrapper">
      <HelloWorld msg="You did it!" />
      <nav>
        <RouterLink to="/">Progress</RouterLink>
        <RouterLink to="/tabs">Tabs</RouterLink>
        <RouterLink to="/accordion">Accordion</RouterLink>
        <RouterLink to="/timeline">Timeline</RouterLink>
        <RouterLink to="/backToTop">BackToTop</RouterLink>
        <RouterLink to="/notification">Notification</RouterLink>
        <RouterLink to="/card">Card</RouterLink>
        <RouterLink to="/infiniteScroll">InfiniteScroll</RouterLink>
        <RouterLink to="/switch">Switch</RouterLink>
        <RouterLink to="/sidebar">Sidebar</RouterLink>
        <RouterLink to="/breadcrumbs">Breadcrumbs</RouterLink>
        <RouterLink to="/masonryLayout">MasonryLayout</RouterLink>
        <RouterLink to="/rating">Rating</RouterLink>
        <RouterLink to="/datePicker">DatePicker</RouterLink>
        <RouterLink to="/colorPicker">ColorPicker</RouterLink>
        <RouterLink to="/rightClickMenu">RightClickMenu</RouterLink>
        <RouterLink to="/rangePicker">RangePicker</RouterLink>
        <RouterLink to="/navbar">Navbar</RouterLink>
        <RouterLink to="/formValidation">FormValidation</RouterLink>
      </nav>
    </div>
  </header>
  <RouterView />
</template>
<style scoped>
header {
  line-height: 1.5;
  max-height: 100vh;
}
.logo {
  display: block;
  margin: 0 auto 2rem;
}
nav {
  width: 100%;
  font-size: 12px;
  text-align: center;
  margin-top: 2rem;
}
nav a.router-link-exact-active {
  color: var(--color-text);
}
nav a.router-link-exact-active:hover {
  background-color: transparent;
}
nav a {
  display: inline-block;
  padding: 0 1rem;
  border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
  border: 0;
}
@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }
  .logo {
    margin: 0 2rem 0 0;
  }
  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
  nav {
    text-align: left;
    margin-left: -1rem;
    font-size: 1rem;
    padding: 1rem 0;
    margin-top: 1rem;
  }
}
</style>

📚页面效果

到此这篇关于DeepSeek 助力 Vue 开发丝滑的表单验证Form Validation功能的文章就介绍到这了,更多相关DeepSeek Vue 表单验证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文