2025-03-12 18:41:20 +08:00
|
|
|
|
/* 工具函数 */
|
|
|
|
|
|
import router from '@/router';
|
|
|
|
|
|
import * as CryptoJS from 'crypto-js';
|
|
|
|
|
|
import pick from 'lodash/pick';
|
|
|
|
|
|
import pickBy from 'lodash/pickBy';
|
|
|
|
|
|
import type { createIssueReqType } from '@/api/issue/types';
|
|
|
|
|
|
import type { RepoItemResData } from '@/utils/types';
|
|
|
|
|
|
import DOMPurify from 'dompurify';
|
|
|
|
|
|
import { Message } from 'vue-devui/message';
|
|
|
|
|
|
import qs from 'qs';
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 返回 axios 的 data, 或者 obj 最内层的 data
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const escapeResData = (obj: object) => {
|
|
|
|
|
|
if (!obj) return obj;
|
|
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(obj, 'data')) return obj;
|
|
|
|
|
|
if (obj?.headers?.constructor?.name === 'AxiosHeaders') { return obj?.data; };
|
|
|
|
|
|
return escapeResData(obj?.data);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export function getRepoName(): string {
|
|
|
|
|
|
//
|
|
|
|
|
|
return router.currentRoute.value.params.repoName;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Parse the time to string
|
|
|
|
|
|
* @param {(Object|string|number)} time
|
|
|
|
|
|
* @param {string} cFormat
|
|
|
|
|
|
* @returns {string | null}
|
|
|
|
|
|
*/
|
|
|
|
|
|
function parseTime(time: string | number | Date, cFormat?: string) {
|
|
|
|
|
|
if (!time) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
|
|
|
|
|
|
let date;
|
|
|
|
|
|
if (typeof time === 'object') {
|
|
|
|
|
|
date = time;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if ((typeof time === 'string')) {
|
|
|
|
|
|
if ((/^[0-9]+$/.test(time))) {
|
|
|
|
|
|
time = parseInt(time);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// support safari
|
|
|
|
|
|
time = time.replace(new RegExp(/-/gm), '/');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
|
|
|
|
|
time = time * 1000;
|
|
|
|
|
|
}
|
|
|
|
|
|
date = new Date(time);
|
|
|
|
|
|
}
|
|
|
|
|
|
const formatObj = {
|
|
|
|
|
|
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(/{([ymdhisa])+}/g, (result, key) => {
|
|
|
|
|
|
const value = formatObj[key];
|
|
|
|
|
|
// Note: getDay() returns 0 on Sunday
|
|
|
|
|
|
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value]; }
|
|
|
|
|
|
return value.toString().padStart(2, '0');
|
|
|
|
|
|
});
|
|
|
|
|
|
return time_str;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function fileToBlob(file: any) {
|
|
|
|
|
|
// 创建 FileReader 对象
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
// FileReader 添加 load 事件
|
|
|
|
|
|
reader.addEventListener('load', (e) => {
|
|
|
|
|
|
let blob;
|
|
|
|
|
|
if (typeof e.target.result === 'object') {
|
|
|
|
|
|
blob = new Blob([e.target.result]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
blob = e.target.result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resolve(blob);
|
|
|
|
|
|
});
|
|
|
|
|
|
// FileReader 以 ArrayBuffer 格式 读取 File 对象中数据
|
|
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 图片转 base64
|
|
|
|
|
|
export function imageToBase64(file: File) {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
|
reader.onload = () => resolve(reader.result);
|
|
|
|
|
|
reader.onerror = error => reject(error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function generateHash(userName: string, salt: string): string {
|
|
|
|
|
|
const hashBytes = CryptoJS.SHA256(userName + salt);
|
|
|
|
|
|
return hashBytes.toString(CryptoJS.enc.Hex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getFilePath(hash: string, filetype: string): string {
|
|
|
|
|
|
const chars: string[] = hash.split('');
|
|
|
|
|
|
const sb: string[] = [];
|
|
|
|
|
|
for (const c of chars) {
|
|
|
|
|
|
if (isNaN(Number(c))) {
|
|
|
|
|
|
sb.push(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${sb.slice(0, 2).join('')}/${sb.slice(2, 4).join('')}/${hash}.${filetype}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getImageUrl(iam_id?: string, fileType: string = 'png', isAvatar: boolean = true) {
|
|
|
|
|
|
if (!iam_id) {
|
2025-07-17 11:02:19 +08:00
|
|
|
|
const accountInfo = JSON.parse(localStorage.getItem('opUserInfo') || '{}');
|
2025-03-12 18:41:20 +08:00
|
|
|
|
iam_id = accountInfo.iam_id;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isAvatar) {
|
|
|
|
|
|
const host = 'https://gitcode-img.obs.cn-south-1.myhuaweicloud.com:443/';
|
|
|
|
|
|
const salt = 'avatar';
|
|
|
|
|
|
const hash = generateHash(iam_id || '', salt);
|
|
|
|
|
|
const path = getFilePath(hash, fileType || 'png');
|
|
|
|
|
|
return host + path + '?time=' + new Date().getTime();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理issue put 数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function formatIssuePutData(data: createIssueReqType) {
|
|
|
|
|
|
const params = pick(data, ['title', 'description', 'confidential', 'discussions', 'assignee', 'assignee_id', 'assignee_ids', 'project_id', 'issue_iid', 'labels', 'milestone_id', 'discussion_locked', 'state_event']);
|
|
|
|
|
|
let { labels } = params;
|
|
|
|
|
|
if (typeof labels === 'object') {
|
|
|
|
|
|
labels = labels.map(item => {
|
|
|
|
|
|
if (typeof item === 'string') {
|
|
|
|
|
|
return item;
|
|
|
|
|
|
} else if (typeof item.name === 'string') {
|
|
|
|
|
|
return item.name;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ({
|
|
|
|
|
|
...params,
|
|
|
|
|
|
labels,
|
|
|
|
|
|
issue_category: '-',
|
|
|
|
|
|
issue_stage: '-',
|
|
|
|
|
|
issue_severity: '-'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 提取展示的姓名
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const pickNickName = (author?: any): string => {
|
|
|
|
|
|
return author?.nick_name || author?.nickname || author?.name_cn || author?.name || author?.username || '';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 去除对象中的空值
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function removeEmptyValue(params: object): object {
|
|
|
|
|
|
return pickBy(params, (e) => (e !== undefined && e !== '' && e !== null));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 替换对象中的null为 undefined
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function replaceNull(obj: {[name:string]: any}): any {
|
|
|
|
|
|
for (const k in obj) {
|
|
|
|
|
|
if (obj[k] === null) {
|
|
|
|
|
|
obj[k] = undefined;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return obj;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理多选数据 1.空值 清空select 2.重复项取消 3.未选中的添加
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function formatSelectedData(current: string | object | undefined | null, selectedList: any[], judge: (a: { [name: string]: unknown }, b: { [name: string]: unknown } | unknown) => boolean): object[] {
|
|
|
|
|
|
if (!current) return [];
|
|
|
|
|
|
const index = selectedList.findIndex((item) => {
|
|
|
|
|
|
if (typeof item === 'string') {
|
|
|
|
|
|
return item === current;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return judge && judge(item, current);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
selectedList.splice(index, 1);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
selectedList.push(current);
|
|
|
|
|
|
}
|
|
|
|
|
|
return selectedList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理引用回复文本
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const formatQuoteReply = (str: string) => {
|
|
|
|
|
|
str = str.trim();
|
|
|
|
|
|
if (!str) return '';
|
|
|
|
|
|
str = str.split('\n').map(e => '>' + e).join('\n') + '\n\n';
|
|
|
|
|
|
return str;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断父级元素有没有类名
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function hasClassInParent(target: Element, classNameList: string[]): boolean {
|
|
|
|
|
|
if (target?.parentElement) {
|
|
|
|
|
|
if (classNameList.every(name => target?.classList?.contains(name))) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return hasClassInParent(target?.parentElement, classNameList);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return target ? classNameList.every(name => target?.classList?.contains(name)) : false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 优先展示已经选择的项,已勾选的排在前面
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const sortList = (list: Object[] = [], select: Object[] = []):Object[] => {
|
|
|
|
|
|
list = list.slice(0);
|
|
|
|
|
|
select = select.slice(0);
|
|
|
|
|
|
if (select[0]) {
|
|
|
|
|
|
const sList:any[] = [];
|
|
|
|
|
|
for (let i = 0; i < select.length; i++) {
|
|
|
|
|
|
const index = list.findIndex(e => e.value === select[i]?.value);
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
const one = list.splice(index, 1)[0];
|
|
|
|
|
|
sList.push(one);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return sList.concat(list);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理repo数据便于使用repoItem组件
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const repoDataHandler = (data: RepoItemResData[]) => {
|
|
|
|
|
|
return data.filter((item) => {
|
|
|
|
|
|
return !!item && (item.id || item.resource_id);
|
|
|
|
|
|
}).map((item) => {
|
|
|
|
|
|
const langs = (item.main_repository_language || []).filter((lang) => {
|
|
|
|
|
|
return !!lang;
|
|
|
|
|
|
});
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: item.id || item.resource_id || '',
|
|
|
|
|
|
imgSrc: '',
|
|
|
|
|
|
title: item.name || '-',
|
|
|
|
|
|
desc: item.description || '-',
|
|
|
|
|
|
isStar: item.starred || false,
|
|
|
|
|
|
tag: item.visibility || '',
|
|
|
|
|
|
to: `/${item.namespace}`,
|
|
|
|
|
|
web_url: item.web_url,
|
|
|
|
|
|
iconHandleList: [
|
|
|
|
|
|
{ icon: 'icon-dot-status', value: langs.join(',') || '-', type: 'language', iconColor: 'red', label: '', to: '' },
|
|
|
|
|
|
{ icon: 'gt-star', value: item.star_count, label: '', to: '' },
|
|
|
|
|
|
{ icon: 'gt-fork', value: item.forks_count, label: '', to: '' },
|
|
|
|
|
|
{ icon: 'gt-date', value: item.last_activity_at, label: '', to: '' }
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文案关键词高亮
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const highlightWords = (Word: string, title?: string) => {
|
|
|
|
|
|
if (!Word) return xssPurify(title || '');
|
|
|
|
|
|
title = title ? xssPurify(title) : '';
|
|
|
|
|
|
const regexPattern = new RegExp(`(${Word})`, 'gi');
|
|
|
|
|
|
const str = title?.replace(regexPattern, (_, match) => `<span style="color:red">${match}</span>`);
|
|
|
|
|
|
return str;
|
|
|
|
|
|
};
|
|
|
|
|
|
/* 过滤对象中的空属性值
|
|
|
|
|
|
* @param obj
|
|
|
|
|
|
* @returns
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function filterEmptyObj(obj: object) {
|
|
|
|
|
|
const newObj = obj;
|
|
|
|
|
|
for (const key in newObj) { // 删除空属性值
|
|
|
|
|
|
if (Object.prototype.hasOwnProperty.call(newObj, key)) {
|
|
|
|
|
|
if (!newObj[key]) {
|
|
|
|
|
|
delete newObj[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return newObj;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const fullscreen = (id: string): void => {
|
|
|
|
|
|
if (document.fullscreenElement || document.webkitCurrentFullScreenElement) {
|
|
|
|
|
|
if (document.exitFullscreen) {
|
|
|
|
|
|
document.exitFullscreen();
|
|
|
|
|
|
} else if (document.mozCancelFullScreen) {
|
|
|
|
|
|
// 兼容Firefox
|
|
|
|
|
|
document.mozCancelFullScreen();
|
|
|
|
|
|
} else if (document.webkitExitFullscreen) {
|
|
|
|
|
|
// 兼容Chrome, Safari and Opera等
|
|
|
|
|
|
document.webkitExitFullscreen();
|
|
|
|
|
|
} else if (document.msExitFullscreen) {
|
|
|
|
|
|
// 兼容IE/Edge
|
|
|
|
|
|
document.msExitFullscreen();
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const dom = document.getElementById(id);
|
|
|
|
|
|
if (dom?.requestFullscreen) {
|
|
|
|
|
|
dom.requestFullscreen();
|
|
|
|
|
|
} else if (dom?.mozRequestFullScreen) {
|
|
|
|
|
|
// 兼容Firefox
|
|
|
|
|
|
dom.mozRequestFullScreen();
|
|
|
|
|
|
} else if (dom?.webkitRequestFullScreen) {
|
|
|
|
|
|
// 兼容Chrome, Safari and Opera等
|
|
|
|
|
|
dom.webkitRequestFullScreen();
|
|
|
|
|
|
} else if (dom?.msRequestFullscreen) {
|
|
|
|
|
|
// 兼容IE/Edge
|
|
|
|
|
|
dom.msRequestFullscreen();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
// 对象深层递归合并
|
|
|
|
|
|
export const deepMerge = function(target: object, source: object) {
|
|
|
|
|
|
for (const key in source) {
|
|
|
|
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
|
|
|
|
if (source[key] instanceof Object && key in target && target[key] instanceof Object) {
|
|
|
|
|
|
deepMerge(target[key], source[key]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
target[key] = source[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return target;
|
|
|
|
|
|
};
|
|
|
|
|
|
// 最长公共子序列(模糊匹配)
|
|
|
|
|
|
export const longestCommonSubsequence = function(str1: string, str2: string) {
|
|
|
|
|
|
const m = str1.length;
|
|
|
|
|
|
const n = str2.length;
|
|
|
|
|
|
const dp = new Array(m + 1);
|
|
|
|
|
|
for (let i = 0; i <= m; i++) {
|
|
|
|
|
|
dp[i] = new Array(n + 1).fill(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (let i = 1; i <= m; i++) {
|
|
|
|
|
|
for (let j = 1; j <= n; j++) {
|
|
|
|
|
|
if (str1[i - 1] === str2[j - 1]) {
|
|
|
|
|
|
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let lcs = '';
|
|
|
|
|
|
let i = m; let j = n;
|
|
|
|
|
|
while (i > 0 && j > 0) {
|
|
|
|
|
|
if (str1[i - 1] === str2[j - 1]) {
|
|
|
|
|
|
lcs = str1[i - 1] + lcs;
|
|
|
|
|
|
i--;
|
|
|
|
|
|
j--;
|
|
|
|
|
|
} else if (dp[i - 1][j] > dp[i][j - 1]) {
|
|
|
|
|
|
i--;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
j--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return lcs;
|
|
|
|
|
|
};
|
|
|
|
|
|
export const blurMatch = function(target: string, keywords: string) {
|
|
|
|
|
|
const m = target.length;
|
|
|
|
|
|
const n = keywords.length;
|
|
|
|
|
|
const dp = new Array(m + 1);
|
|
|
|
|
|
for (let i = 0; i <= m; i++) {
|
|
|
|
|
|
dp[i] = new Array(n + 1).fill('');
|
|
|
|
|
|
}
|
|
|
|
|
|
const match = [];
|
|
|
|
|
|
for (let i = 1; i <= m; i++) {
|
|
|
|
|
|
for (let j = 1; j <= n; j++) {
|
|
|
|
|
|
if (target[i - 1] === keywords[j - 1]) {
|
|
|
|
|
|
dp[i][j] = dp[i - 1][j - 1] + keywords[j - 1];
|
|
|
|
|
|
if (typeof match[j - 1] === 'undefined') match[j - 1] = i - 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dp[i][j] = dp[i - 1][j].length > dp[i][j - 1].length ? dp[i - 1][j] : dp[i][j - 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return match;
|
|
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
|
|
|
* xss 过滤
|
|
|
|
|
|
* purify 规则参考:https://github.com/cure53/DOMPurify#control-our-allow-lists-and-block-lists
|
|
|
|
|
|
*/
|
|
|
|
|
|
const purifyConfig = { FORBID_TAGS: ['img'] };
|
|
|
|
|
|
export const xssPurify = (str: string) => DOMPurify.sanitize(str, purifyConfig);
|
|
|
|
|
|
|
|
|
|
|
|
export const messageError = ({ error_message, message }) => Message.error(error_message || message);
|
|
|
|
|
|
|
|
|
|
|
|
export const savePageRef = (fullPath:string) => {
|
2025-08-26 17:52:32 +08:00
|
|
|
|
// const BASE_URL = (import.meta as any).env.VITE_HOST;
|
|
|
|
|
|
const BASE_URL = window.location.origin + '/openRepoPortal';
|
2025-03-12 18:41:20 +08:00
|
|
|
|
const beforeRef = sessionStorage.getItem('ref');
|
|
|
|
|
|
const ref = beforeRef || document.referrer || '';
|
|
|
|
|
|
window.page_ref = ref;
|
|
|
|
|
|
sessionStorage.setItem('ref', BASE_URL + fullPath);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理上报header中的地址,防止 header size 过大 报错431
|
|
|
|
|
|
* @param href string访问地址
|
|
|
|
|
|
* @returns {string} 访问地址
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const cutParamsInUrl = (href: string) => {
|
|
|
|
|
|
if (!href) return '';
|
|
|
|
|
|
try {
|
|
|
|
|
|
const location = new URL(href);
|
|
|
|
|
|
const query = qs.parse(location.search, { ignoreQueryPrefix: true });
|
|
|
|
|
|
for (const k in query) {
|
|
|
|
|
|
if (typeof query[k] === 'string') {
|
|
|
|
|
|
query[k] = query[k]?.substring(0, 100);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
location.search = qs.stringify(query);
|
|
|
|
|
|
return location.href;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.log(error);
|
|
|
|
|
|
return href;
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取注册来源utm_source(有效时间一小时)
|
|
|
|
|
|
* @returns {string} utm_source
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const getSignUtmSource = () => {
|
|
|
|
|
|
const utm_source_sign = localStorage.getItem('utm_source_sign');
|
|
|
|
|
|
if (utm_source_sign) {
|
|
|
|
|
|
const intervalTime = 1000 * 60 * 60;
|
|
|
|
|
|
const currentTime = Date.now();
|
|
|
|
|
|
const utm_source_sign_time = Number(localStorage.getItem('utm_source_sign_time'));
|
|
|
|
|
|
if (utm_source_sign_time + intervalTime < currentTime) {
|
|
|
|
|
|
// 超过一小时,清除utm_source
|
|
|
|
|
|
localStorage.removeItem('utm_source_sign');
|
|
|
|
|
|
localStorage.removeItem('utm_source_sign_time');
|
|
|
|
|
|
return '';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return JSON.parse(utm_source_sign);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
|
|
|
};
|