180 lines
5.8 KiB
TypeScript
180 lines
5.8 KiB
TypeScript
/**
|
||
* Check if an element has a class
|
||
* @param {HTMLElement} elm
|
||
* @param {string} cls
|
||
* @returns {boolean}
|
||
*/
|
||
export function hasClass(ele: HTMLElement, cls: string) {
|
||
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
|
||
}
|
||
|
||
/**
|
||
* Add class to element
|
||
* @param {HTMLElement} elm
|
||
* @param {string} cls
|
||
*/
|
||
export function addClass(ele: HTMLElement, cls: string) {
|
||
if (!hasClass(ele, cls)) ele.className += ' ' + cls;
|
||
}
|
||
|
||
/**
|
||
* Remove class from element
|
||
* @param {HTMLElement} elm
|
||
* @param {string} cls
|
||
*/
|
||
export function removeClass(ele: HTMLElement, cls: string) {
|
||
if (hasClass(ele, cls)) {
|
||
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
|
||
ele.className = ele.className.replace(reg, ' ');
|
||
}
|
||
}
|
||
|
||
export function mix(color1: string, color2: string, weight: number) {
|
||
weight = Math.max(Math.min(Number(weight), 1), 0);
|
||
const r1 = parseInt(color1.substring(1, 3), 16);
|
||
const g1 = parseInt(color1.substring(3, 5), 16);
|
||
const b1 = parseInt(color1.substring(5, 7), 16);
|
||
const r2 = parseInt(color2.substring(1, 3), 16);
|
||
const g2 = parseInt(color2.substring(3, 5), 16);
|
||
const b2 = parseInt(color2.substring(5, 7), 16);
|
||
const r = Math.round(r1 * (1 - weight) + r2 * weight);
|
||
const g = Math.round(g1 * (1 - weight) + g2 * weight);
|
||
const b = Math.round(b1 * (1 - weight) + b2 * weight);
|
||
const rStr = ('0' + (r || 0).toString(16)).slice(-2);
|
||
const gStr = ('0' + (g || 0).toString(16)).slice(-2);
|
||
const bStr = ('0' + (b || 0).toString(16)).slice(-2);
|
||
return '#' + rStr + gStr + bStr;
|
||
}
|
||
|
||
export function parseTime(time :any, cFormat :any) {
|
||
if (arguments.length === 0) {
|
||
return null
|
||
}
|
||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||
let date
|
||
if (typeof time === 'undefined' || time === null || time === 'null') {
|
||
return ''
|
||
} else if (typeof time === 'object') {
|
||
date = time
|
||
} else {
|
||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||
time = parseInt(time)
|
||
}
|
||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||
time = time * 1000
|
||
}
|
||
date = new Date(time)
|
||
}
|
||
const formatObj:any = {
|
||
y: date.getFullYear(),
|
||
m: date.getMonth() + 1,
|
||
d: date.getDate(),
|
||
h: date.getHours(),
|
||
i: date.getMinutes(),
|
||
s: date.getSeconds(),
|
||
a: date.getDay()
|
||
}
|
||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result :any, key :any) => {
|
||
let value : any = formatObj[key]
|
||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
|
||
if (result.length > 0 && value < 10) {
|
||
value = '0' + value
|
||
}
|
||
return value || 0
|
||
})
|
||
return time_str
|
||
}
|
||
export function downloadFile(obj: any, name: any, suffix: any) {
|
||
try {
|
||
const url = window.URL.createObjectURL(new Blob([obj]));
|
||
const link = document.createElement('a');
|
||
link.style.display = 'none';
|
||
link.href = url;
|
||
|
||
// 优化文件名生成逻辑,避免多余的中横线
|
||
const timeStamp = parseTime(new Date(), '{y}{m}{d}{h}{i}{s}') || '';
|
||
const separator = timeStamp ? '-' : '';
|
||
const fileName = `${timeStamp}${separator}${name}.${suffix}`;
|
||
|
||
link.setAttribute('download', fileName);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
|
||
// 延迟移除,确保下载触发
|
||
setTimeout(() => {
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(url);
|
||
}, 100);
|
||
} catch (error) {
|
||
console.error('Download failed:', error);
|
||
}
|
||
}
|
||
/**
|
||
* 通过 URL 下载文件
|
||
* @param url 文件地址
|
||
* @param fileName 文件名(可选,如果不传则尝试从 URL 解析或使用默认名)
|
||
*/
|
||
export function downloadFileByUrl(url: string, fileName?: string) {
|
||
if (!url) return;
|
||
|
||
// 如果没有提供文件名,尝试从 URL 中提取
|
||
let name = fileName;
|
||
if (!name) {
|
||
const urlParts = url.split('/');
|
||
name = urlParts[urlParts.length - 1] || 'download_file';
|
||
// 去除查询参数
|
||
name = name.split('?')[0];
|
||
}
|
||
|
||
// 方法 1: 使用 fetch 获取 Blob (推荐,可重命名且强制下载)
|
||
// 注意:如果 URL 跨域且服务器未配置 CORS,fetch 会失败
|
||
fetch(url)
|
||
.then((response) => {
|
||
if (!response.ok) throw new Error('Network response was not ok');
|
||
return response.blob();
|
||
})
|
||
.then((blob) => {
|
||
const blobUrl = window.URL.createObjectURL(blob);
|
||
const link = document.createElement('a');
|
||
link.style.display = 'none';
|
||
link.href = blobUrl;
|
||
link.setAttribute('download', name);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
|
||
// 清理
|
||
setTimeout(() => {
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(blobUrl);
|
||
}, 100);
|
||
})
|
||
.catch((error) => {
|
||
console.warn('Fetch download failed (possibly CORS), falling back to direct link:', error);
|
||
|
||
// 方法 2: 回退方案 - 直接使用 a 标签跳转
|
||
// 这种方式对于 PDF/图片等浏览器支持预览的文件,可能会直接在新标签页打开而不是下载
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.target = '_blank';
|
||
// 如果同源,download 属性生效;如果跨域,大多数浏览器会忽略 download 属性并直接打开
|
||
if (fileName) {
|
||
link.setAttribute('download', fileName);
|
||
}
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
});
|
||
}
|
||
const modules = import.meta.glob('@/assets/legend/*.svg', { eager: true });
|
||
// 图例图标映射
|
||
export const iconMap: Record<string, string> = {};
|
||
Object.entries(modules).forEach(([path, module]) => {
|
||
const fileName = path.match(/\/([^/]+)\.svg$/)?.[1];
|
||
if (fileName) {
|
||
iconMap[fileName] = (module as any).default;
|
||
}
|
||
});
|
||
// 获取图标路径
|
||
export const getIconPath = (icon: string): string => {
|
||
return iconMap[icon] || '';
|
||
}; |