环境搭建
- 安装mock.js:命令:npm i mockjs
- 安装axios:命令:npm i axios
Mock语法规范
mock.js的语法规范包含两部分:
- 数据模版定义规范(DTD)
- 数据占位符定义规范(DPD)
数据模版定义规范
- 数据模版的每个属性有3个部分构成,属性名、生成规则、属性值
- 比如:‘name|rule’:value
a. 属性名:name
b. 生成规则:rule
c. 属性值:value - 注意事项:
a. 属性名和生成规则之间用竖线|分隔
b. 生成规则是可选的
c. 生成规则是由7种格式的
ⅰ. ‘name|min-max’:value
ⅱ. name|count’:value
ⅲ. name|min-max.dmin-dmax’:value
ⅳ. name|min-max.dcount’:value
ⅴ. name|count.dcount’:value
ⅵ. name|+step’:value
d. 生成规则的含义需要依赖属性值的类型才能确定
ⅰ. 属性值用可以含有@占位符
ⅱ. 属性值还指定了最终值的初始值和类型 - 生成规则和示例:
a. 属性值是字符串 String
ⅰ. ‘name|min-max’:string:通过重复string生成一个字符串,重复次数发育等于min,小于等于max
ⅱ. name|count’:string:通过重复string生成一个字符串,重复次数等于count
b. 属性值是数字类型 Number
ⅰ. name|+1’:number:属性值自动加1,初始值为number
ⅱ. ‘name|min-max’:number:生成一个大于等于min、小于等于max的整数,属性值number只是用来确定类型的
ⅲ. name|min-max.dmin-dmax’:number:生成一个浮点数,整数的部分大于等于min、小于等于max,小数部分保留dmin到dmax
Mock.mock({
'number1|1-100.1-10':1,
'number2|123.1-10':1,
'number3|123.3':1,
'number4|123.10':1,
})
==>{
number1:12.92,
number2:123.51,
number3:123.777,
number4:123.1231031814
}
c. 属性值是布尔类型 Boolean
ⅰ. ‘name|1’:boolean:随机生成一个布尔值,值为true的概率是1/2,值为false的概率同样是1/2
ⅱ. ‘name|min-max’:value:随机生成一个布尔值,值为 value 的概率是 min / (min + max),值为 !value 的概率是 max / min+max).
d. 属性值是对象 Object
ⅰ. ‘name|count’:object:从属性值 object 中随机选取 count 个属性
ⅱ. ‘name|min-max’:object:从属性值 object 中随机选取 min 到 max个属性
e. 属性值为数组Array
ⅰ. ‘name|1’:array:从属性值 array 中随机选取 1个元素,作为最终值
ⅱ. ‘name|+1’:array:从属性值 array 中顺序选取 1个元素,作为最终值
ⅲ. ‘name|min-max’:array:通过重复属性值 array 生成一个新数组,重复次数大于等于 min,小于等于max。
ⅳ. ‘name|count’:array:通过重复属性值 array 生成一个新数组,重复次数为 count。
f. 属性值是函数 function
ⅰ. ‘name’:function :执行函数 function,取其返回值作为最终的属性值,函数的上下文为属性 name 所在的对象
g. 属性值为正则表达式:RegExp
ⅰ. ‘name’:regexp:根据正则表达式 regexp 反向生成可以匹配它的字符串。用于生成自定义格式的字符串
ⅱ. 例如:
Mock.mock({
'regexp1':/[a-z][A-Z][0-9]/,
'regexp2':/\2\W\s\S\d\D/,
'regexp3':/\d{5,10}/,
})
==>{
regexp1:pJ7,
regexp2:F)\fp1G,
regexp3:561659409
}
数据占位符定义规范
- 占位符只是在属性值字符串中占个位置,并不出现在最终的属性值中
- 占位符的格式为:
a. @占位符
b. @占位符(参数 [,参数]) - 注意:
a. 用@来标识其后的字符串是 占位符
b. 占位符引用的是 Mock.Random 中的方法
c. 通过 Mock.Random.extend() 来扩展自定义占位符
d. 占位也可以引用数据中的属性
e. 占位符会优先引用数据模板中的属性
f. 占位符支持 相对路径和绝对路径
模拟请求get、post
<script setup lang="ts">
import axios from 'axios'
import Mock from 'mockjs'
// 模拟get请求
var { userdata, newList, logininfo } = Mock.mock({
'userdata|1-10': [
{
'id|+1': 1,
name: '@cname',
'age|18-60': 1,
email: '@email(163.com)',
address: '@city(true)',
'sex|1': ['男', '女']
}
],
'newList|20-30': [
{
// @increment(1)自增1
id: '@increment(1)',
title: '@ctitle',
date: '@date(yyyy-MM-dd HH:mm:ss)',
// cparagraph,句子在1到3句
info: '@cparagraph(1,3)',
avatar: '@image(150X150)'
}
],
'logininfo|1': [
{
'name|1': 'zhangsan',
'password|1': '123456',
'age|1': 25,
token: '@guid',
email: '@email(163.com)',
address: '@city(true)',
avatar: '@image(150X150)',
'sex|1': '男'
}
]
})
Mock.mock('/api/userinfo', 'get', () => {
return {
code: 200,
message: '请求成功',
data: userdata
}
})
Mock.mock('/api/newList', 'get', () => {
return {
code: 200,
message: '请求成功',
data: newList
}
})
// 发送get请求
const btn1 = async () => {
const res = await axios.get('/api/userinfo')
console.log(res)
}
const btn2 = async () => {
const res = await axios.get('/api/newList')
console.log(res)
}
// post请求,又参数的
Mock.mock('/api/login', 'post', (data) => {
// 我们获取到的是json格式的字符串,需要转换一下
const user = JSON.parse(data.body)
console.log(user)
if (user.username === 'zhangsan' && user.password == '123456') {
return {
code: 200,
message: '请求成功',
data: logininfo
}
} else if (user.username === 'zhangsan' || user.password! == '123456') {
return { code: 400, message: '用户密码错误', data: logininfo }
} else {
return {
code: 500,
error: '账号不存在'
}
}
})
const btn3 = async () => {
const res = await axios.post('/api/login', { username: 'zhangsan', password: 123456 })
console.log(res)
}
</script>
<template>
<div>
<el-button @click="btn1">发送get请求1</el-button>
<el-button @click="btn2">发送get请求2</el-button>
<el-button @click="btn3">发送post请求2</el-button>
</div>
</template>
模拟实现登录功能
- 模拟接口
mock/login.ts文件
import Mock from 'mockjs'
export const { logininfo } = Mock.mock({
'logininfo|1': [
{
'name|1': 'zhangsan',
'password|1': '123456',
'age|1': '25',
token: '@guid',
email: '@email(163.com)',
address: '@city(true)',
avatar: '@image(150X150)',
sex: '男',
})
mock/index.ts文件
import Mock from 'mockjs'
import { logininfo, putong } from './login'
Mock.setup({
//模拟接口的响应时间
timeout: 2000
})
Mock.mock('/api/login', 'post', (data) => {
// 我们获取到的是json格式的字符串,需要转换一下
const user = JSON.parse(data.body)
console.log(user)
if (user.username === 'zhangsan' && user.password == '123456') {
return {
success: true,
code: 200,
message: '请求成功',
data: logininfo
}
} else if (user.username === 'zhangsan' && user.password !== '123456') {
return { success: false, code: 400, message: '用户密码错误', data: logininfo }
} else {
return {
success: false,
code: 400,
message: '账号不存在'
}
}
})
- 封装接口
登录页面类型声明文件 types/login.d.ts
export type loginfo = {
username: string
password: string
}
export type userinfo = {
name: string
passwoed: string
age: string
token: string
email: string
address: string
avatar: string
sex: string
}
封装接口api
import request from '@/utils/request'
import type { userinfo, loginfo } from '@/types/login'
export const loginAPI = (data: loginfo) => request.post<userinfo>('/api/login', data)
- 调用接口
登录页面
<script setup lang="ts">
//cpIcon是封装的svg图片的相关组件
import cpIcon from '@/components/cp-icon.vue'
import avatarURL from '@/assets/logo.jpg'
import { loginAPI } from '@/api/login'
import type { loginfo } from '@/types/login'
import { ref, reactive } from 'vue'
import type { ElForm } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { userInfoStore } from '@/stores/index'
// router就是路由实例
const router = useRouter()
const store = userInfoStore()
// 图片地址
const state = ref({
url: avatarURL
})
const loading = ref<boolean>(false)
// 登录的表单信息
const loginform = ref<loginfo>({
username: '',
password: ''
})
// 表单校验
const rules = reactive<InstanceType<typeof FormRules>>({
username: [{ required: true, message: '请输入账户信息', trigger: ['blur', 'change'] }],
password: [
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
{ min: 5, max: 15, message: '密码必须是5-15位', trigger: ['blur', 'change'] }
]
})
// 表单的dom元素
// eslint-disable-next-line no-redeclare
type FormInstance = InstanceType<typeof ElForm>
const loginformref = ref<FormInstance>()
//登录功能的实现核心代码
const login = async (formEl: FormInstance | undefined) => {
loading.value = true
if (!formEl) return
await formEl.validate((valid: any) => {
if (valid) {
loading.value = true
loginAPI(loginform.value)
.then((res) => {
store.setuserInfo(res.data)
router.push('/')
ElMessage.success('登录成功')
})
.catch()
.finally(() => {
loading.value = false
})
// 如果有回跳地址就进行回跳,没有跳转到首页
// router.push((route.query.returnUrl as string) || '/')
} else {
ElMessage.warning('请输入正确的用户名和密码')
loading.value = false
}
})
}
</script>
<template>
<div class="login">
<div class="login-main">
<el-form :model="loginform" :rules="rules" ref="loginformref">
<el-form-item>
<el-avatar :size="128" :src="state.url" />
</el-form-item>
<el-form-item prop="username">
<el-input placeholder="账户" v-model="loginform.username">
<template #prefix>
<cp-icon name="login-user"></cp-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="密码" type="password" show-password v- model="loginform.password">
<template #prefix>
<cp-icon name="login-password"></cp-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="check">
<el-checkbox>自动登录</el-checkbox>
<el-link type="primary" :underline="false">忘记密码</el-link>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login(loginformref)" :loading="loading">登录</el-button>
</el-form-item>
<el-form-item class="register">
<el-link type="primary" :underline="false">注册账号</el-link>
</el-form-item>
</el-form>
</div>
</div>
<!-- <cp-icon name="login-user"></cp-icon> -->
</template>
<style lang="scss" scoped>
.login {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: url(../../icons/login/bg.svg) no-repeat center;
background-size: 100%;
.login-main {
width: 368px;
.el-avatar {
margin: 0 auto;
margin-bottom: 81px;
border-radius: 50%;
}
.el-button {
width: 368px;
}
}
}
::v-deep() {
.check {
.el-form-item__content {
display: flex;
justify-content: space-between;
}
}
.register {
.el-form-item__content {
display: flex;
justify-content: flex-end;
}
}
}
</style>