feat: init

This commit is contained in:
ygx
2026-03-05 23:45:39 +08:00
commit 8fab91c5c7
214 changed files with 33682 additions and 0 deletions

19
src/utils/auth.ts Normal file
View File

@@ -0,0 +1,19 @@
const TOKEN_KEY = 'token'
const isLogin = () => {
return !!localStorage.getItem(TOKEN_KEY)
}
const getToken = () => {
return localStorage.getItem(TOKEN_KEY)
}
const setToken = (token: string) => {
localStorage.setItem(TOKEN_KEY, token)
}
const clearToken = () => {
localStorage.removeItem(TOKEN_KEY)
}
export { clearToken, getToken, isLogin, setToken }

4
src/utils/env.ts Normal file
View File

@@ -0,0 +1,4 @@
// const debug = import.meta.env.MODE !== 'production'
const debug = true
export default debug

16
src/utils/event.ts Normal file
View File

@@ -0,0 +1,16 @@
export function addEventListen(target: Window | HTMLElement, event: string, handler: EventListenerOrEventListenerObject, capture = false) {
if (target.addEventListener && typeof target.addEventListener === 'function') {
target.addEventListener(event, handler, capture)
}
}
export function removeEventListen(
target: Window | HTMLElement,
event: string,
handler: EventListenerOrEventListenerObject,
capture = false
) {
if (target.removeEventListener && typeof target.removeEventListener === 'function') {
target.removeEventListener(event, handler, capture)
}
}

22
src/utils/index.ts Normal file
View File

@@ -0,0 +1,22 @@
type TargetContext = '_self' | '_parent' | '_blank' | '_top'
export const openWindow = (url: string, opts?: { target?: TargetContext; [key: string]: any }) => {
const { target = '_blank', ...others } = opts || {}
window.open(
url,
target,
Object.entries(others)
.reduce((preValue: string[], curValue) => {
const [key, value] = curValue
return [...preValue, `${key}=${value}`]
}, [])
.join(',')
)
}
export const regexUrl = new RegExp(
'^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
'i'
)
export default null

53
src/utils/is.ts Normal file
View File

@@ -0,0 +1,53 @@
const opt = Object.prototype.toString
export function isArray(obj: any): obj is any[] {
return opt.call(obj) === '[object Array]'
}
export function isObject(obj: any): obj is { [key: string]: any } {
return opt.call(obj) === '[object Object]'
}
export function isString(obj: any): obj is string {
return opt.call(obj) === '[object String]'
}
export function isNumber(obj: any): obj is number {
return opt.call(obj) === '[object Number]' && obj === obj // eslint-disable-line
}
export function isRegExp(obj: any) {
return opt.call(obj) === '[object RegExp]'
}
export function isFile(obj: any): obj is File {
return opt.call(obj) === '[object File]'
}
export function isBlob(obj: any): obj is Blob {
return opt.call(obj) === '[object Blob]'
}
export function isUndefined(obj: any): obj is undefined {
return obj === undefined
}
export function isNull(obj: any): obj is null {
return obj === null
}
export function isFunction(obj: any): obj is (...args: any[]) => any {
return typeof obj === 'function'
}
export function isEmptyObject(obj: any): boolean {
return isObject(obj) && Object.keys(obj).length === 0
}
export function isExist(obj: any): boolean {
return obj || obj === 0
}
export function isWindow(el: any): el is Window {
return el === window
}

24
src/utils/monitor.ts Normal file
View File

@@ -0,0 +1,24 @@
import { App, ComponentPublicInstance } from 'vue'
import axios from 'axios'
export default function handleError(Vue: App, baseUrl: string) {
if (!baseUrl) {
return
}
Vue.config.errorHandler = (err: unknown, instance: ComponentPublicInstance | null, info: string) => {
// send error info
axios.post(`${baseUrl}/report-error`, {
err,
instance,
info,
// location: window.location.href,
// message: err.message,
// stack: err.stack,
// browserInfo: getBrowserInfo(),
// user info
// dom info
// url info
// ...
})
}
}

View File

@@ -0,0 +1,28 @@
/**
* Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management
* 单独监听路由会浪费渲染性能。使用发布订阅模式去进行分发管理。
*/
import mitt, { Handler } from 'mitt'
import type { RouteLocationNormalized } from 'vue-router'
const emitter = mitt()
const key = Symbol('ROUTE_CHANGE')
let latestRoute: RouteLocationNormalized
export function setRouteEmitter(to: RouteLocationNormalized) {
emitter.emit(key, to)
latestRoute = to
}
export function listenerRouteChange(handler: (route: RouteLocationNormalized) => void, immediate = true) {
emitter.on(key, handler as Handler)
if (immediate && latestRoute) {
handler(latestRoute)
}
}
export function removeRouteListener() {
emitter.off(key)
}

23
src/utils/setup-mock.ts Normal file
View File

@@ -0,0 +1,23 @@
import debug from './env'
export default ({ mock, setup }: { mock?: boolean; setup: () => void }) => {
if (mock !== false && debug) setup()
}
export const successResponseWrap = (data: unknown) => {
return {
data,
status: 'ok',
msg: '请求成功',
code: 20000,
}
}
export const failResponseWrap = (data: unknown, msg: string, code = 50000) => {
return {
data,
status: 'fail',
msg,
code,
}
}