当前位置: 首页 > news >正文

Vue3项目-大事件

1.ESlint配合Prettier完成代码校验

参考另一片文章

使用pnpm创建项目后eslint不提示错误信息-CSDN博客

2.基于husky的代码检验根据

husky 配置

bash中运行

1.pnpm lint

2.git add .

3.git commit -m '初始化提交'

lint-staged 配置

1安装

pnpm i lint-staged -D

2.配置 package.json

{// ... 省略 ..."lint-staged": {"*.{js,ts,vue}": ["eslint --fix"]}
}
​
{"scripts": {// ... 省略 ..."lint-staged": "lint-staged"}
}

3.修改 .husky/pre-commit 文件

pnpm lint-staged

4.调整文件目录,安装sass包

pnpm add sass -D

VueRouter4路由语法

router/index.js

import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
// createRouter 创建路由实例
// 配置 history 模式
// 1.history模式:createWebHistory 地址栏不带#
// 2.hash模式:createWebHashHistory 地址栏带#// vite中的环境变量 import.meta.env.BASE_URL 就是vite.config.js中的base配置项
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),// history: createWebHistory('/jd'),routes: []
})export default router

vite.config.js

import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'// https://vite.dev/config/
export default defineConfig({plugins: [vue(), vueDevTools()],base: '/',resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}
})

import.meta.env.BASE_URL 是Vite 环境变量:https://cn.vitejs.dev/guide/env-and-mode.html

App.vue

<script setup>
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'// 在Vue3 CompositionAPI中
// 1.获取路由对象 router useRouter
//  const router=useRouter()
// 2.获取路由参数 route useRoute
//  const route=useRoute()const router = useRouter()
const route = useRoute()const goList = () => {console.log(router, route)router.push('/list')
}
</script><template><div>我是app</div><button @click="$router.push('/home')">跳首页</button><button @click="goList">跳列表</button>
</template><style scoped></style>

3.ElementPlus组件库按需导入

1.安装 pnpm add element-plus

2.按需导入 pnpm install -D unplugin-vue-components unplugin-auto-import

vite.config.ts 

import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'export default defineConfig({// ...plugins: [// ...AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],
})

3.直接可以使用组件

App.vue

<script setup>
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'// 在Vue3 CompositionAPI中
// 1.获取路由对象 router useRouter
//  const router=useRouter()
// 2.获取路由参数 route useRoute
//  const route=useRoute()const router = useRouter()
const route = useRoute()const goList = () => {console.log(router, route)router.push('/list')
}
</script><template><div>我是app</div><test-demo></test-demo><el-button @click="$router.push('/home')">跳首页</el-button><el-button @click="goList">跳列表</el-button>
</template><style scoped></style>

TestDemo.vue

<script setup></script><template><div>我是测试的组件</div>
</template><style scoped></style>

4.Pinia构建用户仓库和持久化

1.安装插件 pinia-plugin-persistedstate

pnpm add pinia-plugin-persistedstate -D

2.使用 main.js

import persist from 'pinia-plugin-persistedstate'
import { createPinia } from 'pinia'
。。。
app.use(createPinia().use(persist))

3.配置 stores/user.js

import { defineStore } from 'pinia'
import { ref } from 'vue'
​
// 用户模块
export const useUserStore = defineStore('big-user',() => {const token = ref('') // 定义 tokenconst setToken = (t) => (token.value = t) // 设置 token
​return { token, setToken }},{persist: true // 持久化}
)

 App.vue中可以直接使用

<script setup>
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { useUserStore } from './stores/user'
// 在Vue3 CompositionAPI中
// 1.获取路由对象 router useRouter
//  const router=useRouter()
// 2.获取路由参数 route useRoute
//  const route=useRoute()const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const goList = () => {console.log(router, route)router.push('/list')
}
</script><template><div>我是app</div><test-demo></test-demo><el-button @click="$router.push('/home')">跳首页</el-button><el-button @click="goList">跳列表</el-button><el-button type="primary">Primary</el-button><el-button type="success">Success</el-button>{{ userStore.token }}<el-button @click="userStore.setToken('Bear Spojhb')">登陆</el-button><el-button @click="userStore.removeToken()">退出</el-button>
</template><style scoped></style>

pinia独立维护

stores/index.js

import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(persist)
export default pinia

这样main.js就可以简写(感觉没有必要 但是文件比较多的时候有用)

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from '@/stores/index'
import '@/assets/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')

仓库统一导出

pinia 独立维护

- 现在:初始化代码在 main.js 中,仓库代码在 stores 中,代码分散职能不单一

- 优化:由 stores 统一维护,在 stores/index.js 中完成 pinia 初始化,交付 main.js 使用

原本的程序App.vue

import { useUserStore } from '@/stores/modules/user.js'
import { useCountStore } from '@/stores/modules/count.js'

需要从两个仓库文件里面导入

<script setup>
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/stores/modules/user.js'
import { useCountStore } from '@/stores/modules/count.js'// 在Vue3 CompositionAPI中
// 1.获取路由对象 router useRouter
//  const router=useRouter()
// 2.获取路由参数 route useRoute
//  const route=useRoute()const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const countStore = useCountStore()const goList = () => {console.log(router, route)router.push('/list')
}
</script><template><div>我是app</div><test-demo></test-demo><el-button @click="$router.push('/home')">跳首页</el-button><el-button @click="goList">跳列表</el-button><el-button type="primary">Primary</el-button><el-button type="success">Success</el-button>{{ userStore.token }}<el-button @click="userStore.setToken('Bear Spojhb')">登陆</el-button><el-button @click="userStore.removeToken()">退出</el-button>{{ countStore.count }}<el-button @click="countStore.add(1)">+1</el-button></template><style scoped></style>

仓库 统一导出

- 现在:使用一个仓库 import { useUserStore } from ./stores/user.js 不同仓库路径不一致

- 优化:由 stores/index.js 统一导出,导入路径统一 ./stores,而且仓库维护在 stores/modules 中


写在stores/index.js中

import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(persist)
export default pinia// import { useUserStore } from './modules/user'
// export { useUserStore }
// import { useCountStore } from './modules/counter'
// export { useCountStore }//更简单 这一句话等价于上面的两句话
export * from './modules/user'
export * from './modules/counter'

这样App.vue

就可以这样 直接从一个文件里面导入

import { useUserStore, useCountStore } from '@/stores/index.js'

App.vue

<script setup>
import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
// import { useUserStore } from '@/stores/modules/user.js'
// import { useCountStore } from '@/stores/modules/count.js'
import { useUserStore, useCountStore } from '@/stores/index.js'// 在Vue3 CompositionAPI中
// 1.获取路由对象 router useRouter
//  const router=useRouter()
// 2.获取路由参数 route useRoute
//  const route=useRoute()const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const countStore = useCountStore()const goList = () => {console.log(router, route)router.push('/list')
}
</script><template><div>我是app</div><test-demo></test-demo><el-button @click="$router.push('/home')">跳首页</el-button><el-button @click="goList">跳列表</el-button><el-button type="primary">Primary</el-button><el-button type="success">Success</el-button>{{ userStore.token }}<el-button @click="userStore.setToken('Bear Spojhb')">登陆</el-button><el-button @click="userStore.removeToken()">退出</el-button>{{ countStore.count }}<el-button @click="countStore.add(1)">+1</el-button>
</template><style scoped></style>

5.数据交互-请求工具设计

utils/request.js

import axios from 'axios'const baseURL = 'http://big-event-vue-api-t.itheima.net'const instance = axios.create({// TODO 1. 基础地址,超时时间
})instance.interceptors.request.use((config) => {// TODO 2. 携带tokenreturn config},(err) => Promise.reject(err)
)instance.interceptors.response.use((res) => {// TODO 3. 处理业务失败// TODO 4. 摘取核心响应数据return res},(err) => {// TODO 5. 处理401错误return Promise.reject(err)}
)export default instance

完成axios的基本配置

import { useUserStore } from '@/stores/user'
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'const baseURL = 'http://big-event-vue-api-t.itheima.net'const instance = axios.create({baseURL,timeout: 100000
})instance.interceptors.request.use((config) => {const userStore = useUserStore()if (userStore.token) {config.headers.Authorization = userStore.token}return config},(err) => Promise.reject(err)
)instance.interceptors.response.use((res) => {if (res.data.code === 0) {return res}ElMessage({ message: res.data.message || '服务异常', type: 'error' })return Promise.reject(res.data)},(err) => {ElMessage({ message: err.response.data.message || '服务异常', type: 'error' })console.log(err)if (err.response?.status === 401) {router.push('/login')}return Promise.reject(err)}
)export default instance
export { baseURL }

有注释版

import axios from 'axios'
import { useUserStore } from '@/stores'
import { ElMessage } from 'element-plus'
import router from '@/router'const baseURL = 'http://big-event-vue-api-t.itheima.net'const instance = axios.create({// TODO 1. 基础地址,超时时间baseURL,timeout: 10000
})instance.interceptors.request.use((config) => {// TODO 2. 携带tokenconst useStore = useUserStore()if (useStore.token) {config.headers.Authorization = useStore.token}return config},(err) => Promise.reject(err)
)// 响应拦截器
instance.interceptors.response.use((res) => {// TODO 4. 摘取核心响应数据if (res.data.code === 0) {return res}// TODO 3. 处理业务失败// 处理业务失败,给错误提示,抛出错误ElMessage.error(res.data.message || '服务异常')return Promise.reject(res.data)},(err) => {// TODO 5. 处理401错误// 错误的特殊情况=>401权限不足 或 token过期 =>拦截到登陆if (err.response?.status === 401) {router.push('/login')}// 错误的默认情况=>只要给提示ElMessage.error(err.response.data.message || '服务异常')return Promise.reject(err)}
)export default instance
export { baseURL }

6.首页整体路由设计

实现目标:

  • 完成整体路由规划【搞清楚要做几个页面,它们分别在哪个路由下面,怎么跳转的.....】

  • 通过观察, 点击左侧导航, 右侧区域在切换, 那右侧区域内容一直在变, 那这个地方就是一个路由的出口

  • 我们需要搭建嵌套路由

目标:

  • 把项目中所有用到的组件及路由表, 约定下来

约定路由规则

path文件功能组件名路由级别
/loginviews/login/LoginPage.vue登录&注册LoginPage一级路由
/views/layout/LayoutContainer.vue布局架子LayoutContainer一级路由
├─ /article/manageviews/article/ArticleManage.vue文章管理ArticleManage二级路由
├─ /article/channelviews/article/ArticleChannel.vue频道管理ArticleChannel二级路由
├─ /user/profileviews/user/UserProfile.vue个人详情UserProfile二级路由
├─ /user/avatarviews/user/UserAvatar.vue更换头像UserAvatar二级路由
├─ /user/passwordviews/user/UserPassword.vue重置密码UserPassword二级路由

明确了路由规则,可以全部配完,也可以边写边配。

7.登录注册页面 [element-plus 表单 & 表单校验]

安装 element-plus 图标库

pnpm i @element-plus/icons-vue

静态结构准备

<script setup>
import { User, Lock, View } from '@element-plus/icons-vue'
import { ref } from 'vue'
const isRegister = ref(true)
</script><template><!-- el-row表示一行,一行分为24份el-col表示列(1):span="12" 代表在一行中,占12份(50%)(2):span="6"  表示在一行中,占6份(25%)(3):offset="3"代表在一行中,左侧margin份数el-form 整个表单组件el-form-item 表单的一行(一个表单域)el-input 表单元素(输入框)--><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注册相关表单 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister"><el-form-item><h1>注册</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="请输入密码"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="请输入再次密码"></el-input></el-form-item><el-form-item><el-button class="button" type="primary" auto-insert-space> 注册 </el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false"> ← 返回 </el-link></el-form-item></el-form><!-- 登陆相关表单 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登录</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-inputname="password":prefix-icon="Lock"type="password"placeholder="请输入密码"></el-input></el-form-item><!-- <el-form-item><el-input :prefix-icon="View" placeholder="测试代码"></el-input></el-form-item> --><el-form-item class="flex"><div class="flex"><el-checkbox>记住我</el-checkbox><el-link type="primary" :underline="false">忘记密码?</el-link></div></el-form-item><el-form-item><el-button class="button" type="primary" auto-insert-space>登录</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true"> 注册 → </el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" scoped>
.login-page {height: 100vh;background-color: #fff;.bg {background:url('@/assets/logo2.png') no-repeat 60% center / 240px auto,url('@/assets/login_bg.jpg') no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
</style>

8.注册登录-表单校验

LoginPage.vue

1.结构相关

el-row表示一行,一行分为24份

el-col表示列

(1):span="12" 代表在一行中,占12份(50%)

(2):span="6" 表示在一行中,占6份(25%)

(3):offset="3"代表在一行中,左侧margin份数

el-form 整个表单组件

el-form-item 表单的一行(一个表单域)

el-input 表单元素(输入框)

2.四大校验方式

(1)el-form => :model="ruleForm" 绑定的整个form的数据对象 {xxx,xxx,xxx}

(2)el-form => :rules="rules" 绑定的整个rules规则对象 {xxx,xxx,xxx}

(3)表单元素 => v-model="ruleForm.xxx" 给表单元素,绑定form子属性

(4)el-form-item => prop配置生效的是哪个校验规则(和rules中的字段要对应)

<script setup>
import { User, Lock } from '@element-plus/icons-vue'
import { ref } from 'vue'
const isRegister = ref(true)
// 注册
const formModel = ref({username: '',password: '',repassword: ''
})
// 整个表单的校验规则
// 1.非空校验 required:true  message消息提示,trigger触发校验的时机 blur change
// 2.长度校验 min:xx, max:xx
// 3.正则校验 pattern:正则规则 \S非空字符
// 4.自定义校验 => 自己写逻辑校验(校验函数)
//   validator:(rule,value,callback)
//   (1)rule 当前校验规则相关的信息
//  (2)value 所校验的表单元素当前表单值
//  (3)callback 无论成功还是失败,都需要callback回调
//          - callback() 校验成功
//          - callback(new error(错误信息)) 校验失败const rules = {username: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 5, max: 10, message: '用户名必须是5-10位的字符', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ pattern: /^\S{6,15}$/, message: '密码必须是 6-15位 的非空字符', trigger: 'blur' }],repassword: [{ required: true, message: '请输入密码', trigger: 'blur' },{ pattern: /^\S{6,15}$/, message: '密码必须是 6-15位 的非空字符', trigger: 'blur' },{validator: (rule, value, callback) => {// 判断value和当前form中收集的password是否一致if (value !== formModel.value.password) {callback(new Error('两次输入密码不一致'))} else {callback() //就算校验成功,也需要callback}}}]
}
</script><template><!--1.结构相关el-row表示一行,一行分为24份el-col表示列(1):span="12" 代表在一行中,占12份(50%)(2):span="6"  表示在一行中,占6份(25%)(3):offset="3"代表在一行中,左侧margin份数el-form 整个表单组件el-form-item 表单的一行(一个表单域)el-input 表单元素(输入框)2.校验相关(1)el-form => :model="ruleForm" 绑定的整个form的数据对象 {xxx,xxx,xxx}(2)el-form => :rules="rules"    绑定的整个rules规则对象  {xxx,xxx,xxx}(3)表单元素 => v-model="ruleForm.xxx" 给表单元素,绑定form子属性(4)el-form-item => prop配置生效的是哪个校验规则(和rules中的字段要对应)--><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注册相关表单 --><el-form:model="formModel":rules="rules"ref="form"size="large"autocomplete="off"v-if="isRegister"><el-form-item><h1>注册</h1></el-form-item><el-form-item prop="username"><el-inputv-model="formModel.username":prefix-icon="User"placeholder="请输入用户名"></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="formModel.password":prefix-icon="Lock"type="password"placeholder="请输入密码"></el-input></el-form-item><el-form-item prop="repassword"><el-inputv-model="formModel.repassword":prefix-icon="Lock"type="password"placeholder="请输入再次密码"></el-input></el-form-item><el-form-item><el-button class="button" type="primary" auto-insert-space> 注册 </el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false"> ← 返回 </el-link></el-form-item></el-form><!-- 登陆相关表单 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登录</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="请输入用户名"></el-input></el-form-item><el-form-item><el-inputname="password":prefix-icon="Lock"type="password"placeholder="请输入密码"></el-input></el-form-item><!-- <el-form-item><el-input :prefix-icon="View" placeholder="测试代码"></el-input></el-form-item> --><el-form-item class="flex"><div class="flex"><el-checkbox>记住我</el-checkbox><el-link type="primary" :underline="false">忘记密码?</el-link></div></el-form-item><el-form-item><el-button class="button" type="primary" auto-insert-space>登录</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true"> 注册 → </el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" scoped>
.login-page {height: 100vh;background-color: #fff;.bg {background:url('@/assets/logo2.png') no-repeat 60% center / 240px auto,url('@/assets/login_bg.jpg') no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
</style>

9.注册预校验

需求:点击注册按钮,注册之前,需要先校验

通过 ref 获取到 表单组件

const form = ref()
​
<el-form ref="form">

注册之前进行校验

<el-button@click="register"class="button"type="primary"auto-insert-space
>注册
</el-button>
​
const register = async () => {await form.value.validate()console.log('开始注册请求')
}

封装 api 实现注册功能

需求:封装注册api,进行注册,注册成功切换到登录

新建 api/user.js 封装

import request from '@/utils/request'
​
export const userRegisterService = ({ username, password, repassword }) =>request.post('/api/reg', { username, password, repassword })

页面中调用

const register = async () => {await form.value.validate()await userRegisterService(formModel.value)ElMessage.success('注册成功')// 切换到登录isRegister.value = false
}

(ElMessage报错 不要声明,声明会没有样式)

eslintrc 中声明全局变量名, 解决 ElMessage 报错问题

module.exports = {...globals: {ElMessage: 'readonly',ElMessageBox: 'readonly',ElLoading: 'readonly'}
}

10.登陆校验和登陆请求

实现登录校验

【需求说明】给输入框添加表单校验

  1. 用户名不能为空,用户名必须是5-10位的字符,失去焦点 和 修改内容时触发校验

  2. 密码不能为空,密码必须是6-15位的字符,失去焦点 和 修改内容时触发校验

操作步骤:

(1)model 属性绑定 form 数据对象,直接绑定之前提供好的数据对象即可

<el-form :model="formModel" >

(2)rules 配置校验规则,共用注册的规则即可

<el-form :rules="rules" >

(3)v-model 绑定 form 数据对象的子属性

<el-inputv-model="formModel.username":prefix-icon="User"placeholder="请输入用户名"
></el-input>
​
<el-inputv-model="formModel.password"name="password":prefix-icon="Lock"type="password"placeholder="请输入密码"
></el-input>

(4)prop 绑定校验规则

<el-form-item prop="username"><el-inputv-model="formModel.username":prefix-icon="User"placeholder="请输入用户名"></el-input>
</el-form-item>
... 

(5)切换的时候重置

watch(isRegister, () => {formModel.value = {username: '',password: '',repassword: ''}
})

登录前的预校验 & 登录成功

【需求说明1】登录之前的预校验

  • 登录请求之前,需要对用户的输入内容,进行校验

  • 校验通过才发送请求

【需求说明2】登录功能

  1. 封装登录API,点击按钮发送登录请求

  2. 登录成功存储token,存入pinia 和 持久化本地storage

  3. 跳转到首页,给提示

【测试账号】

  • 登录的测试账号: shuaipeng

  • 登录测试密码: 123456

PS: 每天账号会重置,如果被重置了,可以去注册页,注册一个新号

实现步骤:

(1)注册事件,进行登录前的预校验 (获取到组件调用方法)

<el-form ref="form">const login = async () => {await form.value.validate()console.log('开始登录')
}

(2)封装接口 API

export const userLoginService = ({ username, password }) =>request.post('api/login', { username, password })

(3)调用方法将 token 存入 pinia 并 自动持久化本地

const userStore = useUserStore()
const router = useRouter()
const login = async () => {await form.value.validate()const res = await userLoginService(formModel.value)userStore.setToken(res.data.token)ElMessage.success('登录成功')router.push('/')
}

LoginPage.vue

<script setup>
import { User, Lock } from '@element-plus/icons-vue'
import { userRegisterService,userLoginService } from '@/api/user'
// import { ElMessage } from 'element-plus'
import { ref, watch } from 'vue'
import { useUserStore } from '@/stores'
import { useRouter } from 'vue-router'
const form = ref()
const isRegister = ref(false)// 注册
const formModel = ref({username: '',password: '',repassword: ''
})
// 整个表单的校验规则
// 1.非空校验 required:true  message消息提示,trigger触发校验的时机 blur change
// 2.长度校验 min:xx, max:xx
// 3.正则校验 pattern:正则规则 \S非空字符
// 4.自定义校验 => 自己写逻辑校验(校验函数)
//   validator:(rule,value,callback)
//   (1)rule 当前校验规则相关的信息
//  (2)value 所校验的表单元素当前表单值
//  (3)callback 无论成功还是失败,都需要callback回调
//          - callback() 校验成功
//          - callback(new error(错误信息)) 校验失败const rules = {username: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 5, max: 10, message: '用户名必须是5-10位的字符', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ pattern: /^\S{6,15}$/, message: '密码必须是 6-15位 的非空字符', trigger: 'blur' }],repassword: [{ required: true, message: '请输入密码', trigger: 'blur' },{ pattern: /^\S{6,15}$/, message: '密码必须是 6-15位 的非空字符', trigger: 'blur' },{validator: (rule, value, callback) => {// 判断value和当前form中收集的password是否一致if (value !== formModel.value.password) {callback(new Error('两次输入密码不一致'))} else {callback() //就算校验成功,也需要callback}}}]
}const register = async () => {await form.value.validate()await userRegisterService(formModel.value)ElMessage.success('注册成功')// 切换到登录isRegister.value = false// console.log(formModel.value.username)
}const userStore = useUserStore()
const router = useRouter()
const login = async () => {await form.value.validate()// console.log('开始登陆')// const res = await userLoginService(formModel.username, formModel.password)const res = await userLoginService(formModel.value)userStore.setToken(res.data.token)// console.log(res);ElMessage.success('登录成功')router.push('/')
}// 切换时,重置表单内容
watch(isRegister, () => {formModel.value = {username: '',password: '',repassword:''}
})
</script><template><!--1.结构相关el-row表示一行,一行分为24份el-col表示列(1):span="12" 代表在一行中,占12份(50%)(2):span="6"  表示在一行中,占6份(25%)(3):offset="3"代表在一行中,左侧margin份数el-form 整个表单组件el-form-item 表单的一行(一个表单域)el-input 表单元素(输入框)2.校验相关(1)el-form => :model="ruleForm" 绑定的整个form的数据对象 {xxx,xxx,xxx}(2)el-form => :rules="rules"    绑定的整个rules规则对象  {xxx,xxx,xxx}(3)表单元素 => v-model="ruleForm.xxx" 给表单元素,绑定form子属性(4)el-form-item => prop配置生效的是哪个校验规则(和rules中的字段要对应)--><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注册相关表单 --><el-form:model="formModel":rules="rules"ref="form"size="large"autocomplete="off"v-if="isRegister"><el-form-item><h1>注册</h1></el-form-item><el-form-item prop="username"><el-inputv-model="formModel.username":prefix-icon="User"placeholder="请输入用户名"></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="formModel.password":prefix-icon="Lock"type="password"placeholder="请输入密码"></el-input></el-form-item><el-form-item prop="repassword"><el-inputv-model="formModel.repassword":prefix-icon="Lock"type="password"placeholder="请输入再次密码"></el-input></el-form-item><el-form-item><el-button @click="register" class="button" type="primary" auto-insert-space>注册</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false"> ← 返回 </el-link></el-form-item></el-form><!-- 登陆相关表单 --><el-form :model="formModel" :rules="rules" ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登录</h1></el-form-item><el-form-item prop="username"><el-input v-model="formModel.username" :prefix-icon="User" placeholder="请输入用户名"></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="formModel.password"name="password":prefix-icon="Lock"type="password"placeholder="请输入密码"></el-input></el-form-item><!-- <el-form-item><el-input :prefix-icon="View" placeholder="测试代码"></el-input></el-form-item> --><el-form-item class="flex"><div class="flex"><el-checkbox>记住我</el-checkbox><el-link type="primary" :underline="false">忘记密码?</el-link></div></el-form-item><el-form-item><el-button @click="login" class="button" type="primary" auto-insert-space>登录</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true"> 注册 → </el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" scoped>
.login-page {height: 100vh;background-color: #fff;.bg {background:url('@/assets/logo2.png') no-repeat 60% center / 240px auto,url('@/assets/login_bg.jpg') no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
</style>


http://www.mrgr.cn/news/94032.html

相关文章:

  • 在资源有限中逆势突围:从抗战智谋到寒门高考的破局智慧
  • 瑞芯微RK3576(2)-调试过程中遇到的问题
  • 使用STM32CubeMX配置定时器中断实现LED每秒闪烁一次(STM32G070CBT6)
  • 制作自定义镜像
  • 【OpenGL】01-配置环境
  • react中字段响应式
  • 安装、配置和启动 ssh 服务,实现远程连接服务器
  • SVT-AV1源码分析函数 svt_av1_optimize_b
  • c++ constraints与concepts使用笔记
  • 如何用HTML5 Canvas实现电子签名功能✍️
  • 全网最详解答OSPF基础
  • 我与DeepSeek读《大型网站技术架构》(10)- 维基百科的高性能架构设计分析
  • C# Enumerable类 之 数据分组
  • 插入排序算法优化
  • 数字电路逻辑代数 | 运算 / 定律 / 公式 / 规则 / 例解
  • 【设计模式】《设计模式:可复用面向对象软件的基础》设计模式的分类与组织
  • 类和对象(下)
  • 大语言模型-语言模型发展历程
  • ⭐算法OJ⭐链表排序【归并排序】(C++/JavaScript 实现)
  • 基于Ollama平台部署的Qwen大模型实现聊天机器人