mitt:事件总线,是第三方插件。
Vue2.x 使用 EventBus 事件总线进行兄弟组件通信,而在Vue3中 EventBus 事件总线模式已经被移除,官方建议使用外部的、实现了事件触发器接口的库,例如 mitt
或 tiny-emitter
。
比起 Vue 实例上的 EventBus,mitt.js 好在哪里呢?
- 首先它足够小,仅有200bytes。
- 其次支持全部事件的监听和批量移除。
- 它还不依赖 Vue 实例,可以跨框架使用,React 或者 Vue,甚至 jQuery 项目都能使用同一套库。
1、安装 mitt
npm install --save mitt
2、在公共文件 utils 下新建 mitter.ts文件,简单封装下 mitt
utils/mitter.ts
import mitt from 'mitt';
const mitter = mitt();
export default mitter;
3、使用
utils/request.ts
如果登录过期,派发 token-expired 事件。
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { showMessage } from './errorCode';
import { alertController } from '@ionic/vue';
import { showToast } from '.';
import { StorageService } from './storageService';
import mitter from './mitter';
let token: string = '';
// 是否显示重新登录
let isReloginShow: boolean = false;
const storageService = new StorageService();
storageService.create();
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_ENV == 'development' ? import.meta.env.VITE_APP_BASE_API : import.meta.env.VITE_APP_BASE_URL, // 设置API的基础URL
timeout: 10000, // 设置请求超时时间
});
const presentAlert = async () => {
const alert = await alertController.create({
header: '系统提示',
message: '登录状态已过期,您可以继续留在该页面,或者重新登录',
buttons: [
{
text: '取消',
role: 'cancel',
handler: () => {
console.log('Alert canceled');
isReloginShow = false;
},
},
{
text: '重新登录',
role: 'confirm',
handler: () => {
console.log('Alert confirmed');
isReloginShow = false;
mitter.emit('token-expired');
},
},
],
});
await alert.present();
};
// 请求拦截器
service.interceptors.request.use(
async (config: AxiosRequestConfig): Promise<any> => {
// showLoading();
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
const headers = config.headers || {};
token = await storageService.getItem("token");
if (token && !isToken) {
headers['Authorization'] = 'Bearer ' + token; // 让每个请求携带自定义token 请根据实际情况自行修改
// headers['token'] = token;
config.headers = headers;
}
// return new Promise((resolve, reject) => {
// resolve(config)
// })
return config;
},
(error: AxiosError) => {
// 处理请求错误
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
// 对响应数据进行处理
// 未设置状态码则默认成功状态
const code = response.data.code || 200;
// 获取错误信息
const msg = response.data.msg || showMessage(code) || showMessage('default');
// 二进制数据则直接返回
if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
return response.data
}
if (code === 401) {
if (!isReloginShow) {
isReloginShow = true;
presentAlert();
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
showToast(msg);
return Promise.reject(new Error(msg))
} else if (code !== 200) {
showToast(msg);
return Promise.reject('error')
} else {
return response.data
}
// return response;
},
(error: AxiosError) => {
// 处理响应错误
return Promise.reject(error);
}
);
export {
service as request
}
src/HomePage.vue
在首页监听 token-expired 登录过期事件, 在离开页面时移出监听事件。
<script setup lang="ts">
import {
IonPage,
IonSearchbar,
IonList,
IonItem,
IonLabel,
IonSegment,
IonSegmentButton,
IonIcon,
onIonViewWillEnter,
onIonViewDidEnter,
onIonViewWillLeave,
modalController,
IonNote,
IonContent,
IonHeader,
} from "@ionic/vue";
import { ref, inject } from "vue";
import * as echarts from "echarts";
import { StorageService } from "@/utils/storageService";
import { getAreaSituation, getCountrySamplePie, getNotify, getSampleInfo, getSampleInfoList } from "@/api/home";
import mitter from "@/utils/mitter";
import { useRouter } from "vue-router";
import HomeSearchDetails from "@/components/HomeSearchDetails.vue";
import { chevronForwardOutline } from "ionicons/icons";
import { QRScanner, registerKeyCodeReceiver, unregisterReceiver, UHFNotifyListeners, UHFRemoveAllListeners, addListenerRate, getEPC } from "@/utils/ratePlugin";
import { showToast } from "@/utils";
const router = useRouter();
const keyWords = ref<string | undefined>("");
const areaList = ref<any[]>([]);
const selectedArea = ref<any>(null);
const inventoryStatisticsPieChart = ref<HTMLElement | null>(null);
let pieChart: echarts.ECharts;
const seriesData = ref<any[]>([]);
const messageList = ref<string[]>([]);
const searchList = ref<any[]>([]);
const storageService = inject("storageService") as StorageService;
onIonViewWillEnter(async () => {
const token = await storageService.getItem("token");
const userInfo = await storageService.getItem("userInfo");
// console.log(token, "首页token", JSON.parse(userInfo));
getAreaList();
getNotifyData();
});
onIonViewDidEnter(() => {
window.addEventListener("resize", handleResize);
mitter.on("token-expired", () => {
// console.log("token失效,跳转到登录页面");
router.push("/login");
});
getChartData();
});
onIonViewWillLeave(() => {
window.removeEventListener("resize", handleResize);
if (pieChart) {
pieChart.dispose();
}
mitter.off("token-expired");
});
const handleInput = (event: Event) => {
// keyWords.value = (event.target as HTMLInputElement).value;
// console.log("搜索:", keyWords.value);
searchList.value = [];
if (keyWords.value) {
getSampleInfo(keyWords.value).then((res: any) => {
if (res.code === 200) {
searchList.value = res.data;
}
})
}
};
const segmentChange = (event: Event) => {
let val: string = (event.target as HTMLInputElement).value;
selectedArea.value = areaList.value.find((item) => item.areaName == val);
};
const initChart = () => {
pieChart = echarts.init(inventoryStatisticsPieChart.value);
let option = {
title: {
text: "99865",
subtext: "库存总量",
left: "14%",
top: "center",
textStyle: {
fontSize: 20,
},
subtextStyle: {
fontSize: 18,
},
},
tooltip: {
trigger: "item",
},
legend: {
top: "center",
type: "scroll",
right: 0,
orient: "vertical",
formatter: (name: string) => {
let total = 0;
let target = 0;
seriesData.value.forEach((item) => {
total += item.value;
if (item.name == name) {
target = item.value;
}
});
let percent = total ? ((target / total) * 100).toFixed(2) : 0;
return `${name} ${percent}% ${target}`;
},
},
series: [
{
// name: "Access From",
type: "pie",
radius: ["70%", "90%"],
right: "50%",
avoidLabelOverlap: false,
label: {
show: false,
position: "center",
},
labelLine: {
show: false,
},
data: seriesData.value,
},
],
};
option && pieChart.setOption(option);
};
const handleResize = () => {
setTimeout(() => {
if (pieChart) {
pieChart.resize();
}
}, 500);
};
const getAreaList = () => {
getAreaSituation().then((res: any) => {
if (res.code === 200) {
areaList.value = res.data;
selectedArea.value = areaList.value[0];
}
});
};
const getChartData = () => {
getCountrySamplePie().then((res: any) => {
if (res.code === 200) {
seriesData.value = res.data;
initChart();
}
});
};
const getNotifyData = () => {
getNotify().then((res: any) => {
if (res.code === 200) {
messageList.value = res.data;
}
});
};
</script>