remove deprecated api
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# 2026-08-06 工作日志
|
||||
|
||||
## TypeScript enum 重构(全量消除 enum)
|
||||
用户决定改用纯联合类型,自行修改依赖方。全项目 18 处 enum 分两类处理:
|
||||
|
||||
### 改为纯联合类型(字符串枚举,仅作类型用)
|
||||
- `Attachment.ts` AttachmentBizType、`Template.ts` TemplateBizType
|
||||
- `Model.ts` RunEnv / OrderType / ImageType(ImageType 值为 ir-auto 等,与 key 不同)
|
||||
- `Comment.ts` CommentReplyBizType / CommentBizType
|
||||
- `User.ts` UserAttachType(在 UserAPI.ts 有值引用,已同步改为字符串字面量,import 改 type-only)
|
||||
|
||||
### 改为 const 对象 + 派生类型(数字枚举或运行时值引用,纯联合不可行)
|
||||
- `User.ts` LoginType、`Article.ts` ArticleType / ArticleAttachType / ArticleSoftwareDownloadType / ArticleSoftwareRuntime(数字枚举,保留数值避免破坏数据契约)
|
||||
- `deviceStore.ts` Breakpoints(数字,用于比较 width < Breakpoints.SM)
|
||||
- `IOSize.ts` Unit(用 Object.keys/values + Unit.KB 值引用)
|
||||
- `Prismjs.ts` PrismjsType / PrismjsViewer(Map 键值引用)
|
||||
- `directives/Popup.ts` PopupType(switch case 值引用)
|
||||
|
||||
### 结论
|
||||
- `vue-tsc --noEmit` 通过(exit 0)
|
||||
- ESLint 因项目缺 eslint.config.js(仍用旧 .eslintrc,ESLint9 不支持)无法运行,属项目既有配置问题
|
||||
- 判断依据:纯联合无运行时存在,凡 `Object.keys(enum)`、`map.set(Enum.X)`、`case Enum.X`、`Enum.X` 作值的都无法纯联合,必须 const 对象
|
||||
|
||||
## Storage.ts 设计修正
|
||||
- `getString` 返回 `string | null`(对齐 `localStorage.getItem` 契约),不再 throw
|
||||
- `getJSON` 返回 `unknown`,键不存在时返回 `null`,值无法解析时才抛错
|
||||
- `getObject` 返回 `T | null`,不再 throw(修正原 JSDoc 已写 `T | undefined` 但代码 throw 的矛盾)
|
||||
- `is` / `not` 键不存在时自然返回 `false` / `true`,不再崩
|
||||
- `has` 去掉死代码 `!== undefined`(localStorage.getItem 只返回 string|null),单次查询
|
||||
- `vue-tsc --noEmit` 通过
|
||||
|
||||
## Toolkit.ts debounce 修复
|
||||
- **Bug 修复**:`cancel()` 原先只清定时器不重置 `leading` 状态,导致立即模式下 cancel 后首次调用丢失 leading 触发
|
||||
- **类型修复**:返回类型从 `T & { cancel(): void }` 改为 `((...args: Parameters<T>) => void) & { cancel(): void }`,防抖函数实际返回 void 而非 T 的返回值
|
||||
- 补充完整 JSDoc(含 `@example`),说明立即模式(leading + trailing)和延迟模式(trailing only)的行为差异
|
||||
- 状态变量 `immediate` → `leading`,参数 `defaultImmediate` → `immediate`,语义更清晰
|
||||
- 去掉 `eslint-disable-next-line` 和多余注释
|
||||
- `vue-tsc --noEmit` 通过
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
import {Attachment, BizUpdateReq, TempFileResp} from "../types";
|
||||
import {axios} from "./BaseAPI";
|
||||
import { Attachment, BizIdUpdate, TempFile } from "../types";
|
||||
import { axios } from "./BaseAPI";
|
||||
import CommonAPI from "./CommonAPI";
|
||||
|
||||
const BASE_URI = "/attach";
|
||||
@@ -11,7 +11,7 @@ const BASE_URI = "/attach";
|
||||
* @param ttl 有效期
|
||||
* @returns 临时附件列表
|
||||
*/
|
||||
async function uploadTemp(file: File | File[], ttl?: string): Promise<TempFileResp[]> {
|
||||
async function upload(file: File | File[], ttl?: string): Promise<TempFile[]> {
|
||||
const formData = new FormData();
|
||||
const fileList = Array.isArray(file) ? file : [file];
|
||||
for (const item of fileList) {
|
||||
@@ -28,8 +28,8 @@ async function uploadTemp(file: File | File[], ttl?: string): Promise<TempFileRe
|
||||
*
|
||||
* @param req 更新请求
|
||||
*/
|
||||
async function updateByBiz(req: BizUpdateReq): Promise<void> {
|
||||
return await axios.post(`${BASE_URI}/update/biz`, req);
|
||||
async function updateByBizId(req: BizIdUpdate): Promise<void> {
|
||||
return await axios.post(`${BASE_URI}/update/biz/id`, req);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,8 +39,8 @@ async function updateByBiz(req: BizUpdateReq): Promise<void> {
|
||||
* @param bizId 业务 ID
|
||||
* @param attachTypeList 附件类型列表
|
||||
*/
|
||||
async function listByBiz(bizType: string, bizId: string, attachTypeList?: string[]): Promise<Attachment[]> {
|
||||
return await axios.get(`${BASE_URI}/list/biz`, {
|
||||
async function listByBizId(bizType: string, bizId: string, attachTypeList?: string[]): Promise<Attachment[]> {
|
||||
return await axios.get(`${BASE_URI}/list/biz/id`, {
|
||||
params: {
|
||||
bizType,
|
||||
bizId,
|
||||
@@ -50,9 +50,9 @@ async function listByBiz(bizType: string, bizId: string, attachTypeList?: string
|
||||
}
|
||||
|
||||
export default {
|
||||
uploadTemp,
|
||||
updateByBiz,
|
||||
listByBiz,
|
||||
upload,
|
||||
updateByBizId,
|
||||
listByBizId,
|
||||
getReadURL: CommonAPI.getAttachmentReadAPI,
|
||||
getTempReadURL: CommonAPI.getAttachmentTempReadAPI
|
||||
};
|
||||
|
||||
+1
-11
@@ -1,4 +1,4 @@
|
||||
import { CaptchaData, Comment, CommentReply, Page, PageResult } from "../types";
|
||||
import { CaptchaData, Comment, Page, PageResult } from "../types";
|
||||
import { axios } from "./BaseAPI";
|
||||
|
||||
const BASE_URI = "/comment";
|
||||
@@ -11,17 +11,7 @@ async function create(captchaData: CaptchaData<Comment>): Promise<void> {
|
||||
return axios.post(`${BASE_URI}/create`, captchaData);
|
||||
}
|
||||
|
||||
async function pageReply(page: Page<CommentReply>): Promise<PageResult<CommentReply>> {
|
||||
return axios.post(`${BASE_URI}/reply/list`, page);
|
||||
}
|
||||
|
||||
async function createReply(captchaData: CaptchaData<CommentReply>): Promise<void> {
|
||||
return axios.post(`${BASE_URI}/reply/create`, captchaData);
|
||||
}
|
||||
|
||||
export default {
|
||||
page,
|
||||
create,
|
||||
createReply,
|
||||
pageReply
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {CaptchaResult, Setting, TemplateBizType} from "../types";
|
||||
import {axios} from "./BaseAPI";
|
||||
import { CaptchaResult, Setting } from "../types";
|
||||
import { axios } from "./BaseAPI";
|
||||
|
||||
function getBaseURI(): string {
|
||||
const baseURL = axios.defaults.baseURL;
|
||||
@@ -19,10 +19,6 @@ const getAttachmentReadAPI = (id: string) => `${getBaseURI()}/attach/read/${id}`
|
||||
|
||||
const getAttachmentTempReadAPI = (id: string) => `${getBaseURI()}/attach/temp/read?id=${id}`;
|
||||
|
||||
async function getTemplate(bizType: TemplateBizType, code: string): Promise<string> {
|
||||
return axios.get(`/template?bizType=${bizType}&bizCode=${code}`);
|
||||
}
|
||||
|
||||
async function settingMap(map: Record<string, string[]>): Promise<Map<string, Map<string, Setting>>> {
|
||||
const raw = await axios.post("/setting/map", map);
|
||||
const moduleMap = new Map<string, Map<string, Setting>>();
|
||||
@@ -47,6 +43,5 @@ export default {
|
||||
captcha,
|
||||
getAttachmentReadAPI,
|
||||
getAttachmentTempReadAPI,
|
||||
getTemplate,
|
||||
settingMap
|
||||
};
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
import type {Attachment, CaptchaData, LoginRequest, LoginResponse, RegisterRequest, UpdatePasswordRequest, User} from "../types";
|
||||
import {UserAttachType} from "../types";
|
||||
import type {Attachment, CaptchaData, LoginRequest, LoginResponse, RegisterRequest, UpdatePasswordRequest, User, UserAttachType} from "../types";
|
||||
import {axios} from "./BaseAPI";
|
||||
import CommonAPI from "./CommonAPI";
|
||||
|
||||
@@ -55,17 +54,17 @@ async function view(id: string): Promise<User> {
|
||||
|
||||
function getAvatarURL(user?: UserAttachmentOwner) {
|
||||
if (user?.attachmentList) {
|
||||
return findAttachmentByType(user.attachmentList, [UserAttachType.AVATAR, UserAttachType.DEFAULT_AVATAR]);
|
||||
return findAttachmentByType(user.attachmentList, "AVATAR");
|
||||
}
|
||||
}
|
||||
|
||||
function getWrapperURL(user?: UserAttachmentOwner) {
|
||||
if (user?.attachmentList) {
|
||||
return findAttachmentByType(user.attachmentList, [UserAttachType.WRAPPER, UserAttachType.DEFAULT_WRAPPER]);
|
||||
return findAttachmentByType(user.attachmentList, "WRAPPER");
|
||||
}
|
||||
}
|
||||
|
||||
function findAttachmentByType(attachmentList: readonly Attachment[], types: UserAttachType[]) {
|
||||
function findAttachmentByType(attachmentList: readonly Attachment[], ...types: UserAttachType[]) {
|
||||
for (let i = 0; i < attachmentList.length; i++) {
|
||||
const attachType = attachmentList[i].attachType as UserAttachType | undefined;
|
||||
const id = attachmentList[i].id;
|
||||
|
||||
@@ -32,23 +32,23 @@ const isShortScreen = ref(false);
|
||||
const isMobileLayout = ref(false);
|
||||
|
||||
/** 断点配置,单位:px */
|
||||
enum Breakpoints {
|
||||
const Breakpoints = {
|
||||
|
||||
/** 超小设备 */
|
||||
XS = 480,
|
||||
XS: 480,
|
||||
|
||||
/** 手机 */
|
||||
SM = 650,
|
||||
SM: 650,
|
||||
|
||||
/** 平板 */
|
||||
MD = 768,
|
||||
MD: 768,
|
||||
|
||||
/** 笔记本 */
|
||||
LG = 1024,
|
||||
LG: 1024,
|
||||
|
||||
/** 大屏幕 */
|
||||
XL = 1440
|
||||
}
|
||||
XL: 1440
|
||||
} as const;
|
||||
|
||||
Resizer.addListener("DEVICE_SIZE", (width, height) => {
|
||||
screenWidth.value = width;
|
||||
|
||||
+7
-68
@@ -2,12 +2,12 @@ import type { Attachment } from "./Attachment";
|
||||
import type { Model } from "./Model";
|
||||
|
||||
// 文章
|
||||
export type Article<E extends ArticleMusicExtendData | ArticleSoftwareExtendData> = {
|
||||
export type Article<A> = {
|
||||
title?: string;
|
||||
type: ArticleType;
|
||||
digest?: string;
|
||||
data?: string;
|
||||
extendData?: E;
|
||||
args?: A;
|
||||
reads: number;
|
||||
likes: number;
|
||||
showComment: boolean;
|
||||
@@ -15,74 +15,13 @@ export type Article<E extends ArticleMusicExtendData | ArticleSoftwareExtendData
|
||||
canRanking: boolean;
|
||||
} & Model;
|
||||
|
||||
export type ArticleView<E extends ArticleMusicExtendData | ArticleSoftwareExtendData> = {
|
||||
export type ArticleView<A> = {
|
||||
comments?: number;
|
||||
attachmentList: Attachment[];
|
||||
} & Article<E>;
|
||||
} & Article<A>;
|
||||
|
||||
export enum ArticleType {
|
||||
export const ArticleType = "COMMON";
|
||||
|
||||
/** 公版 */
|
||||
PUBLIC,
|
||||
export type ArticleType = (typeof ArticleType)[keyof typeof ArticleType];
|
||||
|
||||
/** 音乐 */
|
||||
MUSIC,
|
||||
|
||||
/** 软件 */
|
||||
SOFTWARE
|
||||
}
|
||||
|
||||
export enum ArticleAttachType {
|
||||
|
||||
COVER,
|
||||
}
|
||||
|
||||
export type ArticleMusicExtendData = {
|
||||
title: string;
|
||||
list: ArticleMusicItem[];
|
||||
info: {
|
||||
key: string;
|
||||
value: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type ArticleSoftwareExtendData = {
|
||||
url?: string;
|
||||
downloads: ArticleSoftwareDownload[];
|
||||
format: string;
|
||||
runtime: ArticleSoftwareRuntime[];
|
||||
size: number;
|
||||
version: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export enum ArticleSoftwareDownloadType {
|
||||
|
||||
TIMI_MONGO,
|
||||
|
||||
TIMI_COS,
|
||||
|
||||
URL
|
||||
}
|
||||
|
||||
export enum ArticleSoftwareRuntime {
|
||||
|
||||
JVM,
|
||||
|
||||
WINDOWS,
|
||||
|
||||
LINUX,
|
||||
|
||||
MAC_OS
|
||||
}
|
||||
|
||||
export type ArticleSoftwareDownload = {
|
||||
type: ArticleSoftwareDownloadType;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type ArticleMusicItem = {
|
||||
title: string;
|
||||
audio?: string;
|
||||
video?: string;
|
||||
}
|
||||
export const ArticleAttachType = "COVER";
|
||||
|
||||
+6
-17
@@ -1,10 +1,10 @@
|
||||
import { Model } from "./Model";
|
||||
|
||||
export type Attachment = {
|
||||
bizType: AttachmentBizType;
|
||||
bizId: string;
|
||||
bizType?: AttachmentBizType;
|
||||
bizId?: string;
|
||||
attachType?: string;
|
||||
mongoId: string;
|
||||
mongoId?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
mimeType?: string;
|
||||
@@ -17,26 +17,15 @@ export type Attachment = {
|
||||
tempFileId?: string;
|
||||
} & Model;
|
||||
|
||||
export type TempFileResp = {
|
||||
export type TempFile = {
|
||||
id: string;
|
||||
expireAt?: number;
|
||||
};
|
||||
|
||||
export type BizUpdateReq = {
|
||||
export type BizIdUpdate = {
|
||||
bizType: AttachmentBizType;
|
||||
bizId: string;
|
||||
items: Partial<Attachment>[];
|
||||
};
|
||||
|
||||
export enum AttachmentBizType {
|
||||
|
||||
USER = "USER",
|
||||
|
||||
GAO_CUSTOMER = "GAO_CUSTOMER",
|
||||
|
||||
GAO_REGISTER_RECORD = "GAO_REGISTER_RECORD",
|
||||
|
||||
TEMP_FILE = "TEMP_FILE",
|
||||
|
||||
MIRROR = "MIRROR"
|
||||
}
|
||||
export type AttachmentBizType = | "USER" | "GAO_CUSTOMER" | "GAO_REGISTER_RECORD" | "TEMP_FILE" | "MIRROR";
|
||||
|
||||
+3
-32
@@ -13,10 +13,10 @@ export type Comment = {
|
||||
user?: User;
|
||||
|
||||
/** 回复列表 */
|
||||
replies?: CommentReply[];
|
||||
replies?: Comment[];
|
||||
|
||||
/** 回复分页 */
|
||||
repliesPage?: Page<CommentReply>;
|
||||
repliesPage?: Page<Comment>;
|
||||
|
||||
/** 用于绑定组件当前页下标 */
|
||||
repliesCurrent?: number;
|
||||
@@ -31,33 +31,4 @@ export type Comment = {
|
||||
repository?: object;
|
||||
} & Model
|
||||
|
||||
export type CommentReply = {
|
||||
replyId?: number;
|
||||
commentId?: number;
|
||||
senderId?: number;
|
||||
senderNick?: string;
|
||||
receiverId?: number;
|
||||
receiverNick?: string;
|
||||
content?: string;
|
||||
|
||||
comment?: Comment;
|
||||
sender?: User;
|
||||
receiver?: User;
|
||||
} & Model;
|
||||
|
||||
export enum CommentReplyBizType {
|
||||
|
||||
COMMENT = "COMMENT",
|
||||
|
||||
SENDER = "SENDER",
|
||||
|
||||
RECEIVER = "RECEIVER"
|
||||
}
|
||||
|
||||
export enum CommentBizType {
|
||||
ARTICLE = "ARTICLE",
|
||||
|
||||
GIT_ISSUE = "GIT_ISSUE",
|
||||
|
||||
GIT_MERGE = "GIT_MERGE",
|
||||
}
|
||||
export type CommentBizType = "ARTICLE" | "GIT_ISSUE" | "GIT_MERGE";
|
||||
|
||||
+7
-17
@@ -1,9 +1,3 @@
|
||||
export enum RunEnv {
|
||||
DEV = "DEV",
|
||||
DEV_SSL = "DEV_SSL",
|
||||
PROD = "PROD"
|
||||
}
|
||||
|
||||
// 基本实体模型
|
||||
export type Model = {
|
||||
id?: string;
|
||||
@@ -23,17 +17,17 @@ export type ApiResponse<T> = {
|
||||
}
|
||||
|
||||
export type Page<T> = {
|
||||
index: number;
|
||||
size: number;
|
||||
orderMap?: { [K in keyof T]?: OrderType };
|
||||
equalsExample?: Partial<T>;
|
||||
likesExample?: Partial<T>;
|
||||
} & BasePage;
|
||||
|
||||
export type BasePage = {
|
||||
index: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export enum OrderType {
|
||||
ASC = "ASC",
|
||||
DESC = "DESC"
|
||||
}
|
||||
export type OrderType = "ASC" | "DESC";
|
||||
|
||||
export type PageResult<T> = {
|
||||
total: number;
|
||||
@@ -53,11 +47,7 @@ export type CaptchaResult = {
|
||||
data: string;
|
||||
}
|
||||
|
||||
export enum ImageType {
|
||||
AUTO = "ir-auto",
|
||||
SMOOTH = "ir-smooth",
|
||||
PIXELATED = "ir-pixelated"
|
||||
}
|
||||
export type ImageType = "IR-AUTO" | "IR-SMOOTH" | "IR-PIXELATED";
|
||||
|
||||
export type KeyValue<V, K = string> = {
|
||||
key: K;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum TemplateBizType {
|
||||
|
||||
GIT = "GIT",
|
||||
|
||||
FOREVER_MC = "FOREVER_MC"
|
||||
}
|
||||
+1
-18
@@ -32,18 +32,7 @@ export type User = {
|
||||
phoneNoVerified?: boolean;
|
||||
} & Model;
|
||||
|
||||
export enum UserAttachType {
|
||||
|
||||
AVATAR = "AVATAR",
|
||||
|
||||
WRAPPER = "WRAPPER",
|
||||
|
||||
LICENSE = "LICENSE",
|
||||
|
||||
DEFAULT_AVATAR = "DEFAULT_AVATAR",
|
||||
|
||||
DEFAULT_WRAPPER = "DEFAULT_WRAPPER"
|
||||
}
|
||||
export type UserAttachType = "AVATAR" | "WRAPPER";
|
||||
|
||||
export type RegisterRequest = {
|
||||
name: string;
|
||||
@@ -76,12 +65,6 @@ export type LoginUser = {
|
||||
user?: User;
|
||||
};
|
||||
|
||||
export enum LoginType {
|
||||
ALERT,
|
||||
IFRAME,
|
||||
REDIRECT
|
||||
}
|
||||
|
||||
export type UserLevelType = {
|
||||
exp: number; // 经验数值,和 UserData.exp 一样
|
||||
value: number; // 经验对应等级,[0, 8]
|
||||
|
||||
@@ -8,7 +8,6 @@ export * from "./Model";
|
||||
export * from "./Permission";
|
||||
export * from "./Role";
|
||||
export * from "./User";
|
||||
export * from "./Template";
|
||||
export * from "./Comment";
|
||||
export * from "./Developer";
|
||||
|
||||
|
||||
+12
-35
@@ -1,28 +1,7 @@
|
||||
import Text from "./Text";
|
||||
|
||||
export enum Unit {
|
||||
|
||||
/** B */
|
||||
B = "B",
|
||||
|
||||
/** KB */
|
||||
KB = "KB",
|
||||
|
||||
/** MB */
|
||||
MB = "MB",
|
||||
|
||||
/** GB */
|
||||
GB = "GB",
|
||||
|
||||
/** TB */
|
||||
TB = "TB",
|
||||
|
||||
/** PB */
|
||||
PB = "PB",
|
||||
|
||||
/** EB */
|
||||
EB = "EB"
|
||||
}
|
||||
export const UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB"] as const;
|
||||
export type Unit = (typeof UNITS)[number];
|
||||
|
||||
/** 储存单位 */
|
||||
export default class IOSize {
|
||||
@@ -48,8 +27,6 @@ export default class IOSize {
|
||||
/** 1 EB */
|
||||
public static EB = IOSize.PB << 10;
|
||||
|
||||
public static Unit = Unit;
|
||||
|
||||
/**
|
||||
* <p>格式化一个储存容量,保留两位小数
|
||||
* <pre>
|
||||
@@ -66,7 +43,7 @@ export default class IOSize {
|
||||
if (size === undefined || size === null) {
|
||||
return "0 B";
|
||||
}
|
||||
const units = Object.keys(Unit);
|
||||
const units = Object.values(UNITS);
|
||||
if (0 < size) {
|
||||
for (let i = 0; i < units.length; i++, size /= 1024) {
|
||||
const unit = units[i];
|
||||
@@ -118,31 +95,31 @@ export default class IOSize {
|
||||
let unit: Unit;
|
||||
|
||||
// 先尝试精确匹配枚举
|
||||
if (Object.values(Unit).includes(unitStr as Unit)) {
|
||||
if (Object.values(UNITS).includes(unitStr as Unit)) {
|
||||
unit = unitStr as Unit;
|
||||
} else {
|
||||
// 处理单字母单位缩写(K/M/G/T/P/E)
|
||||
switch (unitStr.charAt(0)) {
|
||||
case "K":
|
||||
unit = Unit.KB;
|
||||
unit = "KB";
|
||||
break;
|
||||
case "M":
|
||||
unit = Unit.MB;
|
||||
unit = "MB";
|
||||
break;
|
||||
case "G":
|
||||
unit = Unit.GB;
|
||||
unit = "GB";
|
||||
break;
|
||||
case "T":
|
||||
unit = Unit.TB;
|
||||
unit = "TB";
|
||||
break;
|
||||
case "P":
|
||||
unit = Unit.PB;
|
||||
unit = "PB";
|
||||
break;
|
||||
case "E":
|
||||
unit = Unit.EB;
|
||||
unit = "EB";
|
||||
break;
|
||||
case "B":
|
||||
unit = Unit.B;
|
||||
unit = "B";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown unit: " + unitStr);
|
||||
@@ -173,7 +150,7 @@ export default class IOSize {
|
||||
if (val === undefined || val === null) {
|
||||
return 0;
|
||||
}
|
||||
const units = Object.values(Unit);
|
||||
const units = Object.values(UNITS);
|
||||
const ordinal = units.indexOf(unit);
|
||||
return Math.round(val * Math.pow(1024, ordinal));
|
||||
}
|
||||
|
||||
+29
-25
@@ -1,23 +1,25 @@
|
||||
export enum PrismjsType {
|
||||
PlainText = "PlainText",
|
||||
Markdown = "Markdown",
|
||||
JavaScript = "JavaScript",
|
||||
TypeScript = "TypeScript",
|
||||
Initialization = "Initialization",
|
||||
PHP = "PHP",
|
||||
SQL = "SQL",
|
||||
XML = "XML",
|
||||
CSS = "CSS",
|
||||
VUE = "VUE",
|
||||
LESS = "LESS",
|
||||
Markup = "Markup",
|
||||
YAML = "YAML",
|
||||
Json = "Json",
|
||||
Java = "Java",
|
||||
Properties = "Properties",
|
||||
NginxConf = "NginxConf",
|
||||
ApacheConf = "ApacheConf"
|
||||
}
|
||||
export const PrismjsType = {
|
||||
PlainText: "PlainText",
|
||||
Markdown: "Markdown",
|
||||
JavaScript: "JavaScript",
|
||||
TypeScript: "TypeScript",
|
||||
Initialization: "Initialization",
|
||||
PHP: "PHP",
|
||||
SQL: "SQL",
|
||||
XML: "XML",
|
||||
CSS: "CSS",
|
||||
VUE: "VUE",
|
||||
LESS: "LESS",
|
||||
Markup: "Markup",
|
||||
YAML: "YAML",
|
||||
Json: "Json",
|
||||
Java: "Java",
|
||||
Properties: "Properties",
|
||||
NginxConf: "NginxConf",
|
||||
ApacheConf: "ApacheConf"
|
||||
} as const;
|
||||
|
||||
export type PrismjsType = (typeof PrismjsType)[keyof typeof PrismjsType];
|
||||
|
||||
export type PrismjsProperties = {
|
||||
|
||||
@@ -26,14 +28,16 @@ export type PrismjsProperties = {
|
||||
viewer: PrismjsViewer;
|
||||
}
|
||||
|
||||
export enum PrismjsViewer {
|
||||
export const PrismjsViewer = {
|
||||
|
||||
MARKDOWN = "MARKDOWN",
|
||||
MARKDOWN: "MARKDOWN",
|
||||
|
||||
CODE = "CODE",
|
||||
CODE: "CODE",
|
||||
|
||||
TEXT = "TEXT",
|
||||
}
|
||||
TEXT: "TEXT"
|
||||
} as const;
|
||||
|
||||
export type PrismjsViewer = (typeof PrismjsViewer)[keyof typeof PrismjsViewer];
|
||||
|
||||
export default class Prismjs {
|
||||
|
||||
|
||||
+15
-20
@@ -1,7 +1,7 @@
|
||||
export default class Storage {
|
||||
|
||||
/**
|
||||
* 获取为布尔值
|
||||
* 获取为布尔值,键不存在时返回 false
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 布尔值
|
||||
@@ -11,7 +11,7 @@ export default class Storage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取为布尔值并取反
|
||||
* 获取为布尔值并取反,键不存在时返回 true
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 布尔值
|
||||
@@ -25,13 +25,11 @@ export default class Storage {
|
||||
*
|
||||
* @template T 对象类型
|
||||
* @param key 键
|
||||
* @returns {T | undefined} 返回对象
|
||||
* @returns 对象,键不存在时返回 null
|
||||
*/
|
||||
public static getObject<T>(key: string): T {
|
||||
if (this.has(key)) {
|
||||
return this.getJSON(key) as T;
|
||||
}
|
||||
throw Error(`not found ${key}`);
|
||||
public static getObject<T>(key: string): T | null {
|
||||
const value = this.getJSON(key);
|
||||
return value === null ? null : value as T;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,24 +52,21 @@ export default class Storage {
|
||||
* 获取为 JSON
|
||||
*
|
||||
* @param key 键
|
||||
* @returns JSON 对象
|
||||
* @returns JSON 对象,键不存在时返回 null;值无法解析时抛错
|
||||
*/
|
||||
public static getJSON(key: string) {
|
||||
return JSON.parse(this.getString(key));
|
||||
public static getJSON(key: string): unknown {
|
||||
const value = this.getString(key);
|
||||
return value === null ? null : JSON.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取为字符串(其他获取方式一般经过这个方法,找不到配置或配置值无效时会抛错)
|
||||
* 获取为字符串
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 字符串
|
||||
* @returns 字符串,键不存在时返回 null
|
||||
*/
|
||||
public static getString(key: string): string {
|
||||
const value = localStorage.getItem(key);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
throw new Error(`not found: ${key}, ${value}`);
|
||||
public static getString(key: string): string | null {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +76,7 @@ export default class Storage {
|
||||
* @returns true 为存在
|
||||
*/
|
||||
public static has(key: string): boolean {
|
||||
return localStorage.getItem(key) !== undefined && localStorage.getItem(key) !== null;
|
||||
return localStorage.getItem(key) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+46
-16
@@ -232,43 +232,73 @@ export default class Toolkit {
|
||||
return Object.keys(e)[Object.values(e).indexOf(value)];
|
||||
}
|
||||
|
||||
// 防抖
|
||||
// eslint-disable-next-line
|
||||
public static debounce<T extends (...args: any[]) => any>(callback: T, defaultImmediate = true, delay = 600): T & {
|
||||
cancel(): void
|
||||
} {
|
||||
let timerId: ReturnType<typeof setTimeout> | null = null; // 存储定时器
|
||||
let immediate = defaultImmediate;
|
||||
// 定义一个 cancel 办法,用于勾销防抖
|
||||
/**
|
||||
* 防抖函数
|
||||
*
|
||||
* 支持两种模式:
|
||||
* - **立即模式**(`immediate = true`,默认):首次调用立即执行,连续调用在延迟结束后再执行最后一次(leading + trailing)
|
||||
* - **延迟模式**(`immediate = false`):所有调用在延迟结束后执行最后一次(trailing only)
|
||||
*
|
||||
* @param callback 回调函数
|
||||
* @param immediate 是否首次调用立即执行,默认 true
|
||||
* @param delay 延迟毫秒数,默认 600
|
||||
* @returns 防抖后的函数,附带 `cancel()` 方法用于取消 pending 执行并重置状态
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // 立即模式(默认):首次立即触发,连续调用后在延迟结束时再触发一次
|
||||
* const fn = Toolkit.debounce((val: string) => {
|
||||
* console.log(val);
|
||||
* });
|
||||
* fn("a"); // 立即输出 "a"
|
||||
* fn("b"); // 600ms 后输出 "b"(trailing)
|
||||
* fn.cancel(); // 取消 pending 的 trailing 调用,重置为初始状态
|
||||
*
|
||||
* // 延迟模式:连续调用只在停止后触发最后一次
|
||||
* const fn2 = Toolkit.debounce((val: string) => {
|
||||
* console.log(val);
|
||||
* }, false);
|
||||
* fn2("a"); // 不输出
|
||||
* fn2("b"); // 600ms 后输出 "b"
|
||||
* ```
|
||||
*/
|
||||
public static debounce<T extends (...args: any[]) => any>(
|
||||
callback: T,
|
||||
immediate = true,
|
||||
delay = 300
|
||||
): ((...args: Parameters<T>) => void) & { cancel(): void } {
|
||||
let timerId: ReturnType<typeof setTimeout> | null = null;
|
||||
let leading = immediate;
|
||||
|
||||
const cancel = (): void => {
|
||||
if (timerId) {
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
leading = immediate;
|
||||
};
|
||||
|
||||
const debounced = function (this: ThisParameterType<T>, ...args: Parameters<T>): void {
|
||||
const context = this;
|
||||
if (timerId) {
|
||||
cancel();
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
if (immediate) {
|
||||
if (leading) {
|
||||
callback.apply(context, args);
|
||||
immediate = false;
|
||||
leading = false;
|
||||
timerId = setTimeout(() => {
|
||||
immediate = defaultImmediate;
|
||||
leading = immediate;
|
||||
}, delay);
|
||||
} else {
|
||||
// 设置定时器,在延迟时间后执行指标函数
|
||||
timerId = setTimeout(() => {
|
||||
callback.apply(context, args);
|
||||
immediate = defaultImmediate;
|
||||
leading = immediate;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
// 将 cancel 方法附加到 debounced 函数上
|
||||
(debounced as any).cancel = cancel;
|
||||
return debounced as T & { cancel(): void };
|
||||
return debounced as ((...args: Parameters<T>) => void) & { cancel(): void };
|
||||
}
|
||||
|
||||
public static toUserLevel(exp?: number): UserLevelType {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import type { Directive, DirectiveBinding } from "vue";
|
||||
import Toolkit from "../Toolkit";
|
||||
|
||||
export enum PopupType {
|
||||
TEXT,
|
||||
IMG,
|
||||
HTML,
|
||||
EL
|
||||
}
|
||||
export type PopupType = "TEXT" | "IMG" | "HTML" | "ELEMENT";
|
||||
|
||||
/** */
|
||||
export type PopupConfig = {
|
||||
@@ -30,7 +25,7 @@ const VPopup: Directive = {
|
||||
config = binding.value as PopupConfig;
|
||||
} else {
|
||||
config = {
|
||||
type: PopupType.TEXT,
|
||||
type: "TEXT",
|
||||
value: binding.value as any as string,
|
||||
canShow: () => true
|
||||
};
|
||||
@@ -58,24 +53,24 @@ const VPopup: Directive = {
|
||||
popup.appendChild(el);
|
||||
}
|
||||
switch (config.type) {
|
||||
case PopupType.TEXT:
|
||||
case "TEXT":
|
||||
// 文本
|
||||
el = document.createElement("div");
|
||||
el.className = "text";
|
||||
el.textContent = config.value as string;
|
||||
popup.appendChild(el);
|
||||
break;
|
||||
case PopupType.IMG:
|
||||
case "IMG":
|
||||
// 图片
|
||||
el = document.createElement("img");
|
||||
(el as HTMLImageElement).src = config.value as string;
|
||||
popup.appendChild(el);
|
||||
break;
|
||||
case PopupType.HTML:
|
||||
case "HTML":
|
||||
// HTML 字符串
|
||||
popup.appendChild(Toolkit.toDOM(config.value as string));
|
||||
break;
|
||||
case PopupType.EL:
|
||||
case "ELEMENT":
|
||||
// DOM 节点
|
||||
if (config.value instanceof HTMLElement) {
|
||||
const valueEl = config.value as HTMLElement;
|
||||
|
||||
Reference in New Issue
Block a user