目录
一.接口地址
二.apipost 接口测试,能否接通
三.安装axiox
1.下载安装依赖
2.新建src/utils/request.ts文件
2.1解释:后续后端真实接口需要替换baseURL,目前没有使用配置文件,后续更换
3.新建src/utils/storage.ts文件
4.新建src/utils/formDataFormat.ts文件,
5.修改vite.config.ts文件,添加代理转发
5.1解释:后面后端真实接口直接替换即可
6.新建src/api/login 文件夹
7.目录结构
二.测试使用
1.修改login.vue文件
2.测试效果
三.一些说明修改
1.stores/interface/index.ts修改
2.stores/states.ts修改
一.接口地址
fastmock接口项目地址:
https://www.fastmock.site/mock/b9536a1dea3fe4daeec18bc365e14a18/api
登录接口 ,完整引用路径(apipost测试时使用):
https://www.fastmock.site/mock/b9536a1dea3fe4daeec18bc365e14a18/api/login
相对路径,后面在api/login/index.ts里使用
/login
二.apipost 接口测试,能否接通
进入软件,新建一个接口即可,测试如下
登录信息,注意是body的raw的json格式
{
"username":"admin",
"password":"admin"
}
三.安装axiox
1.下载安装依赖
npm install axios
npm install @types/axios --save-dev用来使用Cookie
npm install js-cookie
npm install @types/js-cookie --save-dev
2.新建src/utils/request.ts文件
根据需求,相应的更改
2.1解释:后续后端真实接口需要替换baseURL,目前没有使用配置文件,后续更换
import axios from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '@/utils/storage';// 配置新建一个 axios 实例
const service = axios.create({baseURL: 'https://www.fastmock.site/mock/b9536a1dea3fe4daeec18bc365e14a18/api',timeout: 50000,headers: { 'Content-Type': 'application/json' },
});// 添加请求拦截器
service.interceptors.request.use((config) => {// 在发送请求之前做些什么 tokenif (Session.get('token')) {(<any>config.headers).common['token'] = `${Session.get('token')}`;}return config;},(error) => {// 对请求错误做些什么return Promise.reject(error);}
);// 添加响应拦截器
service.interceptors.response.use((response) => {// 对响应数据做点什么const res = response.data;if (res.code && res.code !== '2000') {// `token` 过期或者账号已在别处登录if (res.code === 401 || res.code === "4001") {Session.clear(); // 清除浏览器全部临时缓存window.location.href = '/'; // 去登录页ElMessageBox.alert('你已被登出,请重新登录', '提示', {}).then(() => {}).catch(() => {});}return response.data;// return Promise.reject(service.interceptors.response);} else {return response.data;}},(error) => {// 对响应错误做点什么if (error.message.indexOf('timeout') != -1) {ElMessage.error('网络超时');} else if (error.message == 'Network Error') {ElMessage.error('网络连接错误');} else {if (error.response.data) ElMessage.error(error.response.statusText);else ElMessage.error('接口路径找不到');}return Promise.reject(error);}
);// 导出 axios 实例
export default service;
3.新建src/utils/storage.ts文件
持久化处理
import Cookies from 'js-cookie';/*** window.localStorage 浏览器永久缓存* @method set 设置永久缓存* @method get 获取永久缓存* @method remove 移除永久缓存* @method clear 移除全部永久缓存*/
export const Local = {// 设置永久缓存set(key: string, val: any) {window.localStorage.setItem(key, JSON.stringify(val));},// 获取永久缓存get(key: string) {let json: any = window.localStorage.getItem(key);return JSON.parse(json);},// 移除永久缓存remove(key: string) {window.localStorage.removeItem(key);},// 移除全部永久缓存clear() {window.localStorage.clear();},
};/*** window.sessionStorage 浏览器临时缓存* @method set 设置临时缓存* @method get 获取临时缓存* @method remove 移除临时缓存* @method clear 移除全部临时缓存*/
export const Session = {// 设置临时缓存set(key: string, val: any) {if (key === 'token') return Cookies.set(key, val);window.sessionStorage.setItem(key, JSON.stringify(val));},// 获取临时缓存get(key: string) {if (key === 'token') return Cookies.get(key);let json: any = window.sessionStorage.getItem(key);return JSON.parse(json);},// 移除临时缓存remove(key: string) {if (key === 'token') return Cookies.remove(key);window.sessionStorage.removeItem(key);},// 移除全部临时缓存clear() {Cookies.remove('token');window.sessionStorage.clear();},
};
4.新建src/utils/formDataFormat.ts文件,
处理访问接口时传递参数格式
export function objectToFormData(obj: Record<string, any>, form?: FormData, namespace?: string): FormData {const fd = form || new FormData();let formKey: string;for (const property in obj) {if (obj.hasOwnProperty(property)) {const key = Array.isArray(obj) ? '[]' : `[${property}]`;if (namespace) {formKey = `${namespace}${key}`;} else {formKey = property;}// 如果属性是对象,但不是 File 对象,则递归调用 objectToFormData 处理嵌套对象if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {objectToFormData(obj[property], fd, formKey);} else {// 如果属性值是字符串或 File 对象,直接添加到 FormDatafd.append(formKey, obj[property]);}}}return fd;
}
5.修改vite.config.ts文件,添加代理转发
5.1解释:后面后端真实接口直接替换即可
添加部分
server: {host: '0.0.0.0',port: 5000,open: true,proxy: {'/api': {target: 'https://www.fastmock.site/mock/b9536a1dea3fe4daeec18bc365e14a18/api',ws: true,changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, ''),},},},
目前为止的完整文件内容
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'// 以下为按需导入,自动引入手动导入element plus
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
//手动导入element plus 时
// import ElementPlus from 'unplugin-element-plus/vite'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),// ElementPlus({// } ),//手动导入element plus 时// 以下为按需导入,自动引入手动导入element plusAutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],resolve: {extensions: ['.vue', '.ts'],alias: {"@": path.resolve(__dirname, "src"),}},server: {host: '0.0.0.0',port: 8888,open: true,proxy: {'/api': {target: 'https://www.fastmock.site/mock/b9536a1dea3fe4daeec18bc365e14a18/api',ws: true,changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, ''),},},},
})
6.新建src/api/login 文件夹
新建src/api/login/index.ts文件以及src/api/login/types.ts文件
// login/index.ts
import request from '@/utils/request';
import { objectToFormData } from "@/utils/formDataFormat";// post请求参数是form-data时,使用此方法处理数据
import { LoginState } from './types';/*** 登录api接口集合* @method login用户登录* @method logout用户退出登录*/
export function UseLoginApi() {return {login: (params: LoginState) => {return request({url: '/login',method: 'post',data: objectToFormData(params),});},// logout: (params: object) => {// return request({// url: '/logout',// method: 'post',// data: objectToFormData(params),// });// },};
}
// login/types.ts
// 登录参数类型
export interface LoginState{username:string;password:string
}
// 拿到的用户数据类型定义
export interface UserInfoState{user:string;ID:string;age:string | number ;sex:string
}
7.目录结构
二.测试使用
1.修改login.vue文件
添加内容
import { UseLoginApi } from "@/api/login/index"
import { LoginState } from "@/api/login/types";const useLoginApi = UseLoginApi()
const login = () => {let data: LoginState = {username: "admin",password: "admin"}useLoginApi.login(data).then((res) => {console.log(res);}).catch((err) => {console.log(err);})
}
完整login.vue文件内容
<template>部分
<template><div class="loginForm"><div class="rloginTitle">登录</div><!-- 各个输入框 --><el-form :model="state.formData" :rules="formRules" ref="formRef" label-width="50px"><el-form-item prop="username"><div class="formInput"><el-input v-model="state.formData.username" placeholder="username" clearable autocomplete="off"prefix-icon="User"></el-input></div></el-form-item><el-form-item prop="password"><div class="formInput"><el-input v-model="state.formData.password" placeholder="password" type="password" show-passwordautocomplete="off" prefix-icon="Lock"></el-input></div></el-form-item><!-- 验证码 --><el-form-item><div class="formInput codeLine"><el-input class="codeInput" @input="checkCodeFun" v-model="checkCode.code" placeholder="输入验证码" clearablemaxlength="4" /><slideVerify class="codeShow" v-model:identifyCode="identifyCode" @click="refresh()"></slideVerify></div></el-form-item><!-- 登陆界面 --><el-form-item><div class="formInput settings"><label class="labelText"><input type="checkbox" v-model="rem_pswd" /> 记住密码</label><label class="labelText"><span @click.stop="go_page('findPassword')">忘记密码?</span><span @click.stop="go_page('register')">注册</span></label></div></el-form-item><!-- 提交按钮 --><el-form-item label-width="0"><el-button :loading="state.loading" :disabled="!checkCode.isTrue" type="primary" class="loginButton" round@click="handleSubmita(formRef)">登录</el-button></el-form-item></el-form></div>
</template>
后续
<script setup lang="ts">
import { UseLoginApi } from "@/api/login/index"
import { LoginState } from "@/api/login/types";const useLoginApi = UseLoginApi()
const login = () => {let data: LoginState = {username: "admin",password: "admin"}useLoginApi.login(data).then((res) => {console.log(res);}).catch((err) => {console.log(err);})
}import { reactive, ref } from 'vue';const state = reactive({loading: false,formData: {username: "admin",password: "admin"}
})
const formRef = ref<FormInstance>(); // 创建一个 ref 来引用表单
const formRules = reactive<FormRules<typeof state.formData>>({username: [{ required: true, message: 'Please input activity form', trigger: 'blur' }],password: [{ required: true, message: 'Please input activity form', trigger: 'blur' }],
})// 记住密码,true 或 false
const rem_pswd = ref(localStorage.getItem('rem_pswd'))const go_page = (path: string) => {router.push({name: 'login', // 替换为目标路由的名称或路径query: {path: path}})
}const handleSubmita = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.validate((valid: any, fields: any) => {if (valid) {login()console.log('login');} else {console.log('error submit!', fields)}})
}// 生成验证码以及检测
import slideVerify from "@/components/slideVerify.vue"
import { FormInstance, FormRules } from 'element-plus';
import router from '@/router';
interface CheckCodeType {code: string;isTrue: boolean;
}// 生成随机验证码
const generateCode = () => {const code = ref('');const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";for (let i = 0; i < 4; i++) {code.value += characters.charAt(Math.floor(Math.random() * characters.length));}return code.value;
}
// 传递 验证码给子组件
const identifyCode = ref(generateCode())
// 刷新验证码
const refresh = () => {identifyCode.value = generateCode()
}
// 检测输入的验证码,控制提交按钮是否可用
const checkCodeFun = (newValue: string) => {if ((newValue as string).toUpperCase() === identifyCode.value) {checkCode.isTrue = true} else {checkCode.isTrue = false}
}
const checkCode = reactive<CheckCodeType>({code: identifyCode.value,isTrue: false
})
</script><style scoped>
/* 可以添加一些样式 */
.loginForm {height: 400px;border: 1px solid;border-radius: 10px;text-align: center;background-color: #f3f3f3;
}.loginForm .rloginTitle {font-size: 23px;margin: 5%;
}.loginButton {width: 250px;margin: auto;margin-top: 20px;
}.formInput {width: 80%;min-width: 50px;
}.codeLine {display: flex;
}.codeLine .codeInput {flex: 2;}.codeLine .codeShow {flex: 1;}.settings {display: flex;justify-content: space-between;
}.settings label {flex: 1;font-size: 16px;
}.settings .labelText {cursor: pointer;
}.btn-base {width: 100%;
}.btn-flex {display: flex;justify-content: center;
}
</style>
目前来说,直接测试是有效果的
2.测试效果
点击登陆后,收到的数据打印成功,
控制台-》网路查看,的确是访问了接口
三.一些说明修改
还记得之前安装pinia时,测试定义了一个userInfoState类型在stores/interface/index.ts里,
这个是用户信息的类型,我么这次转移到了api/login/types.ts里了,所以要有一些改动,
如果是跟接口访问的数据类型,或是接口返回的数据类型有关定义,我们可以在相应的
api/XXX/types.ts里定义,如果是其他的一些状态,我们再定义到stores/interface/index.ts里
1.stores/interface/index.ts修改
// 示例
export interface exampleState {parmas1:string;parmas2:string
}
2.stores/states.ts修改
// 在 src/store/index.js 中创建一个简单的 store
import { UserInfoState } from '@/api/login/types';
import { defineStore } from 'pinia'interface State {userForm: UserInfoState;
}export const useMyStore = defineStore('myStore', {state: (): State => ({userForm: {user: '',ID: '',age: '',sex: ''},// 其他状态}),actions: {// 动作setUserInfo(data:UserInfoState) {this.userForm = data},},
})