init
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, Method as _Method, AxiosResponse } from 'axios';
|
||||
|
||||
import qs from 'qs';
|
||||
import Cookie from 'js-cookie';
|
||||
|
||||
declare interface _AxiosExtend {
|
||||
/**
|
||||
* 发起请求
|
||||
* @param url 请求地址
|
||||
* @param method 请求方法
|
||||
* @param params 请求参数
|
||||
* @param config 请求配置
|
||||
*/
|
||||
request<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
method: Method,
|
||||
params?: Record<string | number, any>,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<R>;
|
||||
/**
|
||||
* 设置token
|
||||
* @param value token值
|
||||
* @param expires 过期时间
|
||||
* - 类型为 number 时,表示 expires 毫秒后 token 过期
|
||||
* - 类型为 Date 时,表示在 expires 这个时间点 token 过期
|
||||
* @param name token 名称,默认为当前 http 实例的 xsrfCookieName 属性值
|
||||
*/
|
||||
setAuthorization(value: string, expires: number | Date, name?: string): void;
|
||||
|
||||
/**
|
||||
* 移出token
|
||||
* @param name token 名称, 默认为当前 http 实例的 xsrfCookieName 属性值
|
||||
*/
|
||||
removeAuthorization(name?: string): void;
|
||||
/**
|
||||
* 校验 token 是否有效
|
||||
* @param name 需要校验的 token 名称,默认为当前 http 实例的 xsrfCookieName 属性值
|
||||
*/
|
||||
checkAuthorization(name?: string): boolean;
|
||||
}
|
||||
|
||||
export interface AxiosHttp extends Omit<AxiosInstance, 'request'>, _AxiosExtend { }
|
||||
|
||||
export type Method = _Method | 'POST_JSON' | 'post_json' | 'PUT_JSON' | 'put_json';
|
||||
|
||||
/**
|
||||
* 转表单格式
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function toFormData(params?: Record<string | number, any>) {
|
||||
const formData = new FormData();
|
||||
if (!params) {
|
||||
return formData;
|
||||
}
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
formData.append(key, val);
|
||||
});
|
||||
} else {
|
||||
formData.set(key, value);
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
function toUrlencoded(params?: Record<string | number, any>) {
|
||||
const urlencoded = new URLSearchParams();
|
||||
for (const key in params) {
|
||||
if (params[key] !== undefined) {
|
||||
urlencoded.append(key, params[key]);
|
||||
}
|
||||
}
|
||||
return urlencoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 axios http
|
||||
* @param config
|
||||
* @returns
|
||||
*/
|
||||
function createAxiosHttp(config: AxiosRequestConfig): AxiosHttp {
|
||||
const _axios = axios.create(config);
|
||||
|
||||
// 添加响应拦截器处理401状态
|
||||
_axios.interceptors.response.use(
|
||||
// 成功响应直接返回
|
||||
response => response,
|
||||
// 错误响应处理
|
||||
error => {
|
||||
// 检查是否是401未授权错误
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 调用removeAuthorization方法清除token
|
||||
http.removeAuthorization();
|
||||
|
||||
// 这里可以添加额外的处理,比如跳转到登录页
|
||||
// 示例: window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
const http: AxiosHttp = {
|
||||
..._axios,
|
||||
request<T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
method: Method,
|
||||
params?: Record<string | number, any>,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<R> {
|
||||
const _method = method.toUpperCase();
|
||||
switch (_method) {
|
||||
case 'GET':
|
||||
return _axios.get(url, {
|
||||
params,
|
||||
paramsSerializer: (data) => {
|
||||
return qs.stringify(data, { indices: false, skipNulls: true });
|
||||
},
|
||||
...config,
|
||||
});
|
||||
case 'POST':
|
||||
return _axios.post(url, toUrlencoded(params), config);
|
||||
case 'POST_JSON':
|
||||
return _axios.post(url, params, config);
|
||||
case 'PUT':
|
||||
return _axios.put(url, toFormData(params), config);
|
||||
case 'PUT_JSON':
|
||||
return _axios.put(url, params, config);
|
||||
case 'DELETE':
|
||||
return _axios.delete(url, { data: toFormData(params), ...config });
|
||||
case 'HEAD':
|
||||
return _axios.head(url, { params, ...config });
|
||||
case 'OPTIONS':
|
||||
return _axios.options(url, { params, ...config });
|
||||
case 'PATCH':
|
||||
return _axios.patch(url, { params, ...config });
|
||||
case 'PURGE':
|
||||
case 'LINK':
|
||||
case 'UNLINK':
|
||||
const m = _method as _Method;
|
||||
return _axios.request({ url, method: m, params, ..._axios.defaults });
|
||||
default:
|
||||
return _axios.request({ url, method: 'GET', params, ..._axios.defaults });
|
||||
}
|
||||
},
|
||||
setAuthorization(token: string, expires: number | Date, name?: string): void {
|
||||
Cookie.set(name ?? _axios.defaults.xsrfCookieName!, token, { expires });
|
||||
},
|
||||
removeAuthorization(name?: string): void {
|
||||
Cookie.remove(name ?? _axios.defaults.xsrfCookieName!);
|
||||
},
|
||||
checkAuthorization(name?: string | undefined): boolean {
|
||||
return Boolean(Cookie.get(name ?? _axios.defaults.xsrfCookieName!));
|
||||
},
|
||||
};
|
||||
|
||||
return http;
|
||||
}
|
||||
|
||||
export default createAxiosHttp;
|
||||
@@ -0,0 +1,51 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* 读取环境变量文件
|
||||
* @param path
|
||||
*/
|
||||
export function readEnvFile(filePath: string) {
|
||||
try {
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
return data
|
||||
.split('\n')
|
||||
.map((kv) => kv.split('='))
|
||||
.filter(([k, v]) => k != undefined && v != undefined)
|
||||
.map(([k, v]) => ({ [k]: v.replace(/\r\n/, '').replace(/\r/, '') }))
|
||||
.reduce((p, c) => ({ ...p, ...c }));
|
||||
} catch (err) {}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取环境变量
|
||||
* @param dirname 环境变量文件目录
|
||||
* @param command 命令 build | dev
|
||||
* @param mode 模式 development | production | test | or any other custom mode
|
||||
*/
|
||||
export function getEnv(dirname: string, command?: string, mode?: string) {
|
||||
const _environment = command === 'build' ? 'production' : 'development';
|
||||
const environment = mode || _environment;
|
||||
const baseEnv = readEnvFile(path.resolve(dirname, `./.env`));
|
||||
const modeEnv = readEnvFile(path.resolve(dirname, `./.env.${environment}`));
|
||||
return { ...baseEnv, ...modeEnv };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件 base64 编码内容
|
||||
* @param file
|
||||
* @returns
|
||||
*/
|
||||
export async function getBase64(file: Blob): Promise<string> {
|
||||
const promise = new Promise<string>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => resolve(reader.result as string));
|
||||
try {
|
||||
reader.readAsDataURL(file);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 格式化金额
|
||||
* @param value
|
||||
* @param fixed
|
||||
* @returns
|
||||
*/
|
||||
export function formatMoney(value: number, fixed: number = 0) {
|
||||
let unit: string = value < 10000 ? '' : value < 100000000 ? 'w' : '亿';
|
||||
value =
|
||||
value < 10000
|
||||
? value
|
||||
: value < 100000000
|
||||
? value / 10000
|
||||
: value / 100000000;
|
||||
let format: string = value.toFixed(fixed);
|
||||
const _val = format.split('.');
|
||||
const _int = _val[0],
|
||||
_dec = _val[1];
|
||||
return `${_val}${unit}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 千位格式化
|
||||
* @param value
|
||||
* @param fixed
|
||||
* @returns
|
||||
*/
|
||||
export function formatThousand(value: number, fixed: number = 0): string {
|
||||
const _val: string[] = value.toFixed(fixed).split('.');
|
||||
let [_int, _dec] = _val;
|
||||
_dec = (parseInt(_dec) === 0 ? undefined : _dec) as string;
|
||||
let numbers = [];
|
||||
let format = '';
|
||||
for (let i = _int.length; i >= 0; i -= 3) {
|
||||
numbers.push(_int.substring(i - 3 < 0 ? 0 : i - 3, i));
|
||||
}
|
||||
return numbers.reverse().join(',') + ((_dec && `.${_dec}`) ?? '');
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 初始化目标值为 undefined 的属性
|
||||
* @param target 目标对象
|
||||
* @param dft 默认值对象
|
||||
*/
|
||||
export function initUndefined<T extends Record<string, any>, K extends keyof T>(target: T, dft: Required<Pick<T, K>>) {
|
||||
(Object.keys(dft) as K[]).forEach((key) => (target[key] = target[key] ?? dft[key]));
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { onMounted, ref, onBeforeUnmount } from 'vue';
|
||||
|
||||
/**
|
||||
* 计算元素距离屏幕左上距离
|
||||
* @param el
|
||||
* @returns
|
||||
*/
|
||||
export function offsetScreen(el: HTMLElement) {
|
||||
let { offsetLeft, offsetTop } = el;
|
||||
|
||||
if (el.offsetParent) {
|
||||
const [left, top] = offsetScreen(el.offsetParent as HTMLElement);
|
||||
offsetLeft += left;
|
||||
offsetTop += top;
|
||||
}
|
||||
|
||||
return [offsetLeft, offsetTop];
|
||||
}
|
||||
|
||||
/**
|
||||
* 全屏api
|
||||
* @param target 需要全屏的元素
|
||||
* @returns
|
||||
*/
|
||||
export function useFullScreen(target: HTMLElement | string) {
|
||||
let _target: HTMLElement = undefined;
|
||||
const name = typeof target === 'object' ? target.tagName : target;
|
||||
onMounted(() => {
|
||||
document.addEventListener('fullscreenchange', fullscreenListener);
|
||||
document.addEventListener('webkitfullscreenchange', fullscreenListener);
|
||||
document.addEventListener('mozfullscreenchange', fullscreenListener);
|
||||
document.addEventListener('msfullscreenchange', fullscreenListener);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('fullscreenchange', fullscreenListener);
|
||||
document.removeEventListener('webkitfullscreenchange', fullscreenListener);
|
||||
document.removeEventListener('mozfullscreenchange', fullscreenListener);
|
||||
document.removeEventListener('msfullscreenchange', fullscreenListener);
|
||||
});
|
||||
|
||||
let state = false;
|
||||
const isEnter = ref(state);
|
||||
function fullscreenListener(e: Event) {
|
||||
if (e.target !== _target) {
|
||||
isEnter.value = false;
|
||||
state = false;
|
||||
} else {
|
||||
state = !state;
|
||||
isEnter.value = state;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入全屏
|
||||
* @returns
|
||||
*/
|
||||
async function enterFullScreen() {
|
||||
return new Promise((resolve, reject) => {
|
||||
_target = typeof target === 'string' ? document.querySelector(target) : target;
|
||||
if (_target) {
|
||||
// @ts-ignore
|
||||
const { requestFullscreen, webkitRequestFullScreen, mozRequestFullScreen, msRequestFullscreen } = _target;
|
||||
const _requestFullscreen: () => void =
|
||||
requestFullscreen ?? webkitRequestFullScreen ?? mozRequestFullScreen ?? msRequestFullscreen;
|
||||
|
||||
if (_requestFullscreen) {
|
||||
const _result = _requestFullscreen.apply(_target) as any;
|
||||
if (_result instanceof Promise) {
|
||||
_result.then(() => resolve(null)).catch((e) => reject(e));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
} else {
|
||||
reject(`sorry, your browser don't support fullscreen feature`);
|
||||
}
|
||||
} else {
|
||||
reject(`can'nt find target element ${name} for fullscreen`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function exitFullscreen() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// @ts-ignore
|
||||
const { exitFullscreen, webkitCancelFullScreen, mozCancelFullScreen, msExitFullscreen } = document;
|
||||
const _exitFullscreen: () => void =
|
||||
exitFullscreen ?? webkitCancelFullScreen ?? mozCancelFullScreen ?? msExitFullscreen;
|
||||
if (_exitFullscreen) {
|
||||
const _result = _exitFullscreen.apply(document) as any;
|
||||
if (_result instanceof Promise) {
|
||||
_result.then(() => resolve(null)).catch((e) => reject(e));
|
||||
} else {
|
||||
isEnter.value = false;
|
||||
resolve(null);
|
||||
}
|
||||
} else {
|
||||
reject(`sorry, your browser don't support fullscreen feature`);
|
||||
}
|
||||
});
|
||||
}
|
||||
return { enterFullScreen, exitFullscreen, isEnter };
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
export const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||
export const domainReg = /^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$/
|
||||
export const portReg = /^([1-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/
|
||||
export const subDomainPrefix = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])$/
|
||||
|
||||
export const passReg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}$/
|
||||
export const userNameReg = /^[a-zA-Z0-9_]{3,}$/
|
||||
|
||||
|
||||
export function checkPass(str: string) {
|
||||
return passReg.test(str)
|
||||
}
|
||||
export function checkUserName(str: string) {
|
||||
return userNameReg.test(str)
|
||||
}
|
||||
|
||||
|
||||
export function checkEmail(str: string) {
|
||||
return emailReg.test(str)
|
||||
}
|
||||
|
||||
export function checkDomain(str: string) {
|
||||
return domainReg.test(str)
|
||||
}
|
||||
export function checkPort(str: string) {
|
||||
return portReg.test(str)
|
||||
}
|
||||
|
||||
export function checksubDomainPrefix(str: string) {
|
||||
if (!str || str === '')
|
||||
return false;
|
||||
const subs = str.split(';')
|
||||
if (subs) {
|
||||
let succ = true
|
||||
subs.forEach(sub => {
|
||||
if (sub && sub != '')
|
||||
if (!subDomainPrefix.test(sub)) {
|
||||
succ = false;
|
||||
}
|
||||
});
|
||||
return succ
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { watch, computed, ref } from 'vue';
|
||||
|
||||
function useModelValue<T>(value: () => T | undefined, onChange: (val?: T) => void, defaultValue?: T) {
|
||||
const _value = ref<T>();
|
||||
_value.value = value() ?? defaultValue;
|
||||
const sValue = computed({
|
||||
get() {
|
||||
return value() ?? _value.value;
|
||||
},
|
||||
set(val: T | undefined) {
|
||||
_value.value = val;
|
||||
onChange(val);
|
||||
},
|
||||
});
|
||||
watch(value, () => {
|
||||
_value.value = value();
|
||||
});
|
||||
return { value: sValue };
|
||||
}
|
||||
|
||||
export default useModelValue;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useSettingStore } from '@/store';
|
||||
import { onBeforeMount, onUnmounted, onActivated, onDeactivated } from 'vue';
|
||||
|
||||
export function useUnbounded() {
|
||||
const { setContentClass } = useSettingStore();
|
||||
|
||||
const setUnbounded = () => {
|
||||
setContentClass('unbounded');
|
||||
setTimeout(() => window.dispatchEvent(new Event('resize')), 300);
|
||||
};
|
||||
|
||||
const removeUnbounded = () => {
|
||||
setContentClass('common');
|
||||
setTimeout(() => window.dispatchEvent(new Event('resize')), 300);
|
||||
};
|
||||
|
||||
onBeforeMount(setUnbounded);
|
||||
onUnmounted(removeUnbounded);
|
||||
onActivated(setUnbounded);
|
||||
onDeactivated(removeUnbounded);
|
||||
}
|
||||
Reference in New Issue
Block a user