mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-13 00:10:27 +00:00
refactor: 优化eslint配置,提升代码质量 (#1341)
* feat: 统一并标准化eslint * lint: napcat.webui * lint: napcat.webui * lint: napcat.core * build: fix * lint: napcat.webui * refactor: 重构eslint * Update README.md
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { PlayMode } from '@/const/enum'
|
||||
import { PlayMode } from '@/const/enum';
|
||||
|
||||
import WebUIManager from '@/controllers/webui_manager'
|
||||
import WebUIManager from '@/controllers/webui_manager';
|
||||
import type {
|
||||
FinalMusic,
|
||||
Music163ListResponse,
|
||||
Music163URLResponse
|
||||
} from '@/types/music'
|
||||
Music163URLResponse,
|
||||
} from '@/types/music';
|
||||
|
||||
/**
|
||||
* 获取网易云音乐歌单
|
||||
@@ -13,17 +13,17 @@ import type {
|
||||
* @returns 歌单信息
|
||||
*/
|
||||
export const get163MusicList = async (id: string) => {
|
||||
let res = await WebUIManager.proxy<Music163ListResponse>(
|
||||
const res = await WebUIManager.proxy<Music163ListResponse>(
|
||||
'https://wavesgame.top/playlist/track/all?id=' + id
|
||||
)
|
||||
);
|
||||
// const res = await request.get<Music163ListResponse>(
|
||||
// `https://wavesgame.top/playlist/track/all?id=${id}`
|
||||
// )
|
||||
if (res?.data?.code !== 200) {
|
||||
throw new Error('获取歌曲列表失败')
|
||||
throw new Error('获取歌曲列表失败');
|
||||
}
|
||||
return res.data
|
||||
}
|
||||
return res.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取歌曲地址
|
||||
@@ -32,29 +32,29 @@ export const get163MusicList = async (id: string) => {
|
||||
*/
|
||||
export const getSongsURL = async (ids: number[]) => {
|
||||
const _ids = ids.reduce((prev, cur, index) => {
|
||||
const groupIndex = Math.floor(index / 10)
|
||||
const groupIndex = Math.floor(index / 10);
|
||||
if (!prev[groupIndex]) {
|
||||
prev[groupIndex] = []
|
||||
prev[groupIndex] = [];
|
||||
}
|
||||
prev[groupIndex].push(cur)
|
||||
return prev
|
||||
}, [] as number[][])
|
||||
prev[groupIndex].push(cur);
|
||||
return prev;
|
||||
}, [] as number[][]);
|
||||
const res = await Promise.all(
|
||||
_ids.map(async (id) => {
|
||||
const res = await WebUIManager.proxy<Music163URLResponse>(
|
||||
`https://wavesgame.top/song/url?id=${id.join(',')}`
|
||||
)
|
||||
);
|
||||
if (res?.data?.code !== 200) {
|
||||
throw new Error('获取歌曲地址失败')
|
||||
throw new Error('获取歌曲地址失败');
|
||||
}
|
||||
return res.data.data
|
||||
return res.data.data;
|
||||
})
|
||||
)
|
||||
);
|
||||
const result = res.reduce((prev, cur) => {
|
||||
return prev.concat(...cur)
|
||||
}, [])
|
||||
return result
|
||||
}
|
||||
return prev.concat(...cur);
|
||||
}, []);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取网易云音乐歌单歌曲
|
||||
@@ -62,26 +62,26 @@ export const getSongsURL = async (ids: number[]) => {
|
||||
* @returns 歌曲信息
|
||||
*/
|
||||
export const get163MusicListSongs = async (id: string) => {
|
||||
const listRes = await get163MusicList(id)
|
||||
const songs = listRes.songs.map((song) => song.id)
|
||||
const songsRes = await getSongsURL(songs)
|
||||
const finalMusic: FinalMusic[] = []
|
||||
const listRes = await get163MusicList(id);
|
||||
const songs = listRes.songs.map((song) => song.id);
|
||||
const songsRes = await getSongsURL(songs);
|
||||
const finalMusic: FinalMusic[] = [];
|
||||
for (let i = 0; i < listRes.songs.length; i++) {
|
||||
const song = listRes.songs[i]
|
||||
const music = songsRes.find((s) => s.id === song.id)
|
||||
const songURL = music?.url
|
||||
const song = listRes.songs[i];
|
||||
const music = songsRes.find((s) => s.id === song.id);
|
||||
const songURL = music?.url;
|
||||
if (songURL) {
|
||||
finalMusic.push({
|
||||
id: song.id,
|
||||
url: songURL.replace(/http:\/\//, '//').replace(/https:\/\//, '//'),
|
||||
title: song.name,
|
||||
artist: song.ar.map((p) => p.name).join('/'),
|
||||
cover: song.al.picUrl
|
||||
})
|
||||
cover: song.al.picUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
return finalMusic
|
||||
}
|
||||
return finalMusic;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取随机音乐
|
||||
@@ -90,13 +90,13 @@ export const get163MusicListSongs = async (id: string) => {
|
||||
* @returns 随机音乐id
|
||||
*/
|
||||
export const getRandomMusic = (ids: number[], currentId: number): number => {
|
||||
const randomIndex = Math.floor(Math.random() * ids.length)
|
||||
const randomId = ids[randomIndex]
|
||||
const randomIndex = Math.floor(Math.random() * ids.length);
|
||||
const randomId = ids[randomIndex];
|
||||
if (randomId === currentId) {
|
||||
return getRandomMusic(ids, currentId)
|
||||
return getRandomMusic(ids, currentId);
|
||||
}
|
||||
return randomId
|
||||
}
|
||||
return randomId;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取下一首音乐id
|
||||
@@ -109,14 +109,14 @@ export const getNextMusic = (
|
||||
currentId: number,
|
||||
mode: PlayMode
|
||||
): number => {
|
||||
const ids = musics.map((music) => music.id)
|
||||
const ids = musics.map((music) => music.id);
|
||||
if (mode === PlayMode.Loop) {
|
||||
const currentIndex = ids.findIndex((id) => id === currentId)
|
||||
const nextIndex = currentIndex + 1
|
||||
return ids[nextIndex] || ids[0]
|
||||
const currentIndex = ids.findIndex((id) => id === currentId);
|
||||
const nextIndex = currentIndex + 1;
|
||||
return ids[nextIndex] || ids[0];
|
||||
}
|
||||
if (mode === PlayMode.Random) {
|
||||
return getRandomMusic(ids, currentId)
|
||||
return getRandomMusic(ids, currentId);
|
||||
}
|
||||
return currentId
|
||||
}
|
||||
return currentId;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
export const deepClone = <T>(obj: T): T => {
|
||||
// 我们这里只只用于配置项,所以直接使用字符串就行
|
||||
const newObj = JSON.parse(JSON.stringify(obj))
|
||||
const newObj = JSON.parse(JSON.stringify(obj));
|
||||
|
||||
return newObj
|
||||
}
|
||||
return newObj;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Op } from 'quill'
|
||||
import type { Op } from 'quill';
|
||||
|
||||
import type { EmojiValue } from '@/components/chat_input/formats/emoji_blot'
|
||||
import type { ImageValue } from '@/components/chat_input/formats/image_blot'
|
||||
import type { ReplyBlockValue } from '@/components/chat_input/formats/reply_blot'
|
||||
import type { EmojiValue } from '@/components/chat_input/formats/emoji_blot';
|
||||
import type { ImageValue } from '@/components/chat_input/formats/image_blot';
|
||||
import type { ReplyBlockValue } from '@/components/chat_input/formats/reply_blot';
|
||||
|
||||
import {
|
||||
type AllOB11WsResponse,
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
type OB11PrivateMessage,
|
||||
type OB11Segment,
|
||||
type OneBot11Lifecycle,
|
||||
type RequestResponse
|
||||
} from '../types/onebot'
|
||||
type RequestResponse,
|
||||
} from '../types/onebot';
|
||||
|
||||
/**
|
||||
* 获取事件名称
|
||||
@@ -26,19 +26,19 @@ import {
|
||||
export const getEventName = (event: OB11AllEvent['post_type']): string => {
|
||||
switch (event) {
|
||||
case 'message':
|
||||
return '消息'
|
||||
return '消息';
|
||||
case 'notice':
|
||||
return '通知'
|
||||
return '通知';
|
||||
case 'request':
|
||||
return '请求'
|
||||
return '请求';
|
||||
case 'meta_event':
|
||||
return '元事件'
|
||||
return '元事件';
|
||||
case 'message_sent':
|
||||
return '消息上报'
|
||||
return '消息上报';
|
||||
default:
|
||||
return '未知'
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取生命周期事件名称
|
||||
@@ -51,15 +51,15 @@ export const getLifecycleName = (
|
||||
): string => {
|
||||
switch (event) {
|
||||
case 'enable':
|
||||
return '启用'
|
||||
return '启用';
|
||||
case 'disable':
|
||||
return '停用'
|
||||
return '停用';
|
||||
case 'connect':
|
||||
return '连接'
|
||||
return '连接';
|
||||
default:
|
||||
return '未知'
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取生命周期事件Chip颜色
|
||||
@@ -72,15 +72,15 @@ export const getLifecycleColor = (
|
||||
): 'success' | 'danger' | 'default' => {
|
||||
switch (event) {
|
||||
case 'enable':
|
||||
return 'success'
|
||||
return 'success';
|
||||
case 'disable':
|
||||
return 'danger'
|
||||
return 'danger';
|
||||
case 'connect':
|
||||
return 'success'
|
||||
return 'success';
|
||||
default:
|
||||
return 'default'
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断 OneBot WS 返回值是否为事件
|
||||
@@ -89,8 +89,8 @@ export const getLifecycleColor = (
|
||||
* @description 用于判断 OneBot WS 返回值是否为事件
|
||||
*/
|
||||
export const isOB11Event = (data: AllOB11WsResponse): data is OB11AllEvent => {
|
||||
return 'post_type' in data
|
||||
}
|
||||
return 'post_type' in data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断 OneBot WS 返回值是否为请求响应
|
||||
@@ -101,8 +101,8 @@ export const isOB11Event = (data: AllOB11WsResponse): data is OB11AllEvent => {
|
||||
export const isOB11RequestResponse = (
|
||||
data: AllOB11WsResponse
|
||||
): data is RequestResponse => {
|
||||
return 'status' in data && 'retcode' in data
|
||||
}
|
||||
return 'status' in data && 'retcode' in data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取请求响应状态文本
|
||||
@@ -115,15 +115,15 @@ export const getResponseStatusText = (
|
||||
): string => {
|
||||
switch (status) {
|
||||
case 'ok':
|
||||
return '成功'
|
||||
return '成功';
|
||||
case 'failed':
|
||||
return '失败'
|
||||
return '失败';
|
||||
case 'async':
|
||||
return '异步'
|
||||
return '异步';
|
||||
default:
|
||||
return '未知'
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取请求响应状态颜色
|
||||
@@ -136,15 +136,15 @@ export const getResponseStatusColor = (
|
||||
): 'success' | 'danger' | 'warning' | 'default' => {
|
||||
switch (status) {
|
||||
case 'ok':
|
||||
return 'success'
|
||||
return 'success';
|
||||
case 'failed':
|
||||
return 'danger'
|
||||
return 'danger';
|
||||
case 'async':
|
||||
return 'warning'
|
||||
return 'warning';
|
||||
default:
|
||||
return 'default'
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取通知类型名称
|
||||
@@ -155,33 +155,33 @@ export const getResponseStatusColor = (
|
||||
export const getNoticeTypeName = (type: OB11Notice['notice_type']): string => {
|
||||
switch (type) {
|
||||
case OB11NoticeType.GroupUpload:
|
||||
return '群文件上传'
|
||||
return '群文件上传';
|
||||
case OB11NoticeType.GroupAdmin:
|
||||
return '群管理员变动'
|
||||
return '群管理员变动';
|
||||
case OB11NoticeType.GroupDecrease:
|
||||
return '群成员减少'
|
||||
return '群成员减少';
|
||||
case OB11NoticeType.GroupIncrease:
|
||||
return '群成员增加'
|
||||
return '群成员增加';
|
||||
case OB11NoticeType.GroupBan:
|
||||
return '群禁言'
|
||||
return '群禁言';
|
||||
case OB11NoticeType.FriendAdd:
|
||||
return '好友添加'
|
||||
return '好友添加';
|
||||
case OB11NoticeType.GroupRecall:
|
||||
return '群消息撤回'
|
||||
return '群消息撤回';
|
||||
case OB11NoticeType.FriendRecall:
|
||||
return '好友消息撤回'
|
||||
return '好友消息撤回';
|
||||
case OB11NoticeType.Notify:
|
||||
return '通知'
|
||||
return '通知';
|
||||
case OB11NoticeType.GroupMsgEmojiLike:
|
||||
return '群消息表情回应'
|
||||
return '群消息表情回应';
|
||||
case OB11NoticeType.GroupEssence:
|
||||
return '群消息精华'
|
||||
return '群消息精华';
|
||||
case OB11NoticeType.GroupCard:
|
||||
return '群名片更新'
|
||||
return '群名片更新';
|
||||
default:
|
||||
return '未知'
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断 OneBot 消息是否为私聊消息
|
||||
@@ -192,8 +192,8 @@ export const getNoticeTypeName = (type: OB11Notice['notice_type']): string => {
|
||||
export const isOB11PrivateMessage = (
|
||||
data: OB11Message
|
||||
): data is OB11PrivateMessage => {
|
||||
return data.message_type === 'private'
|
||||
}
|
||||
return data.message_type === 'private';
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断 OneBot 消息是否为群消息
|
||||
@@ -204,8 +204,8 @@ export const isOB11PrivateMessage = (
|
||||
export const isOB11GroupMessage = (
|
||||
data: OB11Message
|
||||
): data is OB11GroupMessage => {
|
||||
return data.message_type === 'group'
|
||||
}
|
||||
return data.message_type === 'group';
|
||||
};
|
||||
|
||||
/**
|
||||
* 将 Quill Delta 转换为 OneBot 消息
|
||||
@@ -217,32 +217,32 @@ export const quillToMessage = (op: Op) => {
|
||||
let message: OB11Segment = {
|
||||
type: 'text',
|
||||
data: {
|
||||
text: op.insert as string
|
||||
}
|
||||
}
|
||||
text: op.insert as string,
|
||||
},
|
||||
};
|
||||
if (typeof op.insert !== 'string') {
|
||||
if (op.insert?.image) {
|
||||
message = {
|
||||
type: 'image',
|
||||
data: {
|
||||
file: (op.insert.image as ImageValue).src
|
||||
}
|
||||
}
|
||||
file: (op.insert.image as ImageValue).src,
|
||||
},
|
||||
};
|
||||
} else if (op.insert?.emoji) {
|
||||
message = {
|
||||
type: 'face',
|
||||
data: {
|
||||
id: (op.insert.emoji as EmojiValue).id
|
||||
}
|
||||
}
|
||||
id: (op.insert.emoji as EmojiValue).id,
|
||||
},
|
||||
};
|
||||
} else if (op.insert?.reply) {
|
||||
message = {
|
||||
type: 'reply',
|
||||
data: {
|
||||
id: (op.insert.reply as ReplyBlockValue).messageId
|
||||
}
|
||||
}
|
||||
id: (op.insert.reply as ReplyBlockValue).messageId,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { QQItem } from '@/components/quick_login'
|
||||
import type { QQItem } from '@/components/quick_login';
|
||||
|
||||
/**
|
||||
* 判断 QQ 快速登录列表项是否为 QQ 信息
|
||||
@@ -9,5 +9,5 @@ import type { QQItem } from '@/components/quick_login'
|
||||
export const isQQQuickNewItem = (
|
||||
data?: QQItem | LoginListItem | null
|
||||
): data is LoginListItem => {
|
||||
return !!data && 'nickName' in data && 'faceUrl' in data
|
||||
}
|
||||
return !!data && 'nickName' in data && 'faceUrl' in data;
|
||||
};
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
import axios from 'axios'
|
||||
import axios from 'axios';
|
||||
|
||||
import key from '@/const/key'
|
||||
import key from '@/const/key';
|
||||
|
||||
export const serverRequest = axios.create({
|
||||
timeout: 5000
|
||||
})
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
export const request = axios.create({
|
||||
timeout: 10000
|
||||
})
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
export const requestServerWithFetch = async (
|
||||
url: string,
|
||||
options: RequestInit
|
||||
) => {
|
||||
const token = localStorage.getItem(key.token)
|
||||
const token = localStorage.getItem(key.token);
|
||||
|
||||
if (token) {
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: `Bearer ${JSON.parse(token)}`
|
||||
}
|
||||
Authorization: `Bearer ${JSON.parse(token)}`,
|
||||
};
|
||||
}
|
||||
|
||||
const baseURL = '/api'
|
||||
const baseURL = '/api';
|
||||
|
||||
const response = await fetch(baseURL + url, options)
|
||||
const response = await fetch(baseURL + url, options);
|
||||
|
||||
return response
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
serverRequest.interceptors.request.use((config) => {
|
||||
const baseURL = '/api'
|
||||
const baseURL = '/api';
|
||||
|
||||
config.baseURL = baseURL
|
||||
config.baseURL = baseURL;
|
||||
|
||||
const token = localStorage.getItem(key.token)
|
||||
const token = localStorage.getItem(key.token);
|
||||
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${JSON.parse(token)}`
|
||||
config.headers['Authorization'] = `Bearer ${JSON.parse(token)}`;
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
return config;
|
||||
});
|
||||
|
||||
serverRequest.interceptors.response.use((response) => {
|
||||
// 如果是流式传输的文件
|
||||
if (response.headers['content-type'] === 'application/octet-stream') {
|
||||
return response
|
||||
return response;
|
||||
}
|
||||
if (response.data.code !== 0) {
|
||||
if (response.data.message === 'Unauthorized') {
|
||||
const token = localStorage.getItem(key.token)
|
||||
const token = localStorage.getItem(key.token);
|
||||
if (token && JSON.parse(token)) {
|
||||
localStorage.removeItem(key.token)
|
||||
window.location.reload()
|
||||
localStorage.removeItem(key.token);
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
throw new Error(response.data.message)
|
||||
throw new Error(response.data.message);
|
||||
}
|
||||
|
||||
return response
|
||||
})
|
||||
return response;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LogLevel } from '@/const/enum'
|
||||
import { LogLevel } from '@/const/enum';
|
||||
|
||||
export const gradientText = (
|
||||
text: string,
|
||||
@@ -8,90 +8,90 @@ export const gradientText = (
|
||||
italic: boolean = false,
|
||||
underline: boolean = false
|
||||
) => {
|
||||
const steps = text.length
|
||||
const steps = text.length;
|
||||
const colorStep = startColor.map(
|
||||
(start, index) => (endColor[index] - start) / steps
|
||||
)
|
||||
);
|
||||
|
||||
let coloredText = ''
|
||||
let coloredText = '';
|
||||
for (let i = 0; i < steps; i++) {
|
||||
const color = startColor.map((start, index) =>
|
||||
Math.round(start + colorStep[index] * i)
|
||||
)
|
||||
coloredText += `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text[i]}`
|
||||
);
|
||||
coloredText += `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text[i]}`;
|
||||
}
|
||||
|
||||
// 添加样式
|
||||
if (bold) {
|
||||
coloredText = `\x1b[1m${coloredText}`
|
||||
coloredText = `\x1b[1m${coloredText}`;
|
||||
}
|
||||
if (italic) {
|
||||
coloredText = `\x1b[3m${coloredText}`
|
||||
coloredText = `\x1b[3m${coloredText}`;
|
||||
}
|
||||
if (underline) {
|
||||
coloredText = `\x1b[4m${coloredText}`
|
||||
coloredText = `\x1b[4m${coloredText}`;
|
||||
}
|
||||
|
||||
return coloredText + '\x1b[0m' // 重置颜色和样式
|
||||
}
|
||||
return coloredText + '\x1b[0m'; // 重置颜色和样式
|
||||
};
|
||||
|
||||
export const logColor = {
|
||||
[LogLevel.DEBUG]: 'green',
|
||||
[LogLevel.INFO]: 'black',
|
||||
[LogLevel.WARN]: 'yellow',
|
||||
[LogLevel.ERROR]: 'red',
|
||||
[LogLevel.FATAL]: 'red'
|
||||
} as const
|
||||
[LogLevel.FATAL]: 'red',
|
||||
} as const;
|
||||
|
||||
export const colorizeLogLevel = (content: string) => {
|
||||
const logLevel = content.match(/\[[a-zA-Z]+\]/) || []
|
||||
let _content = content
|
||||
const logLevel = content.match(/\[[a-zA-Z]+\]/) || [];
|
||||
let _content = content;
|
||||
const level =
|
||||
(logLevel?.[0]?.replace('[', '').replace(']', '') as LogLevel) ??
|
||||
LogLevel.INFO
|
||||
const color = logColor[level]
|
||||
LogLevel.INFO;
|
||||
const color = logColor[level];
|
||||
switch (color) {
|
||||
case 'green':
|
||||
_content = `\x1b[32m${_content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[32m${_content}\x1b[0m`;
|
||||
break;
|
||||
case 'black':
|
||||
_content = `\x1b[30m${_content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[30m${_content}\x1b[0m`;
|
||||
break;
|
||||
case 'yellow':
|
||||
_content = `\x1b[33m${_content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[33m${_content}\x1b[0m`;
|
||||
break;
|
||||
case 'red':
|
||||
_content = `\x1b[31m${_content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[31m${_content}\x1b[0m`;
|
||||
break;
|
||||
default:
|
||||
_content = `\x1b[30m${_content}\x1b[0m`
|
||||
_content = `\x1b[30m${_content}\x1b[0m`;
|
||||
}
|
||||
return {
|
||||
content: _content,
|
||||
level
|
||||
}
|
||||
}
|
||||
level,
|
||||
};
|
||||
};
|
||||
|
||||
export const colorizeLogLevelWithTag = (content: string, level: LogLevel) => {
|
||||
let _content = content
|
||||
let _content = content;
|
||||
switch (level) {
|
||||
case LogLevel.DEBUG:
|
||||
_content = `\x1b[32m[DEBUG] ${content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[32m[DEBUG] ${content}\x1b[0m`;
|
||||
break;
|
||||
case LogLevel.INFO:
|
||||
_content = `\x1b[30m[INFO] ${content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[30m[INFO] ${content}\x1b[0m`;
|
||||
break;
|
||||
case LogLevel.WARN:
|
||||
_content = `\x1b[33m[WARN] ${content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[33m[WARN] ${content}\x1b[0m`;
|
||||
break;
|
||||
case LogLevel.ERROR:
|
||||
_content = `\x1b[31m[ERROR] ${content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[31m[ERROR] ${content}\x1b[0m`;
|
||||
break;
|
||||
case LogLevel.FATAL:
|
||||
_content = `\x1b[31m[FATAL] ${content}\x1b[0m`
|
||||
break
|
||||
_content = `\x1b[31m[FATAL] ${content}\x1b[0m`;
|
||||
break;
|
||||
default:
|
||||
_content = `\x1b[30m${content}\x1b[0m`
|
||||
_content = `\x1b[30m${content}\x1b[0m`;
|
||||
}
|
||||
return _content
|
||||
}
|
||||
return _content;
|
||||
};
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { request } from './request'
|
||||
import { request } from './request';
|
||||
|
||||
const style = document.createElement('style')
|
||||
document.head.appendChild(style)
|
||||
const style = document.createElement('style');
|
||||
document.head.appendChild(style);
|
||||
|
||||
export function loadTheme() {
|
||||
export function loadTheme () {
|
||||
request('/files/theme.css?_t=' + Date.now())
|
||||
.then((res) => res.data)
|
||||
.then((css) => {
|
||||
style.innerHTML = css
|
||||
style.innerHTML = css;
|
||||
})
|
||||
.catch(() => {
|
||||
console.error('Failed to load theme.css')
|
||||
})
|
||||
console.error('Failed to load theme.css');
|
||||
});
|
||||
}
|
||||
|
||||
export const colorKeys = [
|
||||
@@ -121,21 +121,21 @@ export const colorKeys = [
|
||||
'--heroui-divider',
|
||||
'--heroui-code-background',
|
||||
'--heroui-strong',
|
||||
'--heroui-code-mdx'
|
||||
] as const
|
||||
'--heroui-code-mdx',
|
||||
] as const;
|
||||
|
||||
export const generateTheme = (theme: ThemeConfig, validField?: string) => {
|
||||
let css = `:root ${validField ? `.${validField}` : ''}, .light ${validField ? `.${validField}` : ''}, [data-theme="light"] ${validField ? `.${validField}` : ''} {`
|
||||
let css = `:root ${validField ? `.${validField}` : ''}, .light ${validField ? `.${validField}` : ''}, [data-theme="light"] ${validField ? `.${validField}` : ''} {`;
|
||||
for (const key in theme.light) {
|
||||
const _key = key as keyof ThemeConfigItem
|
||||
css += `${_key}: ${theme.light[_key]};`
|
||||
const _key = key as keyof ThemeConfigItem;
|
||||
css += `${_key}: ${theme.light[_key]};`;
|
||||
}
|
||||
css += `}`
|
||||
css += `.dark ${validField ? `.${validField}` : ''}, [data-theme="dark"] ${validField ? `.${validField}` : ''} {`
|
||||
css += '}';
|
||||
css += `.dark ${validField ? `.${validField}` : ''}, [data-theme="dark"] ${validField ? `.${validField}` : ''} {`;
|
||||
for (const key in theme.dark) {
|
||||
const _key = key as keyof ThemeConfigItem
|
||||
css += `${_key}: ${theme.dark[_key]};`
|
||||
const _key = key as keyof ThemeConfigItem;
|
||||
css += `${_key}: ${theme.dark[_key]};`;
|
||||
}
|
||||
css += `}`
|
||||
return css
|
||||
}
|
||||
css += '}';
|
||||
return css;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export const sleep = (ms: number) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms))
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* 获取距离当前时间的发布时间
|
||||
@@ -13,26 +13,26 @@ export const sleep = (ms: number) =>
|
||||
* @example getReleaseTime("2021-10-10T10:10:10Z") => "1天前"
|
||||
*/
|
||||
export const getReleaseTime = (time: string) => {
|
||||
const releaseTime = new Date(time).getTime()
|
||||
const nowTime = new Date().getTime()
|
||||
const diffTime = nowTime - releaseTime
|
||||
const diffDays = Math.floor(diffTime / (24 * 3600 * 1000))
|
||||
const diffHours = Math.floor((diffTime % (24 * 3600 * 1000)) / (3600 * 1000))
|
||||
const diffMinutes = Math.floor((diffTime % (3600 * 1000)) / (60 * 1000))
|
||||
const diffSeconds = Math.floor((diffTime % (60 * 1000)) / 1000)
|
||||
const releaseTime = new Date(time).getTime();
|
||||
const nowTime = new Date().getTime();
|
||||
const diffTime = nowTime - releaseTime;
|
||||
const diffDays = Math.floor(diffTime / (24 * 3600 * 1000));
|
||||
const diffHours = Math.floor((diffTime % (24 * 3600 * 1000)) / (3600 * 1000));
|
||||
const diffMinutes = Math.floor((diffTime % (3600 * 1000)) / (60 * 1000));
|
||||
const diffSeconds = Math.floor((diffTime % (60 * 1000)) / 1000);
|
||||
|
||||
if (diffDays > 0) {
|
||||
return `${diffDays}天前`
|
||||
return `${diffDays}天前`;
|
||||
} else if (diffHours > 0) {
|
||||
return `${diffHours}小时前`
|
||||
return `${diffHours}小时前`;
|
||||
} else if (diffMinutes > 0) {
|
||||
return `${diffMinutes}分钟前`
|
||||
return `${diffMinutes}分钟前`;
|
||||
} else {
|
||||
return `${diffSeconds}秒前`
|
||||
return `${diffSeconds}秒前`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const formatNumber = (n: number) => n.toString().padStart(2, '0')
|
||||
const formatNumber = (n: number) => n.toString().padStart(2, '0');
|
||||
/**
|
||||
* 将时间戳转换为日期字符串
|
||||
* @param timestamp 时间戳
|
||||
@@ -40,13 +40,13 @@ const formatNumber = (n: number) => n.toString().padStart(2, '0')
|
||||
* @example timestampToDateString(163383301) => "2021-10-10 10:10:10"
|
||||
*/
|
||||
export const timestampToDateString = (timestamp: number) => {
|
||||
timestamp = timestamp * 1000
|
||||
const date = new Date(timestamp)
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const hour = date.getHours()
|
||||
const minute = date.getMinutes()
|
||||
const second = date.getSeconds()
|
||||
return `${year}-${formatNumber(month)}-${formatNumber(day)} ${formatNumber(hour)}:${formatNumber(minute)}:${formatNumber(second)}`
|
||||
}
|
||||
timestamp = timestamp * 1000;
|
||||
const date = new Date(timestamp);
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hour = date.getHours();
|
||||
const minute = date.getMinutes();
|
||||
const second = date.getSeconds();
|
||||
return `${year}-${formatNumber(month)}-${formatNumber(day)} ${formatNumber(hour)}:${formatNumber(minute)}:${formatNumber(second)}`;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { AxiosResponse } from 'axios'
|
||||
import { AxiosResponse } from 'axios';
|
||||
|
||||
/**
|
||||
* 打开链接
|
||||
* @param url 链接地址
|
||||
* @param newTab 是否在新标签页打开
|
||||
*/
|
||||
export function openUrl(url: string, newTab = true) {
|
||||
export function openUrl (url: string, newTab = true) {
|
||||
if (newTab) {
|
||||
window.open(url)
|
||||
window.open(url);
|
||||
} else {
|
||||
window.location.href = url
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,21 @@ export function openUrl(url: string, newTab = true) {
|
||||
* @param response Axios响应
|
||||
* @returns 标准的HTTP报文
|
||||
*/
|
||||
export function parseAxiosResponse(
|
||||
export function parseAxiosResponse (
|
||||
response: AxiosResponse,
|
||||
http_version: string = 'HTTP/1.1'
|
||||
) {
|
||||
if (!response?.status) {
|
||||
return 'No response'
|
||||
return 'No response';
|
||||
}
|
||||
const statusLine = `${http_version} ${response.status} ${response.statusText}`
|
||||
const statusLine = `${http_version} ${response.status} ${response.statusText}`;
|
||||
const headers = Object.entries(response.headers)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join('\r\n')
|
||||
.join('\r\n');
|
||||
const body = response.data
|
||||
? `\r\n\r\n${typeof response.data === 'string' ? JSON.stringify(JSON.parse(response.data), null, 2) : JSON.stringify(response.data, null, 2)}`
|
||||
: ''
|
||||
return `${statusLine}\r\n${headers}${body}`
|
||||
: '';
|
||||
return `${statusLine}\r\n${headers}${body}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,5 +41,5 @@ export function parseAxiosResponse(
|
||||
* @returns 是否为URI
|
||||
*/
|
||||
export const isURI = (uri: string) => {
|
||||
return /^(http|https|file):\/\/.*/.test(uri)
|
||||
}
|
||||
return /^(http|https|file):\/\/.*/.test(uri);
|
||||
};
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
* @returns 版本号数字
|
||||
*/
|
||||
export const versionToNumber = (version: string): number => {
|
||||
const finalVersionString = version.replace(/^v/, '')
|
||||
const finalVersionString = version.replace(/^v/, '');
|
||||
|
||||
const versionArray = finalVersionString.split('.')
|
||||
const versionArray = finalVersionString.split('.');
|
||||
const versionNumber =
|
||||
parseInt(versionArray[2]) +
|
||||
parseInt(versionArray[1]) * 100 +
|
||||
parseInt(versionArray[0]) * 10000
|
||||
parseInt(versionArray[0]) * 10000;
|
||||
|
||||
return versionNumber
|
||||
}
|
||||
return versionNumber;
|
||||
};
|
||||
|
||||
/**
|
||||
* 比较版本号
|
||||
@@ -25,12 +25,12 @@ export const versionToNumber = (version: string): number => {
|
||||
* -1: version1 < version2
|
||||
*/
|
||||
export const compareVersion = (version1: string, version2: string): number => {
|
||||
const versionNumber1 = versionToNumber(version1)
|
||||
const versionNumber2 = versionToNumber(version2)
|
||||
const versionNumber1 = versionToNumber(version1);
|
||||
const versionNumber2 = versionToNumber(version2);
|
||||
|
||||
if (versionNumber1 === versionNumber2) {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
return versionNumber1 > versionNumber2 ? 1 : -1
|
||||
}
|
||||
return versionNumber1 > versionNumber2 ? 1 : -1;
|
||||
};
|
||||
|
||||
@@ -19,10 +19,10 @@ import {
|
||||
ZodTuple,
|
||||
ZodTypeAny,
|
||||
ZodUnion,
|
||||
ZodUnknown
|
||||
} from 'zod'
|
||||
ZodUnknown,
|
||||
} from 'zod';
|
||||
|
||||
export type LiteralValue = string | number | boolean | null
|
||||
export type LiteralValue = string | number | boolean | null;
|
||||
|
||||
export type ParsedSchema = {
|
||||
name?: string
|
||||
@@ -32,37 +32,37 @@ export type ParsedSchema = {
|
||||
enum?: LiteralValue[]
|
||||
children?: ParsedSchema[]
|
||||
description?: string
|
||||
}
|
||||
};
|
||||
|
||||
export function parse(
|
||||
export function parse (
|
||||
schema: ZodTypeAny,
|
||||
name?: string,
|
||||
isRoot = true
|
||||
): ParsedSchema | ParsedSchema[] {
|
||||
const optional = schema.isOptional ? schema.isOptional() : false
|
||||
const description = schema.description
|
||||
const optional = schema.isOptional ? schema.isOptional() : false;
|
||||
const description = schema.description;
|
||||
if (schema instanceof ZodString) {
|
||||
return { name, type: 'string', optional, description }
|
||||
return { name, type: 'string', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodNumber) {
|
||||
return { name, type: 'number', optional, description }
|
||||
return { name, type: 'number', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodBoolean) {
|
||||
return { name, type: 'boolean', optional, description }
|
||||
return { name, type: 'boolean', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodBigInt) {
|
||||
return { name, type: 'bigint', optional, description }
|
||||
return { name, type: 'bigint', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodDate) {
|
||||
return { name, type: 'date', optional, description }
|
||||
return { name, type: 'date', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodUnknown) {
|
||||
return { name, type: 'unknown', optional, description }
|
||||
return { name, type: 'unknown', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodLiteral) {
|
||||
@@ -71,8 +71,8 @@ export function parse(
|
||||
type: 'value',
|
||||
optional,
|
||||
value: schema._def.value as LiteralValue,
|
||||
description
|
||||
}
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
if (schema instanceof ZodEnum) {
|
||||
@@ -81,16 +81,16 @@ export function parse(
|
||||
type: 'enum',
|
||||
optional,
|
||||
enum: schema._def.values as LiteralValue[],
|
||||
description
|
||||
}
|
||||
return data
|
||||
description,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
if (schema instanceof ZodUnion) {
|
||||
const options = schema._def.options
|
||||
const options = schema._def.options;
|
||||
const parsedOptions = options.map(
|
||||
(option: ZodTypeAny) => parse(option, undefined, false) as ParsedSchema
|
||||
)
|
||||
);
|
||||
|
||||
const basicTypes = [
|
||||
'string',
|
||||
@@ -100,42 +100,42 @@ export function parse(
|
||||
'date',
|
||||
'unknown',
|
||||
'value',
|
||||
'enum'
|
||||
]
|
||||
'enum',
|
||||
];
|
||||
const optionTypes: (string | string[])[] = parsedOptions.map(
|
||||
(option: ParsedSchema) => option.type
|
||||
)
|
||||
);
|
||||
const isAllBasicTypes = optionTypes.every((type) =>
|
||||
basicTypes.includes(Array.isArray(type) ? type[0] : type)
|
||||
)
|
||||
);
|
||||
|
||||
if (isAllBasicTypes) {
|
||||
const types = [
|
||||
...new Set(
|
||||
optionTypes.flatMap((type) => (Array.isArray(type) ? type[0] : type))
|
||||
)
|
||||
]
|
||||
return { name, type: types, optional, description }
|
||||
),
|
||||
];
|
||||
return { name, type: types, optional, description };
|
||||
} else {
|
||||
return {
|
||||
name,
|
||||
type: 'union',
|
||||
optional,
|
||||
children: parsedOptions,
|
||||
description
|
||||
}
|
||||
description,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (schema instanceof ZodObject) {
|
||||
const shape = schema._def.shape()
|
||||
const shape = schema._def.shape();
|
||||
const children = Object.keys(shape).map((key) =>
|
||||
parse(shape[key], key, false)
|
||||
) as ParsedSchema[]
|
||||
) as ParsedSchema[];
|
||||
if (isRoot) {
|
||||
return children
|
||||
return children;
|
||||
} else {
|
||||
return { name, type: 'object', optional, children, description }
|
||||
return { name, type: 'object', optional, children, description };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,120 +144,120 @@ export function parse(
|
||||
schema._def.type,
|
||||
undefined,
|
||||
false
|
||||
) as ParsedSchema
|
||||
) as ParsedSchema;
|
||||
return {
|
||||
name,
|
||||
type: 'array',
|
||||
optional,
|
||||
children: Array.isArray(childSchema) ? childSchema : [childSchema],
|
||||
description
|
||||
}
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
if (schema instanceof ZodNullable || schema instanceof ZodDefault) {
|
||||
return parse(schema._def.innerType, name)
|
||||
return parse(schema._def.innerType, name);
|
||||
}
|
||||
|
||||
if (schema instanceof ZodOptional) {
|
||||
const data = parse(schema._def.innerType, name)
|
||||
const data = parse(schema._def.innerType, name);
|
||||
if (Array.isArray(data)) {
|
||||
data.forEach((item) => {
|
||||
item.optional = true
|
||||
item.description = description
|
||||
})
|
||||
item.optional = true;
|
||||
item.description = description;
|
||||
});
|
||||
} else {
|
||||
data.optional = true
|
||||
data.description = description
|
||||
data.optional = true;
|
||||
data.description = description;
|
||||
}
|
||||
return data
|
||||
return data;
|
||||
}
|
||||
|
||||
if (schema instanceof ZodRecord) {
|
||||
const valueType = parse(schema._def.valueType) as ParsedSchema
|
||||
const valueType = parse(schema._def.valueType) as ParsedSchema;
|
||||
return {
|
||||
name,
|
||||
type: 'record',
|
||||
optional,
|
||||
children: [valueType],
|
||||
description
|
||||
}
|
||||
description,
|
||||
};
|
||||
}
|
||||
|
||||
if (schema instanceof ZodTuple) {
|
||||
const items: ParsedSchema[] = schema._def.items.map((item: ZodTypeAny) =>
|
||||
parse(item)
|
||||
)
|
||||
return { name, type: 'tuple', optional, children: items, description }
|
||||
);
|
||||
return { name, type: 'tuple', optional, children: items, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodNull) {
|
||||
return { name, type: 'null', optional, description }
|
||||
return { name, type: 'null', optional, description };
|
||||
}
|
||||
|
||||
if (schema instanceof ZodLazy) {
|
||||
return parse(schema._def.getter(), name)
|
||||
return parse(schema._def.getter(), name);
|
||||
}
|
||||
|
||||
if (schema instanceof ZodEffects) {
|
||||
return parse(schema._def.schema, name)
|
||||
return parse(schema._def.schema, name);
|
||||
}
|
||||
|
||||
return { name, type: 'unknown', optional, description }
|
||||
return { name, type: 'unknown', optional, description };
|
||||
}
|
||||
|
||||
const generateDefault = (schema: ZodSchema): unknown => {
|
||||
if (schema instanceof ZodObject) {
|
||||
const obj: Record<string, unknown> = {}
|
||||
const obj: Record<string, unknown> = {};
|
||||
for (const key in schema.shape) {
|
||||
obj[key] = generateDefault(schema.shape[key])
|
||||
obj[key] = generateDefault(schema.shape[key]);
|
||||
}
|
||||
return obj
|
||||
return obj;
|
||||
}
|
||||
if (schema instanceof ZodString) {
|
||||
return 'textValue'
|
||||
return 'textValue';
|
||||
}
|
||||
if (schema instanceof ZodNumber) {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
if (schema instanceof ZodBoolean) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
if (schema instanceof ZodArray) {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
if (schema instanceof ZodUnion) {
|
||||
return generateDefault(schema._def.options[0])
|
||||
return generateDefault(schema._def.options[0]);
|
||||
}
|
||||
if (schema instanceof ZodEnum) {
|
||||
return schema._def.values[0]
|
||||
return schema._def.values[0];
|
||||
}
|
||||
if (schema instanceof ZodLiteral) {
|
||||
return schema._def.value
|
||||
return schema._def.value;
|
||||
}
|
||||
if (schema instanceof ZodNullable) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
if (schema instanceof ZodOptional) {
|
||||
return generateDefault(schema._def.innerType)
|
||||
return generateDefault(schema._def.innerType);
|
||||
}
|
||||
if (schema instanceof ZodRecord) {
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
if (schema instanceof ZodTuple) {
|
||||
return schema._def.items.map((item: ZodTypeAny) => generateDefault(item))
|
||||
return schema._def.items.map((item: ZodTypeAny) => generateDefault(item));
|
||||
}
|
||||
if (schema instanceof ZodNull) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
if (schema instanceof ZodLazy) {
|
||||
return generateDefault(schema._def.getter())
|
||||
return generateDefault(schema._def.getter());
|
||||
}
|
||||
if (schema instanceof ZodEffects) {
|
||||
return generateDefault(schema._def.schema)
|
||||
return generateDefault(schema._def.schema);
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const generateDefaultJson = (schema: ZodSchema) => {
|
||||
return JSON.stringify(generateDefault(schema), null, 2)
|
||||
}
|
||||
return JSON.stringify(generateDefault(schema), null, 2);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user