vue3+ts 实现文件上传(包含上传、下载、和预览功能),番外(接口请求封装)

公共组件
上传文件包含上传、下载、和预览功能

复制代码
<!-- 上传文件 -->
<template>
	<div class="demo-image__preview">
		<el-image-viewer
			hide-on-click-modal
			@close="
				() => {
					showViewer = false;
				}
			"
			v-if="showViewer"
			:url-list="srcList"
		/>
	</div>
	<el-upload
		style="width: 100%"
		action=""
		v-model:file-list="fileList"
		class="upload-demo"
		:http-request="uploadFile"
		multiple
		:on-preview="handlePreview"
		:on-success="handleSuccess"
		:on-remove="handleRemove"
		:on-error="handleError"
		:before-upload="handleBefore"
		:limit="attr.limit"
		:on-exceed="handleExceed"
		ref="fileRef"
		:disabled="attr.readonly"
	>
		<el-button type="primary" v-if="!attr.readonly">上传</el-button>
	</el-upload>
	<el-dialog title="查看视频" v-model="videoShow">
		<video
			ref="veo"
			@click.prevent.once="onPlay"
			@loadeddata="poster ? () => false : getPoster()"
			:src="src"
			:autoplay="autoplay"
			controls="true"
			style="width: 100%; height: 100%"
		></video>
	</el-dialog>
</template>

<script setup lang="ts" name="ImportExcel">
import { uploadFile as uploadFun, downloadFileByFileId } from "@/api/modules/upload";
import type { UploadProps, UploadUserFile, UploadRawFile } from "element-plus";
import { ElMessage, genFileId } from "element-plus";
const emit = defineEmits(["update:modelValue"]);
const videoShow = ref(false);
const props = defineProps({
	modelValue: [Array],
	attr: {
		type: Object,
		required: false,
		default: () => {}
	}
});

const fileRef = ref<any>(null);
//上传文件
function uploadFile(params: any) {
	let formData = new FormData();
	//传值
	formData.append("file", params.file);
	uploadFun(formData).then(res => {
		if (res.code == 200) {
			params.onSuccess(res.data);
		} else {
			params.onError(res.data);
		}
	});
}
function handleBefore(row: any) {
	if (row.size / 1024 / 1024 > 100) {
		ElMessage.error("文件大小不能超过100M");
		return false;
	}
	return true;
}
const fileList = ref<UploadUserFile[]>([]);
//上传成功之后的操作
const handleSuccess = (res: any, file: UploadUserFile, fileList: UploadUserFile[]) => {
	console.log(file);
	file.uid = res.id;
	let arr = fileList.map(item => {
		let obj: any = {};
		obj[props.attr.name] = item.name;
		obj[props.attr.id] = item.uid;
		return obj;
	});
	console.log(fileList);
	emit("update:modelValue", arr);
};

watch(
	() => props.modelValue,
	val => {
		if (val) {
			fileList.value = val.map((item: any) => {
				item.name = item[props.attr.name];
				item.uid = item[props.attr.id];
				// item.response = item;
				return item;
			});
		} else {
			fileList.value = [];
			return [];
		}
	},
	{ deep: true, immediate: true }
);

const veo = ref();

const originPlay = ref(true);
const autoplay = ref(false);
const hidden = ref(false); // 是否隐藏播放器中间的播放按钮
const second = ref(0.5);

const poster = ref("");
const getPoster = () => {
	// 在未设置封面时,自动截取视频0.5s对应帧作为视频封面
	// 由于不少视频第一帧为黑屏,故设置视频开始播放时间为0.5s,即取该时刻帧作为封面图
	veo.value.currentTime = second.value;
	// 创建canvas元素
	const canvas = document.createElement("canvas");
	const ctx = canvas.getContext("2d");
	// canvas画图
	canvas.width = veo.value.videoWidth;
	canvas.height = veo.value.videoHeight;
	ctx?.drawImage(veo.value, 0, 0, canvas.width, canvas.height);
};
//是否播放
const onPlay = () => {
	if (originPlay.value) {
		veo.value.currentTime = 0;
		originPlay.value = false;
	}
	if (autoplay.value) {
		veo.value?.pause();
	} else {
		hidden.value = true;
		veo.value?.play();
	}
};
const isView = (ext: any) => {
	return ["png", "jpg", "jpeg", "bmp", "gif", "webp", "psd", "svg"].indexOf(ext.toLowerCase()) !== -1;
};
//上传失败
const handleError = (res: any) => {
	console.log(res);
	ElMessage.error({ message: res.msg || "上传失败!" });
};
const src = ref("");
const srcList = ref([] as any[]);
const showViewer = ref(false);
//下载文件
const handlePreview: UploadProps["onPreview"] = uploadFile => {
	let suffix = uploadFile.name.substring(uploadFile.name.lastIndexOf(".") + 1);
	if (suffix == "mp4") {
		videoShow.value = true;
		src.value = "/api/dems-resource/file/loadOnlineVideo?id=" + uploadFile.uid;
		return;
	}
	if (isView(suffix)) {
		src.value = "/api/dems-resource/file/loadOnlineImage?id=" + uploadFile.uid;
		srcList.value = [src.value];
		showViewer.value = true;
		return;
	}
	downloadFileByFileId(uploadFile.name, uploadFile.uid + "");
};
//删除文件
const handleRemove: UploadProps["onRemove"] = (uploadFile, uploadFiles) => {
	let arr = uploadFiles.map(item => {
		let obj: any = {};
		obj[props.attr.name] = item.name;
		obj[props.attr.id] = item.uid;
		return obj;
	});
	emit("update:modelValue", arr);
};

const handleExceed: UploadProps["onExceed"] = files => {
	if (props.attr.limit == 1) {
		fileRef.value!.clearFiles();
		const file = files[0] as UploadRawFile;
		file.uid = genFileId();
		fileRef.value!.handleStart(file);
		fileRef.value!.submit();
	}
};
</script>
<style lang="scss" scoped>
.upload {
	width: 100%;
}
:deep(.el-dialog .el-dialog__header) {
	border-bottom: none !important;
}
</style>

main.ts全局引用

复制代码
import customUpload from "@/components/Upload/custom-upload.vue";
app.component("CustomUpload", customUpload);

组件页面使用

复制代码
多文件
	<custom-upload v-model="formInline.attachmentResultList" :attr="attrResult" />
js
const attrResult = ref({
	id: "fileUploadId",
	name: "attachmentName",
	limit: 999,
	readonly: true
});

单文件

复制代码
	<custom-upload v-model="faultList" :attr="attr" />
	const attr = ref({
	id: "reportFileUploadId",
	name: "faultReport",
	limit: 1,
	readonly: false
});

子组件方法
//获取故障文件信息(永远只能上传一个,然后可以替换那一个文件)
const getBreakdownData = () => {
	if (faultList.value.length > 0) {
		let file = faultList.value[0];
		formInline.value.faultReport = file.faultReport;
		formInline.value.reportFileUploadId = file.reportFileUploadId;
	}
	getFaultDurationStr();
	return formInline.value;
};
defineExpose({
	getBreakdownData
});
父组件方法
const failureRecordSn = ref("");
save(){
	// 接收故障记录
		formInline.value.failureRecord = breakdownRef.value.getBreakdownData();
		formInline.value.failureRecord!.failureRecordSn = failureRecordSn.value;
		let failureRecord = { ...formInline.value.failureRecord };
		// 故障记录为空时删除
		if (!Object.values(failureRecord).some(i => !!i)) {
			delete formInline.value.failureRecord;
		}
}

用到的文件
upload.ts

复制代码
//文件的接口类型
import { Upload } from "@/api/interface/index";
api封装
import http from "@/api";
//前缀
import { UPLOAD_FILE } from "@/api/config/servicePort";
import { ElMessage } from "element-plus";
//上传文件
export const uploadFile = (params: FormData) => {
	return http.upload<Upload.ResFileList>(UPLOAD_FILE + `/file/uploadFile`, params, {
		headers: { "Content-Type": "multipart/form-data" }
	});
};
// * 用文件名称下载文件
export const downloadFileByFileId = (fileName: string, id: string) => {
	http.download(UPLOAD_FILE + `/file/downloadFileByFileId`, { fileName, fileId: id }).then(res => {
		ElMessage.success({ message: "下载成功!" });
		const blob = new Blob([res]); //处理文档流
		const link = document.createElement("a");
		link.download = fileName;
		link.style.display = "none";
		link.href = URL.createObjectURL(blob);
		document.body.appendChild(link);
		link.click();
		URL.revokeObjectURL(link.href); // 释放URL 对象
		document.body.removeChild(link);
	});
};

定义接口interface
import { Upload } from "@/api/interface/index";
接口如下

复制代码
// * 文件上传模块
export namespace Upload {
	export interface ResFileUrl {
		fileUrl: string;
	}
	export interface ResFileList {
		id: string;
		fileName: string;
		fileUrl: string;
		uploadTime: string;
		operator: string;
		fileType: string;
		fileSize: number;
		fileOldname: string;
	}
}

http在api/index.ts文件里面
包含拦截器,响应器和请求方式的封装
import http from "@/api";

复制代码
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { showFullScreenLoading, tryHideFullScreenLoading } from "@/config/serviceLoading";
import { ResultData, GatewayResultData } from "@/api/interface";
import { ResultEnum } from "@/enums/httpEnum";
import { checkStatus } from "./helper/checkStatus";
import { ElMessage, ElMessageBox } from "element-plus";
import { GlobalStore } from "@/stores";
import { CallCenterStore } from "@/stores/modules/ccs";//这个可以不要(其他功能的)
import { LOGIN_URL } from "@/config/config";//主题颜色(// * 登录页地址(默认)
export const LOGIN_URL: string = "/login";)
import { encodeHtml, decodeHtml } from "@/utils/htmlUtil";//富文本
import router from "@/routers";
import qs from "qs";
const config = {
	// 默认地址请求地址,可在 .env.*** 文件中修改
	baseURL: import.meta.env.VITE_API_URL as string,
	// 设置超时时间(10s)
	timeout: ResultEnum.TIMEOUT as number,
	// 跨域时候允许携带凭证
	withCredentials: true
};

class RequestHttp {
	service: AxiosInstance;
	public constructor(config: AxiosRequestConfig) {
		// 实例化axios
		this.service = axios.create(config);

		/**
		 * @description 请求拦截器
		 * 客户端发送请求 -> [请求拦截器] -> 服务器
		 * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
		 */
		this.service.interceptors.request.use(
			(config: InternalAxiosRequestConfig) => {
				encodeHtml(config);
				const globalStore = GlobalStore();
				// * 如果当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { headers: { noLoading: true } }来控制不显示loading,参见loginApi
				config.headers!.noLoading || showFullScreenLoading();
				const token = globalStore.token;
				if (config.headers && typeof config.headers?.set === "function") config.headers.set("Authorization", token);
				return config;
			},
			(error: AxiosError) => {
				return Promise.reject(error);
			}
		);

		/**
		 * @description 响应
		 *  服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
		 */
		this.service.interceptors.response.use(
			(response: AxiosResponse) => {
				const { data } = response;
				const globalStore = GlobalStore();
				const callCenterStore = CallCenterStore();
				// * 在请求结束后,并关闭请求 loading
				tryHideFullScreenLoading();
				// * 登陆失效(code == 401)
				if (data.code == ResultEnum.OVERDUE) {
					if (document.getElementsByClassName("el-message").length == 0) {
						ElMessage.error(data.msg);
						//错误提示
					}
					globalStore.setToken("");
					globalStore?.webSocket?.close();
					globalStore.setWebSocket(null);
					callCenterStore.clearAllData();
					router.replace(LOGIN_URL);
					return Promise.reject(data);
				}
				// * 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)
				if (data.code && data.code !== ResultEnum.SUCCESS) {
					if (data.code > 200 && data.code < 300) {
						ElMessage.warning(data.msg);
					} else {
						if (data.code !== 502) {
							ElMessage.error(data.msg);
						}
					}
					return Promise.reject(data);
				}
				decodeHtml(data, response.config.url);
				// * 成功请求(在页面上除非特殊情况,否则不用在页面处理失败逻辑)
				return data;
			},
			async (error: AxiosError) => {
				tryHideFullScreenLoading();
				const { response } = error;
				const { data, code } = (response?.data as any) || {};
				if (code == 400) {
					let arr = [];
					for (let v in data) {
						arr.push(data[v]);
					}
					ElMessageBox.alert(arr.join("</br>"), "错误", {
						confirmButtonText: "确认",
						dangerouslyUseHTMLString: true
					});
					return Promise.reject();
				}
				// 由于后端的微服务网关报错时返回的是特殊响应数据,此处对登录失败的情况做特殊处理
				if (response?.config?.url === "auth/token/login") {
					return { data: response.data };
				}
				tryHideFullScreenLoading();
				// 上传失败如果不return 不进el-upload的on-error
				if (
					response?.config?.url === "/resource/file/uploadFiles" ||
					response?.config?.url === "/dems-resource/file/uploadFile"
				) {
					return { data: response?.data };
				}
				// 请求超时 && 网络错误单独判断,没有 response
				if (error.message.indexOf("timeout") !== -1) ElMessage.error("请求超时!请您稍后重试");
				if (error.message.indexOf("Network Error") !== -1) ElMessage.error("网络错误!请您稍后重试");
				// 根据响应的错误状态码,做不同的处理
				if (response && response.data) {
					let data = <any>response.data;
					let obj = data.body ?? data;
					if ("msg" in obj) {
						ElMessage.error(obj.msg);
					} else {
						checkStatus(response.status);
					}
				} else if (response) {
					checkStatus(response.status);
				}
				// * 登陆失效(code == 401)
				if (response?.status == ResultEnum.OVERDUE) {
					if (document.getElementsByClassName("el-message").length == 0) {
						ElMessage.error(data.msg);
						//错误提示
					}
					const globalStore = GlobalStore();
					globalStore.setToken("");
					globalStore?.webSocket?.close();
					globalStore.setWebSocket(null);
					const callCenterStore = CallCenterStore();
					callCenterStore.clearAllData();
					router.replace(LOGIN_URL);
				}
				// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
				if (!window.navigator.onLine) router.replace("/500");
				return Promise.reject(error);
			}
		);
	}

	// * 常用请求方法封装
	get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.get(url, { params, ..._object });
	}
	post<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.post(url, qs.stringify(params), _object);
	}
	postJson<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.post(url, params, _object);
	}
	put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.put(url, params, _object);
	}
	delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
		return this.service.delete(url, { params, ..._object });
	}
	download(url: string, params?: object, _object = {}): Promise<BlobPart> {
		return this.service.get(url, { params, ..._object, responseType: "blob", timeout: 0 });
	}
	getRequest<T>(url: string, params?: object, _object = {}): Promise<GatewayResultData<T>> {
		return this.service.get(url, { params, ..._object });
	}
	upload<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
		return this.service.post(url, params, { ..._object, timeout: 0 });
	}
}

export default new RequestHttp(config);

import { showFullScreenLoading, tryHideFullScreenLoading } from "@/config/serviceLoading";

复制代码
import { ElLoading } from "element-plus";

/* 全局请求 loading(服务方式调用) */
let loadingInstance: ReturnType<typeof ElLoading.service>;

/**
 * @description 开启 Loading
 * */
const startLoading = () => {
	loadingInstance = ElLoading.service({
		fullscreen: true,
		lock: true,
		text: "Loading",
		background: "rgba(0, 0, 0, 0.7)"
	});
};

/**
 * @description 结束 Loading
 * */
const endLoading = () => {
	loadingInstance.close();
};

/**
 * @description 显示全屏加载
 * */
let needLoadingRequestCount = 0;
export const showFullScreenLoading = () => {
	if (needLoadingRequestCount === 0) {
		startLoading();
	}
	needLoadingRequestCount++;
};

/**
 * @description 隐藏全屏加载
 * */
export const tryHideFullScreenLoading = () => {
	if (needLoadingRequestCount <= 0) return;
	needLoadingRequestCount--;
	if (needLoadingRequestCount === 0) {
		endLoading();
	}
};

interface接口

import { ResultData, GatewayResultData } from "@/api/interface";

复制代码
// * 请求响应参数(不包含data)
export interface Result {
	code: number;
	msg: string;
}

/**
 * 请求响应参数(包含data)
 */
export interface ResultData<T = any> extends Result {
	data: T;
}

/**
 * 请求响应参数(网关的特殊响应)
 */
export interface GatewayResultData<T = any> extends Result {
	body: T;
}

/**
 *  后端返回的分页响应参数
 */
export interface ResPage<T> {
	// 查询数据列表
	records: T[];
	// 当前页
	current: number;
	// 每页显示条数
	size: number;
	// 总数
	total: number;
}

/**
 * 分页请求参数
 */
export interface ReqPage {
	// 当前页
	pageNum: number;
	//每页记录数
	pageSize: number;
	// 排序字段
	orderByColumn?: string;
	// 排序的方向
	isAsc?: "asc" | "desc";
}

// * 文件上传模块
export namespace Upload {
	export interface ResFileUrl {
		fileUrl: string;
	}
	export interface ResFileList {
		id: string;
		fileName: string;
		fileUrl: string;
		uploadTime: string;
		operator: string;
		fileType: string;
		fileSize: number;
		fileOldname: string;
	}
}

枚举配置

import { ResultEnum } from "@/enums/httpEnum";

复制代码
// * 请求枚举配置
/**
 * @description:请求配置
 */
export enum ResultEnum {
	SUCCESS = 200,
	ERROR = 500,
	OVERDUE = 401,
	TIMEOUT = 10000,
	TYPE = "success"
}

/**
 * @description:请求方法
 */
export enum RequestEnum {
	GET = "GET",
	POST = "POST",
	PATCH = "PATCH",
	PUT = "PUT",
	DELETE = "DELETE"
}

/**
 * @description:常用的contentTyp类型
 */
export enum ContentTypeEnum {
	// json
	JSON = "application/json;charset=UTF-8",
	// text
	TEXT = "text/plain;charset=UTF-8",
	// form-data 一般配合qs
	FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8",
	// form-data 上传
	FORM_DATA = "multipart/form-data;charset=UTF-8"
}

状态检查
import { checkStatus } from "./helper/checkStatus";

复制代码
import { ElMessage } from "element-plus";

/**
 * @description: 校验网络请求状态码
 * @param {Number} status
 * @return void
 */
export const checkStatus = (status: number): void => {
	switch (status) {
		case 400:
			ElMessage.error("请求失败!请您稍后重试");
			break;
		case 401:
			ElMessage.error("登录失效!请您重新登录");
			break;
		case 403:
			ElMessage.error("当前账号无权限访问!");
			break;
		case 404:
			ElMessage.error("你所访问的资源不存在!");
			break;
		case 405:
			ElMessage.error("请求方式错误!请您稍后重试");
			break;
		case 408:
			ElMessage.error("请求超时!请您稍后重试");
			break;
		case 500:
			ElMessage.error("服务异常!");
			break;
		case 502:
			ElMessage.error("网关错误!");
			break;
		case 503:
			ElMessage.error("服务不可用!");
			break;
		case 504:
			ElMessage.error("网关超时!");
			break;
		default:
			ElMessage.error("请求失败!");
	}
};

import { GlobalStore } from "@/stores";

复制代码
import { defineStore, createPinia } from "pinia";
import { GlobalState, ThemeConfigProps, AssemblySizeType } from "./interface";
import { DEFAULT_PRIMARY } from "@/config/config";
import piniaPersistConfig from "@/config/piniaPersist";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const GlobalStore = defineStore({
	// id: 必须的,在所有 Store 中唯一
	id: "GlobalState",
	// state: 返回对象的函数
	state: (): GlobalState => ({
		// token
		token: "",
		// 用户信息
		userInfo: "",
		// webSocket 对象
		webSocket: null,
		// element组件大小
		assemblySize: "default",
		// language
		language: "",
		// isAdmin
		isAdmin: false,
		// themeConfig
		themeConfig: {
			// 当前页面是否全屏
			maximize: false,
			// 布局切换 ==>  纵向:vertical | 经典:classic | 横向:transverse | 分栏:columns
			layout: "vertical",
			// 默认 primary 主题颜色
			primary: DEFAULT_PRIMARY,
			// 深色模式
			isDark: false,
			// 灰色模式
			isGrey: false,
			// 色弱模式
			isWeak: false,
			// 折叠菜单
			isCollapse: false,
			// 面包屑导航
			breadcrumb: true,
			// 面包屑导航图标
			breadcrumbIcon: true,
			// 标签页
			tabs: true,
			// 标签页图标
			tabsIcon: true,
			// 页脚
			footer: true
		}
	}),
	getters: {},
	actions: {
		// setToken
		setToken(token: string) {
			this.token = token;
		},
		// setUserInfo
		setUserInfo(userInfo: any) {
			this.userInfo = userInfo;
		},
		// setWebSocket
		setWebSocket(webSocket: WebSocket | null) {
			this.webSocket = webSocket;
		},
		// setAssemblySizeSize
		setAssemblySizeSize(assemblySize: AssemblySizeType) {
			this.assemblySize = assemblySize;
		},
		// updateLanguage
		updateLanguage(language: string) {
			this.language = language;
		},
		// setThemeConfig
		setThemeConfig(themeConfig: ThemeConfigProps) {
			this.themeConfig = themeConfig;
		},
		setIsAdmin(isAdmin: boolean) {
			this.isAdmin = isAdmin;
		}
	},
	persist: piniaPersistConfig("GlobalState")
});

// piniaPersist(持久化)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

export default pinia;

import { GlobalState, ThemeConfigProps, AssemblySizeType } from "./interface";

复制代码
/* GlobalState */
export interface GlobalState {
	token: string;
	userInfo: any;
	webSocket: WebSocket | null;
	assemblySize: AssemblySizeType;
	language: string;
	themeConfig: ThemeConfigProps;
	isAdmin: boolean;
}

/* themeConfigProp */
export interface ThemeConfigProps {
	maximize: boolean;
	layout: LayoutType;
	primary: string;
	isDark: boolean;
	isGrey: boolean;
	isCollapse: boolean;
	isWeak: boolean;
	breadcrumb: boolean;
	breadcrumbIcon: boolean;
	tabs: boolean;
	tabsIcon: boolean;
	footer: boolean;
}

export type AssemblySizeType = "default" | "small" | "large";

export type LayoutType = "vertical" | "classic" | "transverse" | "columns";

/* tabsMenuProps */
export interface TabsMenuProps {
	icon: string;
	title: string;
	path: string;
	name: string;
	close: boolean;
}

/* TabsState */
export interface TabsState {
	tabsMenuList: TabsMenuProps[];
}

/* AuthState */
export interface AuthState {
	routeName: string;
	authButtonList: {
		[key: string]: string[];
	};
	authMenuList: Menu.MenuOptions[];
}

/* keepAliveState */
export interface keepAliveState {
	keepAliveName: string[];
}

import { DEFAULT_PRIMARY } from "@/config/config";

复制代码
// ? 全局不动配置项 只做导出不做修改

// * 首页地址(默认)
export const HOME_URL: string = "/home";

// * 登录页地址(默认)
export const LOGIN_URL: string = "/login";

// * 默认主题颜色
export const DEFAULT_PRIMARY: string = "#009688";

// * 路由白名单地址(必须是本地存在的路由 staticRouter.ts)
export const ROUTER_WHITE_LIST: string[] = ["/500"];

// * 高德地图 key
export const AMAP_MAP_KEY: string = "";

// * 百度地图 key
export const BAIDU_MAP_KEY: string = "";

import piniaPersistConfig from "@/config/piniaPersist";

复制代码
import { PersistedStateOptions } from "pinia-plugin-persistedstate";
/**
 * @description pinia持久化参数配置
 * @param {String} key 存储到持久化的 name
 * @param {Array} paths 需要持久化的 state name
 * @return persist
 * */
const piniaPersistConfig = (key: string, paths?: string[]) => {
	const persist: PersistedStateOptions = {
		key,
		// storage: localStorage,
		storage: sessionStorage,
		paths
	};
	return persist;
};

export default piniaPersistConfig;
相关推荐
WeiXiao_Hyy35 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js