书接上回
本篇主要介绍: Pinia + 全局loading + 公共方法
继续填充
Pinia
想了解Pinia的可看该博主的文章,挺详细的。此篇不做讲解,只讲实际应用
链接: Pinia基础知识
index.ts
import type { App } from "vue";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
const store = createPinia() as any;
// 创建Store时,将persistent选项设置为true,则整个Store将使用默认持久化配置保存
store.use(piniaPluginPersistedstate);
export const setupStore = (app: App<Element>) => {
app.use(store);
};
export { store };
user.ts 填前文中的坑,补充全局方法:token及login的全局变量及方法
import { defineStore } from "pinia";
import { store } from "./index";
import { UserLoginType, UserType } from "../api/login/types";
import type { RouteRecordRaw } from "vue-router";
import { Modal } from "ant-design-vue";
import { loginOutLocalApi } from "@/api/login";
import router from "@/router";
interface UserState {
userInfo?: UserType;
tokenKey: string;
token: string;
roleRouters?: string[] | AppCustomRouteRecordRaw[];
rememberMe: boolean;
loginInfo?: UserLoginType;
}
interface RouteMetaCustom extends Record<string | number | symbol, unknown> {
hidden?: boolean;
alwaysShow?: boolean;
title?: string;
icon?: string;
noCache?: boolean;
breadcrumb?: boolean;
affix?: boolean;
activeMenu?: string;
noTagsView?: boolean;
canTo?: boolean;
permission?: string[];
}
interface AppCustomRouteRecordRaw extends Omit<RouteRecordRaw, "meta" | "component" | "children"> {
name: string;
meta: RouteMetaCustom;
component: string;
path: string;
redirect: string;
children?: AppCustomRouteRecordRaw[];
}
export const useUserStore = defineStore("user", {
state: (): UserState => {
return {
userInfo: undefined,
tokenKey: "Authorization",
token: "",
roleRouters: undefined,
// 记住我
rememberMe: true,
loginInfo: undefined,
};
},
getters: {
getTokenKey(): string {
return this.tokenKey;
},
getToken(): string {
return this.token;
},
getUserInfo(): UserType | undefined {
return this.userInfo;
},
getRoleRouters(): string[] | AppCustomRouteRecordRaw[] | undefined {
return this.roleRouters;
},
getRememberMe(): boolean {
console.log(this.rememberMe);
return this.rememberMe;
},
getLoginInfo(): UserLoginType | undefined {
return this.loginInfo;
},
},
actions: {
setTokenKey(tokenKey: string) {
this.tokenKey = tokenKey;
},
setToken(token: string) {
this.token = token;
},
setUserInfo(userInfo?: UserType) {
this.userInfo = userInfo;
},
setRoleRouters(roleRouters: string[] | AppCustomRouteRecordRaw[]) {
this.roleRouters = roleRouters;
},
logoutConfirm() {
Modal.confirm({
title: "提示",
content: "是否退出本系统?",
okText: "确定",
cancelText: "取消",
onOk: () => {
const res = loginOutLocalApi();
if (res) {
this.reset();
}
},
});
},
reset() {
this.setToken("");
this.setUserInfo(undefined);
this.setRoleRouters([]);
router.replace("/");
},
logout() {
this.reset();
},
setRememberMe(rememberMe: boolean) {
this.rememberMe = rememberMe;
},
setLoginInfo(loginInfo: UserLoginType | undefined) {
this.loginInfo = loginInfo;
},
},
persist: true,
});
export const useUserStoreWithOut = () => {
return useUserStore(store);
};
此处为了接口的规范性,添加了interface。
在api文件夹中添加接口文件types.ts
export interface UserLoginType {
username: string
password: string
}
export interface UserType {
username: string
password: string
role: string
roleId: string
tokenInfo: {
token: string
}
}
全局loading
使用antd的Spin组件封装全局loading。
在前文的axios中使用,请求开始时调用Spin.show() 结束时调用Spin.hide()
import { createApp, h } from "vue";
import { Spin } from "ant-design-vue";
let instance = null as any;
// 定义全屏遮罩样式
const style = {
position: "fixed",
left: 0,
top: 0,
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
background: "rgba(255, 255, 255, 0.5)",
zIndex: 99999,
};
function getInstance() {
if (!instance) {
const vn = createApp({
data() {
return {
show: false,
message: "Loading...",
timeoutId: "loader",
};
},
unmounted() {
if (instance && instance.$el) {
instance.$el.remove(); // 当组件卸载时,从DOM中移除元素以避免内存泄漏
}
},
methods: {
loading(val: string, timeout: number | undefined) {
this.show = true;
this.message = val || "Loading...";
if (timeout) {
this.timeoutId = setTimeout(() => {
this.close();
}, timeout); // 超时时间单位为毫秒
}
},
close() {
clearTimeout(this.timeoutId); // 清除定时器
this.show = false;
},
},
render() {
return this.show ? h("div", { style }, [h(Spin, { tip: this.message })]) : null;
},
});
const ele = document.createElement("div");
instance = vn.mount(ele);
document.body.appendChild(ele);
}
return instance;
}
export default {
...Spin,
show(val = "Loading...", timeout = 0) {
getInstance().loading(val, timeout);
},
hide() {
getInstance().close();
},
};
公共方法
公共方法都放在utils中,常用的如下:
/**
* 生成随机字符串
*/
export function toAnyString() {
const str: string = "xxxxx-xxxxx-4xxxx-yxxxx-xxxxx".replace(/[xy]/g, (c: string) => {
const r: number = (Math.random() * 16) | 0;
const v: number = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString();
});
return str;
}
/**
* 首字母大写
*/
export function firstUpperCase(str: string) {
return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
}
// 字符串间隔天数,yyyyMMdd-yyyyMMdd
export function intervalDays(date1: any, date2: any) {
date1 = String(date1);
date2 = String(date2);
var d1 = date1.replace(/(\d{4})(\d{2})(\d{2})/gm, "$1-$2-$3");
var d2 = date2.replace(/(\d{4})(\d{2})(\d{2})/gm, "$1-$2-$3");
var h1 = new Date(d1);
var h2 = new Date(d2);
var days = h1.getTime() - h2.getTime();
var time = parseInt(days / (1000 * 60 * 60 * 24) + "");
return time;
}
// 传入Date 输出 yyyyMMdd 字符串类型格式
export function dateToStr1(date: any) {
if (date === null || date === "") {
return "";
}
const Y = date.getFullYear() + "";
const M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "";
const D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + "";
return Y + M + D;
}
// 传入Date 输出 yyyy-MM-dd 字符串类型格式
export function dateToStr2(date: any) {
const Y = date.getFullYear() + "";
const M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "";
const D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + "";
return "" + Y + "-" + M + "-" + D;
}
// 传入Date 输出 yyyyMMddhhmmss / yyyy-mm-dd hh:mm:ss 字符串类型格式
export function dateToStr3(date: any) {
const Y = date.getFullYear() + "";
const M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "";
const D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + "";
const hString = date.getHours().toString();
const mString = date.getMinutes().toString();
const sString = date.getSeconds().toString();
let h = "";
let m = "";
let s = "";
hString.length <= 1 ? (h = "0" + hString) : (h = hString);
mString.length <= 1 ? (m = "0" + mString) : (m = mString);
sString.length <= 1 ? (s = "0" + sString) : (s = sString);
return Y + M + D + h + m + s;
}
// 传入Date 输出 yyyy-mm-dd hh:mm:ss 字符串类型格式
export function dateToStr4(date: any) {
const Y = date.getFullYear() + "";
const M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "";
const D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + "";
const hString = date.getHours().toString();
const mString = date.getMinutes().toString();
const sString = date.getSeconds().toString();
let h = "";
let m = "";
let s = "";
hString.length <= 1 ? (h = "0" + hString) : (h = hString);
mString.length <= 1 ? (m = "0" + mString) : (m = mString);
sString.length <= 1 ? (s = "0" + sString) : (s = sString);
return Y + "-" + M + "-" + D + " " + h + ":" + m + ":" + s;
}
// 传入yyyyMMdd字符串类型格式 输出 Date
export function strToDate(str: string | number) {
const dateStr = String(str).replace(/^(\d{4})(\d{2})(\d{2})$/, "$1-$2-$3");
var date = new Date(dateStr.replace(/-/g, "/"));
return date;
}
// 传入yyyyMMdd字符串类型格式 输出yyyy-MM-dd
export function strToDateStr(str: string | number) {
const dateStr = str ? String(str).replace(/^(\d{4})(\d{2})(\d{2})$/, "$1-$2-$3") : "";
return dateStr;
}
// 传入两个yyyyMMdd字符串类型格式 ,输出两个日期的月份差,str1必须大于str2
export function dateToStr5(str1: string, str2: string) {
//先计算年份差距
var year = 0;
var month = 0;
//const year = parseInt(str1.substring(4, 6)) - parseInt(str2.substring(4, 6));
if (parseInt(str1.substring(4, 6)) < parseInt(str2.substring(4, 6))) {
year = parseInt(str1.substring(0, 4)) - 1 - parseInt(str2.substring(0, 4));
month = parseInt(str1.substring(4, 6)) + 12 - parseInt(str2.substring(4, 6));
console.log("1");
} else {
year = parseInt(str1.substring(0, 4)) - parseInt(str2.substring(0, 4));
month = parseInt(str1.substring(4, 6)) - parseInt(str2.substring(4, 6));
}
var day = year * 12 + month;
return day;
}
// 传入yyyyMMdd字符串类型格式 输出 yyyy-MM-dd
export function strToStr(value: string) {
let date = "";
if (!value) {
return "";
}
if (value.length == 8) {
const year = value.substring(0, 4);
const month = value.substring(4, 6);
const day = value.substring(6, 8);
date = year + "-" + month + "-" + day;
} else if (value.length == 6) {
const year = value.substring(0, 4);
const month = value.substring(4, 6);
date = year + "-" + month;
} else if (value.length == 4) {
const year = value.substring(0, 4);
date = year;
}
return date;
}
// n 天之前或之后的日期 输出Date类型(days可以正负)
export function getDaysAfterDate(date: Date, days: number) {
return new Date(date.getTime() - days * 24 * 60 * 60 * 1000);
}
// 输出当前 yyyy_MM_ddhhmmss 字符串类型格式 适用导出文件名
export function dateFileName() {
const index = new Date()
.toLocaleString("chinese", { hour12: false })
.toString()
.replace(new RegExp(":| ", "g"), "");
return index;
}
// 输入数字转成日期(5位,excel表格日期),默认转换成YYYY-MM-DD
export function numberToDate(number: number, format: string) {
if (number != undefined) {
let date = new Date((number - 1) * 24 * 3600000 + 1) as any;
date.setYear(date.getFullYear() - 70);
const Y = date.getFullYear();
const dateRel =
(Y % 4 === 0 && Y % 100 !== 0) || Y % 400 == 0
? new Date((number - 2) * 24 * 3600000 + 1)
: (new Date((number - 1) * 24 * 3600000 + 1) as any);
dateRel.setYear(date.getFullYear() - 70);
const M =
(dateRel.getMonth() + 1 < 10 ? "0" + (dateRel.getMonth() + 1) : dateRel.getMonth() + 1) + "";
const D = (dateRel.getDate() < 10 ? "0" + dateRel.getDate() : dateRel.getDate()) + "";
if (format && format.length == 1) {
return Y + format + M + format + D;
} else {
return Y + "-" + M + "-" + D;
}
} else {
return undefined;
}
}
// 判断值是否是数字
export function isRealNum(val: any) {
if (typeof val !== "number") {
return false;
}
if (!isNaN(val)) {
return true;
} else {
return false;
}
}
// 判断字符是否包含数字
export function regNumber(str: string) {
var reg = /\d+/;
if (reg.test(str)) {
return true;
} else {
return false;
}
}
// 判断一个字符串中是否包含字母
export function isLetter(str: any) {
for (var i in str) {
var asc = str.charCodeAt(i);
if ((asc >= 65 && asc <= 90) || (asc >= 97 && asc <= 122)) {
return true;
}
}
return false;
}
// 判断一个字符串中是否包含汉字
export function isChina(str: string) {
if (/.*[\u4e00-\u9fa5]+.*$/.test(str)) {
return true;
}
return false;
}
//判断是否包含特殊字符
export function isSpecialCharacter(str: string) {
if (str === "" || str === null) {
return false;
}
if (
str.indexOf("<") > -1 ||
str.indexOf(">") > -1 ||
str.indexOf("&") > -1 ||
str.indexOf("'") > -1 ||
str.indexOf('"') > -1 ||
str.indexOf("\\") > -1 ||
str.indexOf("/") > -1 ||
str.indexOf(":") > -1 ||
str.indexOf("*") > -1 ||
str.indexOf("?") > -1 ||
str.indexOf("|") > -1
) {
return true;
}
return false;
}
本篇结束,今日份的结束。后面文章会更新:Layout布局,导航栏,标签页。