This commit is contained in:
Timi
2026-08-01 17:35:48 +08:00
parent 2cf33d1003
commit efea624376
6 changed files with 132 additions and 39 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"module": "./dist/timi-web.mjs",
"style": "./dist/timi-web.css",
"private": false,
"version": "0.0.23",
"version": "0.0.24",
"license": "MIT",
"scripts": {
"dev": "vite",
+6 -3
View File
@@ -1,5 +1,5 @@
import { axios } from "./BaseAPI";
import type { Page, PageResult, Permission, Role, RolePayload, UserRoleAuthorizeReq } from "../types";
import type { ModuleCode, Page, PageResult, Permission, Role, RolePayload, UserRoleAuthorizeReq } from "../types";
const BASE_URI = "/user/role";
@@ -27,9 +27,12 @@ async function remove(id: string): Promise<void> {
});
}
async function authorizedList(userId: string): Promise<Role[]> {
async function authorizedList(userId: string, moduleCode?: ModuleCode): Promise<Role[]> {
return axios.post(`${BASE_URI}/authorized/list`, undefined, {
params: { userId }
params: {
userId,
moduleCode
}
});
}
+5 -5
View File
@@ -103,23 +103,23 @@ textarea {
-webkit-filter: grayscale(1);
}
.bold {
.text-bold {
font-weight: bold !important;
}
.italic {
.text-italic {
font-style: italic !important;
}
.delete {
.text-delete {
text-decoration: line-through !important;
}
.underline {
.text-underline {
text-decoration: underline !important;
}
.not-underline {
.text-not-underline {
text-decoration: none !important;
}
+4 -1
View File
@@ -15,7 +15,10 @@ export type Model = {
export type ApiResponse<T> = {
code: number;
msg?: string;
message: string;
messageCode: string;
success: boolean;
fail: boolean;
data: T;
}
+2 -2
View File
@@ -62,13 +62,13 @@ const applyDefaultInterceptors = (instance: AxiosInstance) => {
status: axiosResp?.status,
response: axiosResp,
serverResponse: serverResp,
error: serverResp.msg
error: serverResp.message
});
if (globalErrorCallback) {
globalErrorCallback(serverResp);
}
// 由调用方处理
return Promise.reject(serverResp.msg);
return Promise.reject(serverResp.message);
}
}
notifyResponseObserver({
+108 -21
View File
@@ -3,6 +3,7 @@ export type ScrollListener = {
source: Event;
viewWidth: number;
viewHeight: number;
scrollHeight: number;
top: number;
bottom: number;
}
@@ -14,35 +15,64 @@ export type ScrollBottomListenerOptions = {
/** 返回 true 时才触发 */
canLoad?: () => boolean;
/** 自定义滚动容器,默认 window */
getElement?: () => HTMLElement | null | undefined;
}
export default class Scroller {
private static instance: Scroller;
listeners = new Map<string, Function>();
listeners = new Map<string, {
target: HTMLElement | Window;
listener: EventListener;
}>();
private constructor() {
private constructor() {}
window.addEventListener("scroll", source => {
// 滚动距离
private static waitForLayout(): Promise<void> {
return new Promise(resolve => {
requestAnimationFrame(() => {
requestAnimationFrame(() => resolve());
});
});
}
private static getScrollState(element?: HTMLElement | null, source?: Event): ScrollListener {
if (element) {
return {
source: source || new Event("scroll"),
viewWidth: element.clientWidth,
viewHeight: element.clientHeight,
scrollHeight: element.scrollHeight,
top: element.scrollTop,
bottom: element.scrollHeight - element.scrollTop - element.clientHeight
};
}
const top = document.body.scrollTop || document.documentElement.scrollTop;
// 可视高度
const viewWidth = document.documentElement.clientWidth || document.body.clientWidth;
// 可视高度
const viewHeight = document.documentElement.clientHeight || document.body.clientHeight;
// 滚动高度
const sH = document.documentElement.scrollHeight || document.body.scrollHeight;
// 触发事件
const bottom = sH - top - viewHeight;
this.listeners.forEach(listener => listener({
source,
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
return {
source: source || new Event("scroll"),
viewWidth,
viewHeight,
scrollHeight,
top,
bottom
} as ScrollListener));
}, true);
bottom: scrollHeight - top - viewHeight
};
}
private static bindTarget(name: string, target: HTMLElement | Window, listener: EventListener) {
target.addEventListener("scroll", listener, {
passive: true
});
Scroller.getInstance().listeners.set(name, {
target,
listener
});
}
private static getInstance(): Scroller {
@@ -58,8 +88,24 @@ export default class Scroller {
* @param name 事件名
* @param listener 监听事件
*/
public static addListener(name: string, listener: (event: ScrollListener) => void) {
Scroller.getInstance().listeners.set(name, listener);
public static addListener(name: string, listener: (event: ScrollListener) => void, getElement?: () => HTMLElement | null | undefined) {
Scroller.removeListener(name);
const targetElement = getElement?.();
if (getElement && !targetElement) {
return;
}
const target = targetElement || window;
const eventListener: EventListener = source => {
if (!targetElement) {
const eventTarget = source.target;
const isWindowScrollTarget = eventTarget === document || eventTarget === document.documentElement || eventTarget === document.body;
if (!isWindowScrollTarget) {
return;
}
}
listener(Scroller.getScrollState(targetElement, source));
};
Scroller.bindTarget(name, target, eventListener);
}
/**
@@ -71,15 +117,51 @@ export default class Scroller {
*/
public static addBottomListener(name: string, listener: (event: ScrollListener) => void | Promise<void>, options: ScrollBottomListenerOptions = {}) {
const distance = options.distance ?? 120;
Scroller.addListener(name, event => {
if (distance < event.bottom) {
let loading = false;
let armed = true;
const triggerLoad = async (event: ScrollListener): Promise<void> => {
const inRange = event.bottom <= distance;
if (!inRange) {
armed = true;
return;
}
if (loading) {
return;
}
if (!armed) {
return;
}
if (options.canLoad && !options.canLoad()) {
return;
}
void listener(event);
});
armed = false;
loading = true;
try {
let currentEvent = event;
while (true) {
const previousScrollHeight = currentEvent.scrollHeight;
await listener(currentEvent);
await Scroller.waitForLayout();
if (options.canLoad && !options.canLoad()) {
break;
}
currentEvent = Scroller.getScrollState(options.getElement?.());
if (distance < currentEvent.bottom) {
armed = true;
break;
}
if (currentEvent.scrollHeight <= previousScrollHeight) {
armed = true;
break;
}
}
} finally {
loading = false;
}
};
Scroller.addListener(name, event => {
void triggerLoad(event);
}, options.getElement);
}
/**
@@ -88,6 +170,11 @@ export default class Scroller {
* @param name 事件名
*/
public static removeListener(name: string) {
const entry = Scroller.getInstance().listeners.get(name);
if (!entry) {
return;
}
entry.target.removeEventListener("scroll", entry.listener);
Scroller.getInstance().listeners.delete(name);
}