feat: implement gas and delivery admin systems

This commit is contained in:
david
2026-07-30 14:16:58 +08:00
parent 1093385f95
commit f5ecc0d973
252 changed files with 49385 additions and 363 deletions

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 };

View File

@@ -0,0 +1,3 @@
const debug = import.meta.env.MODE !== 'production';
export default debug;

View File

@@ -0,0 +1,19 @@
import axios from 'axios';
import type { App, ComponentPublicInstance } from 'vue';
export default function setupErrorReport(app: App, baseUrl: string) {
if (!baseUrl) {
return;
}
app.config.errorHandler = (
err: unknown,
instance: ComponentPublicInstance | null,
info: string,
) => {
axios.post(`${baseUrl}/report-error`, {
err,
instance,
info,
});
};
}

View File

@@ -0,0 +1,27 @@
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);
}
}

View File

@@ -0,0 +1,23 @@
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 =
/^(?!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;

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]' && !Number.isNaN(obj);
}
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;
}

View File

@@ -0,0 +1,8 @@
export function parseUrlQuery(url: string): Record<string, string> {
const { searchParams } = new URL(url, 'http://localhost');
const query: Record<string, string> = {};
searchParams.forEach((value, key) => {
query[key] = value;
});
return query;
}

View File

@@ -0,0 +1,31 @@
/**
* Listening to routes alone would waste rendering performance.
* Use the publish-subscribe model for distribution management.
*/
import mitt, { type 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);
}