初始化

This commit is contained in:
xiaoyi
2024-01-27 19:53:17 +08:00
commit 07dbe71c31
840 changed files with 119152 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
import CryptoJS from 'crypto-js'
const CryptoSecret = '__CRYPTO_SECRET__'
export function enCrypto(data: any) {
const str = JSON.stringify(data)
return CryptoJS.AES.encrypt(str, CryptoSecret).toString()
}
export function deCrypto(data: string) {
const bytes = CryptoJS.AES.decrypt(data, CryptoSecret)
const str = bytes.toString(CryptoJS.enc.Utf8)
if (str)
return JSON.parse(str)
return null
}

View File

@@ -0,0 +1,59 @@
/**
* 转义 HTML 字符
* @param source
*/
export function encodeHTML(source: string) {
return source
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
/**
* 判断是否为代码块
* @param text
*/
export function includeCode(text: string | null | undefined) {
const regexp = /^(?:\s{4}|\t).+/gm
return !!(text?.includes(' = ') || text?.match(regexp))
}
/**
* 复制文本
* @param options
*/
export function copyText(options: { text: string; origin?: boolean }) {
const props = { origin: true, ...options }
let input: HTMLInputElement | HTMLTextAreaElement
if (props.origin)
input = document.createElement('textarea')
else
input = document.createElement('input')
input.setAttribute('readonly', 'readonly')
input.value = props.text
document.body.appendChild(input)
input.select()
if (document.execCommand('copy'))
document.execCommand('copy')
document.body.removeChild(input)
}
export function utcToShanghaiTime(utcTime: string, format: string): string {
const date = new Date(utcTime)
const shanghaiTime = date.getTime() + 8 * 60 * 60 * 1000
const shanghaiDate = new Date(shanghaiTime)
let result = format.replace('YYYY', shanghaiDate.getFullYear().toString())
result = result.replace('MM', (`0${shanghaiDate.getMonth() + 1}`).slice(-2))
result = result.replace('DD', (`0${shanghaiDate.getDate()}`).slice(-2))
result = result.replace('hh', (`0${shanghaiDate.getHours()}`).slice(-2))
result = result.replace('mm', (`0${shanghaiDate.getMinutes()}`).slice(-2))
result = result.replace('ss', (`0${shanghaiDate.getSeconds()}`).slice(-2))
return result
}

View File

@@ -0,0 +1,12 @@
export function formatDate(dateString: string, format: string): string {
const date = new Date(dateString)
const year = date.getFullYear()
const month = (`0${date.getMonth() + 1}`).slice(-2)
const day = (`0${date.getDate()}`).slice(-2)
format = format.replace('yyyy', year.toString())
format = format.replace('MM', month)
format = format.replace('dd', day)
return format
}

View File

@@ -0,0 +1,18 @@
type CallbackFunc<T extends unknown[]> = (...args: T) => void
export function debounce<T extends unknown[]>(
func: CallbackFunc<T>,
wait: number,
): (...args: T) => void {
let timeoutId: ReturnType<typeof setTimeout> | undefined
return (...args: T) => {
const later = () => {
clearTimeout(timeoutId)
func(...args)
}
clearTimeout(timeoutId)
timeoutId = setTimeout(later, wait)
}
}

View File

@@ -0,0 +1,7 @@
export function getCurrentDate() {
const date = new Date()
const day = date.getDate()
const month = date.getMonth() + 1
const year = date.getFullYear()
return `${year}-${month}-${day}`
}

View File

@@ -0,0 +1,17 @@
type ThrottledFunction<T extends any[]> = (...args: T) => void
export function throttle<T extends any[]>(func: ThrottledFunction<T>, delay: number): ThrottledFunction<T> {
let timeoutId: ReturnType<typeof setTimeout> | null
let lastArgs: T
return function throttled(...args: T) {
lastArgs = args
if (!timeoutId) {
timeoutId = setTimeout(() => {
func.apply(this, lastArgs)
timeoutId = null
}, delay)
}
}
}

View File

@@ -0,0 +1,55 @@
export function isNumber<T extends number>(value: T | unknown): value is number {
return Object.prototype.toString.call(value) === '[object Number]'
}
export function isString<T extends string>(value: T | unknown): value is string {
return Object.prototype.toString.call(value) === '[object String]'
}
export function isBoolean<T extends boolean>(value: T | unknown): value is boolean {
return Object.prototype.toString.call(value) === '[object Boolean]'
}
export function isNull<T extends null>(value: T | unknown): value is null {
return Object.prototype.toString.call(value) === '[object Null]'
}
export function isUndefined<T extends undefined>(value: T | unknown): value is undefined {
return Object.prototype.toString.call(value) === '[object Undefined]'
}
export function isObject<T extends object>(value: T | unknown): value is object {
return Object.prototype.toString.call(value) === '[object Object]'
}
export function isArray<T extends any[]>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Array]'
}
export function isFunction<T extends (...args: any[]) => any | void | never>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Function]'
}
export function isDate<T extends Date>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Date]'
}
export function isRegExp<T extends RegExp>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object RegExp]'
}
export function isPromise<T extends Promise<any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Promise]'
}
export function isSet<T extends Set<any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Set]'
}
export function isMap<T extends Map<any, any>>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object Map]'
}
export function isFile<T extends File>(value: T | unknown): value is T {
return Object.prototype.toString.call(value) === '[object File]'
}

View File

@@ -0,0 +1,59 @@
import { h, defineComponent, withDirectives, resolveDirective } from "vue";
export default defineComponent({
name: "Motion",
props: {
delay:{
type: Number,
default: 50
},
y:{
type: Number,
default: 50
},
opacity:{
type: Number,
default: 0.5
},
scale: {
type: Number,
default: 1
},
duration:{
type: Number,
default: 350
}
},
setup(props, { slots }) {
// 使用 setup 函数的参数来访问 props 和 slots
return () => {
const motion = resolveDirective("motion");
return withDirectives(
h(
"div",
{},
{
default: () => [slots.default?.()]
}
),
[
[
motion,
{
initial: { opacity: props.opacity, y: props.y, scale: props.scale },
enter: {
opacity: 1,
y: 0,
scale: 1,
transition : {
delay: props.delay,
duration: props.duration
}
}
}
]
]
);
};
}
});

View File

@@ -0,0 +1,37 @@
import axios, { type AxiosResponse } from 'axios'
import { useAuthStore, useGlobalStore } from '@/store'
const service = axios.create({
baseURL: import.meta.env.VITE_GLOB_API_URL,
timeout: 2400 * 1000,
})
service.interceptors.request.use(
(config) => {
const token = useAuthStore().token
const fingerprint = useGlobalStore()?.fingerprint
const currentDomain = window.location.origin;
config.headers['X-Website-Domain'] = currentDomain;
fingerprint && (config.headers.Fingerprint = fingerprint)
if (token)
config.headers.Authorization = `Bearer ${token}`
return config
},
(error) => {
return Promise.reject(error.response)
},
)
service.interceptors.response.use(
(response: AxiosResponse): AxiosResponse => {
if ([200, 201].includes(response.status))
return response
throw new Error(response.status.toString())
},
(error) => {
return Promise.reject(error)
},
)
export default service

View File

@@ -0,0 +1,113 @@
import type { AxiosProgressEvent, AxiosResponse, GenericAbortSignal } from 'axios'
import request from './axios'
import { useAuthStore } from '@/store'
export interface HttpOption {
url: string
data?: any
method?: string
headers?: any
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
signal?: GenericAbortSignal
beforeRequest?: () => void
afterRequest?: () => void
}
export interface Response<T = any> {
data: T
message: string | null
status: string
}
let last401ErrorTimestamp = 0
const homePagePath = ['/chatlog/chatList', '/group/query']
function hasWhitePath(path: string) {
if (!path)
return false
return homePagePath.some(item => path.includes(item))
}
function http<T = any>(
{ url, data, method, headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
) {
const successHandler = (res: AxiosResponse<Response<T>>) => {
const authStore = useAuthStore()
const { code } = res.data
if ((code >= 200 && code < 300) || !code)
return res.data
if (code === 401) {
authStore.removeToken()
window.location.reload()
}
return Promise.reject(res.data)
}
const failHandler = (error: Response<Error>) => {
const authStore = useAuthStore()
let data = ''
error.response?.data && (data = error.response.data)
afterRequest?.()
const status = error?.response?.status
if (status === 401) {
authStore.removeToken()
if (!hasWhitePath(error?.request?.responseURL)) {
authStore.loadInit && authStore.setLoginDialog(true)
const message = error.response.data?.message || '请先登录后再进行使用!'
Date.now() - last401ErrorTimestamp > 3000 && window.$message.error(message)
}
last401ErrorTimestamp = Date.now()
}
else {
if (data && !data?.success)
window.$message.error(data?.message || '请求接口错误!')
}
throw new Error(error.response?.data?.message || error || 'Error')
}
beforeRequest?.()
method = method || 'GET'
const params = Object.assign(typeof data === 'function' ? data() : data ?? {}, {})
return method === 'GET'
? request.get(url, { params, signal, onDownloadProgress }).then(successHandler, failHandler)
: request.post(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler)
}
export function get<T = any>(
{ url, data, method = 'GET', onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
return http<T>({
url,
method,
data,
onDownloadProgress,
signal,
beforeRequest,
afterRequest,
})
}
export function post<T = any>(
{ url, data, method = 'POST', headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
return http<T>({
url,
method,
data,
headers,
onDownloadProgress,
signal,
beforeRequest,
afterRequest,
})
}
export default post

View File

@@ -0,0 +1 @@
export * from './local'

View File

@@ -0,0 +1,70 @@
import { deCrypto, enCrypto } from '../crypto'
interface StorageData<T = any> {
data: T
expire: number | null
}
export function createLocalStorage(options?: { expire?: number | null; crypto?: boolean }) {
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
const { expire, crypto } = Object.assign(
{
expire: DEFAULT_CACHE_TIME,
crypto: true,
},
options,
)
function set<T = any>(key: string, data: T) {
const storageData: StorageData<T> = {
data,
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
}
const json = crypto ? enCrypto(storageData) : JSON.stringify(storageData)
window.localStorage.setItem(key, json)
}
function get(key: string) {
const json = window.localStorage.getItem(key)
if (json) {
let storageData: StorageData | null = null
try {
storageData = crypto ? deCrypto(json) : JSON.parse(json)
}
catch {
// Prevent failure
}
if (storageData) {
const { data, expire } = storageData
if (expire === null || expire >= Date.now())
return data
}
remove(key)
return null
}
}
function remove(key: string) {
window.localStorage.removeItem(key)
}
function clear() {
window.localStorage.clear()
}
return {
set,
get,
remove,
clear,
}
}
export const ls = createLocalStorage()
export const ss = createLocalStorage({ expire: null, crypto: false })