This commit is contained in:
Timi
2026-07-31 15:28:13 +08:00
parent fc816e2d84
commit 2cf33d1003
8 changed files with 106 additions and 16 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"module": "./dist/timi-web.mjs", "module": "./dist/timi-web.mjs",
"style": "./dist/timi-web.css", "style": "./dist/timi-web.css",
"private": false, "private": false,
"version": "0.0.22", "version": "0.0.23",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+9 -5
View File
@@ -1,8 +1,12 @@
import {Attachment, CaptchaData, LoginRequest, LoginResponse, RegisterRequest, UpdatePasswordRequest, User, UserAttachType} from "../types"; import type {Attachment, CaptchaData, LoginRequest, LoginResponse, RegisterRequest, UpdatePasswordRequest, User} from "../types";
import {UserAttachType} from "../types";
import {axios} from "./BaseAPI"; import {axios} from "./BaseAPI";
import CommonAPI from "./CommonAPI"; import CommonAPI from "./CommonAPI";
const BASE_URI = "/user"; const BASE_URI = "/user";
type UserAttachmentOwner = {
attachmentList?: readonly Attachment[];
};
async function register(req: CaptchaData<RegisterRequest>): Promise<LoginResponse> { async function register(req: CaptchaData<RegisterRequest>): Promise<LoginResponse> {
return await axios.post(`${BASE_URI}/register`, req); return await axios.post(`${BASE_URI}/register`, req);
@@ -31,7 +35,7 @@ async function logout(): Promise<void> {
return await axios.post(`${BASE_URI}/logout`); return await axios.post(`${BASE_URI}/logout`);
} }
async function updateCurrent(req: User): Promise<LoginResponse> { async function updateCurrent(req: Partial<User>): Promise<LoginResponse> {
return await axios.post(`${BASE_URI}/current/update`, req); return await axios.post(`${BASE_URI}/current/update`, req);
} }
@@ -49,19 +53,19 @@ async function view(id: string): Promise<User> {
return await axios.post(`${BASE_URI}/view/${id}`); return await axios.post(`${BASE_URI}/view/${id}`);
} }
function getAvatarURL(user?: User) { function getAvatarURL(user?: UserAttachmentOwner) {
if (user?.attachmentList) { if (user?.attachmentList) {
return findAttachmentByType(user.attachmentList, [UserAttachType.AVATAR, UserAttachType.DEFAULT_AVATAR]); return findAttachmentByType(user.attachmentList, [UserAttachType.AVATAR, UserAttachType.DEFAULT_AVATAR]);
} }
} }
function getWrapperURL(user?: User) { function getWrapperURL(user?: UserAttachmentOwner) {
if (user?.attachmentList) { if (user?.attachmentList) {
return findAttachmentByType(user.attachmentList, [UserAttachType.WRAPPER, UserAttachType.DEFAULT_WRAPPER]); return findAttachmentByType(user.attachmentList, [UserAttachType.WRAPPER, UserAttachType.DEFAULT_WRAPPER]);
} }
} }
function findAttachmentByType(attachmentList: Attachment[], types: UserAttachType[]) { function findAttachmentByType(attachmentList: readonly Attachment[], types: UserAttachType[]) {
for (let i = 0; i < attachmentList.length; i++) { for (let i = 0; i < attachmentList.length; i++) {
const attachType = attachmentList[i].attachType as UserAttachType | undefined; const attachType = attachmentList[i].attachType as UserAttachType | undefined;
const id = attachmentList[i].id; const id = attachmentList[i].id;
+25 -2
View File
@@ -16,12 +16,35 @@ export type AccessMatchMode = "and" | "or";
let _storageKey = "loginUser"; let _storageKey = "loginUser";
function normalizeAccessList(list?: string[]): string[] { type AccessItem = string | {
moduleCode?: string;
code?: string;
};
function normalizeAccessItem(item?: AccessItem): string {
if (!item) {
return "";
}
if (typeof item === "string") {
return item.trim();
}
const code = item.code?.trim();
if (!code) {
return "";
}
const moduleCode = item.moduleCode?.trim();
return moduleCode && !code.includes(":") ? `${moduleCode}:${code}` : code;
}
function normalizeAccessList(list?: AccessItem[]): string[] {
if (!list?.length) { if (!list?.length) {
return []; return [];
} }
return list return list
.map(item => item?.trim()) .map(normalizeAccessItem)
.filter((item): item is string => !!item); .filter((item): item is string => !!item);
} }
+2 -2
View File
@@ -23,8 +23,8 @@ export type Page<T> = {
index: number; index: number;
size: number; size: number;
orderMap?: { [K in keyof T]?: OrderType }; orderMap?: { [K in keyof T]?: OrderType };
equalsExample?: T; equalsExample?: Partial<T>;
likesExample?: T; likesExample?: Partial<T>;
} }
export enum OrderType { export enum OrderType {
+29
View File
@@ -7,6 +7,15 @@ export type ScrollListener = {
bottom: number; bottom: number;
} }
export type ScrollBottomListenerOptions = {
/** 距离底部小于等于该值时触发 */
distance?: number;
/** 返回 true 时才触发 */
canLoad?: () => boolean;
}
export default class Scroller { export default class Scroller {
private static instance: Scroller; private static instance: Scroller;
@@ -53,6 +62,26 @@ export default class Scroller {
Scroller.getInstance().listeners.set(name, listener); Scroller.getInstance().listeners.set(name, listener);
} }
/**
* 添加触底监听
*
* @param name 事件名
* @param listener 触底回调
* @param options 触底配置
*/
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) {
return;
}
if (options.canLoad && !options.canLoad()) {
return;
}
void listener(event);
});
}
/** /**
* 移除监听 * 移除监听
* *
+4
View File
@@ -30,6 +30,10 @@ export default class Text {
return template.replace(/\$\{(\w+)}/g, (_, key) => variables[key]); return template.replace(/\$\{(\w+)}/g, (_, key) => variables[key]);
} }
public static pad(value: number | string, length = 2, fill = "0"): string {
return value.toString().padStart(length, fill);
}
public static unitCompact(val: number | string | null | undefined, unit: string, fixed = 0): string { public static unitCompact(val: number | string | null | undefined, unit: string, fixed = 0): string {
return this.unit(val, unit, fixed, true); return this.unit(val, unit, fixed, true);
}; };
+25 -6
View File
@@ -1,4 +1,5 @@
import Toolkit from "./Toolkit"; import Toolkit from "./Toolkit";
import Text from "./Text";
export default class Time { export default class Time {
@@ -15,6 +16,25 @@ export default class Time {
return new Date().getTime(); return new Date().getTime();
} }
public static toTimestamp(date: Date): number {
return date.getTime();
}
public static toDateObject(unix?: number): Date | undefined {
if (!unix) {
return undefined;
}
return new Date(unix);
}
public static formatDate(date: Date): string {
return `${date.getFullYear()}-${Text.pad(date.getMonth() + 1)}-${Text.pad(date.getDate())}`;
}
public static formatDateTime(date: Date): string {
return `${this.formatDate(date)} ${Text.pad(date.getHours())}:${Text.pad(date.getMinutes())}`;
}
/** /**
* Unix 时间戳转日期 * Unix 时间戳转日期
* *
@@ -22,8 +42,7 @@ export default class Time {
*/ */
public static toDate(unix?: number): string { public static toDate(unix?: number): string {
if (!unix) return ""; if (!unix) return "";
const d = new Date(unix); return this.formatDate(new Date(unix));
return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d.getDate().toString().padStart(2, "0")}`;
} }
/** /**
@@ -34,13 +53,13 @@ export default class Time {
public static toTime(unix?: number): string { public static toTime(unix?: number): string {
if (!unix) return ""; if (!unix) return "";
const d = new Date(unix); const d = new Date(unix);
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`; return `${Text.pad(d.getHours())}:${Text.pad(d.getMinutes())}`;
} }
public static toShortTime(unix?: number): string { public static toShortTime(unix?: number): string {
if (!unix) return ""; if (!unix) return "";
const d = new Date(unix); const d = new Date(unix);
return `${d.getMinutes().toString().padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}`; return `${Text.pad(d.getMinutes())}:${Text.pad(d.getSeconds())}`;
} }
/** /**
@@ -131,9 +150,9 @@ export default class Time {
const minutes = Math.floor((seconds % 3600) / 60); const minutes = Math.floor((seconds % 3600) / 60);
const second = seconds % 60; const second = seconds % 60;
if (0 < hours) { if (0 < hours) {
return `${hours}:${minutes.toString().padStart(2, "0")}:${second.toString().padStart(2, "0")}`; return `${hours}:${Text.pad(minutes)}:${Text.pad(second)}`;
} }
return `${minutes.toString().padStart(2, "0")}:${second.toString().padStart(2, "0")}`; return `${Text.pad(minutes)}:${Text.pad(second)}`;
} }
/** /**
+11
View File
@@ -398,4 +398,15 @@ export default class Toolkit {
} }
return JSON.stringify(JSON.parse(jsonStr), null, 4); return JSON.stringify(JSON.parse(jsonStr), null, 4);
} }
public static downloadBlob(blob: Blob, fileName: string): void {
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
link.remove();
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
}
} }