1
This commit is contained in:
121
src/util/hook.ts
Normal file
121
src/util/hook.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { useRef, useEffect, useCallback, useMemo, useContext, useState } from "react";
|
||||
import { copyText } from "../utils";
|
||||
import { alertContext } from "../contexts/alertContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// 防抖
|
||||
export function useDebounce(func: any, wait: number, immediate: boolean, callback?: any,): (any?: any) => any {
|
||||
let timer = useRef<NodeJS.Timeout | null>();
|
||||
const fnRef = useRef<any>(func);
|
||||
useEffect(() => { fnRef.current = func; }, [func]);
|
||||
const timerCancel = function () { if (timer.current) clearTimeout(timer.current); };
|
||||
|
||||
function debounced(...args: any[]) {
|
||||
const runFunction = () => {
|
||||
return callback
|
||||
? callback(fnRef.current.apply(fnRef.current, args))
|
||||
: fnRef.current.apply(fnRef.current, args);
|
||||
};
|
||||
timerCancel();
|
||||
if (immediate) {
|
||||
let runNow = !timer.current;
|
||||
timer.current = setTimeout(() => { timer.current = null; }, wait);
|
||||
if (runNow) {
|
||||
runFunction();
|
||||
}
|
||||
} else {
|
||||
timer.current = setTimeout(() => { runFunction(); }, wait);
|
||||
}
|
||||
}
|
||||
debounced.cancel = function () { timerCancel(); timer.current = null; };
|
||||
return useCallback(debounced, [wait, immediate, timerCancel, func]);
|
||||
}
|
||||
|
||||
export function useHasForm(flow) {
|
||||
return useMemo(() => {
|
||||
// 如果有 VariableNode inputnode 就属于
|
||||
return !!flow?.data?.nodes.find(node => ["VariableNode", "InputFileNode"].includes(node.data.type))
|
||||
}, [flow])
|
||||
}
|
||||
|
||||
export function useHasReport(flow) {
|
||||
return useMemo(() =>
|
||||
!!flow?.data?.nodes.find(node => "Report" === node.data.type)
|
||||
, [flow])
|
||||
}
|
||||
|
||||
// 复制文案
|
||||
export function useCopyText() {
|
||||
const { t } = useTranslation()
|
||||
const { setSuccessData } = useContext(alertContext);
|
||||
return (url) => {
|
||||
copyText(url).then(() =>
|
||||
setSuccessData({ title: t('chat.copyTip') })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 表格通用逻辑(分页展示、表格数据、关键词检索)
|
||||
export function useTable<T extends object>(param, apiFun) {
|
||||
|
||||
const [page, setPage] = useState({
|
||||
page: 1,
|
||||
pageSize: param.pageSize || 20,
|
||||
keyword: "",
|
||||
});
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<T[]>([]);
|
||||
|
||||
const paramRef = useRef({});
|
||||
|
||||
const requestIdRef = useRef(0); // 控制请求响应顺序
|
||||
const loadData = () => {
|
||||
setLoading(true);
|
||||
const requestId = ++requestIdRef.current
|
||||
apiFun({ ...page, ...paramRef.current }).then(res => {
|
||||
if (requestId !== requestIdRef.current) return
|
||||
if (!("total" in res)) return console.error('该接口不支持分页,无法正常使用 useTable')
|
||||
setData(res.data);
|
||||
setTotal(res.total);
|
||||
setLoading(false);
|
||||
}).catch(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
const debounceLoad = useDebounce(loadData, 600, false)
|
||||
|
||||
// 记录旧值
|
||||
const prevValueRef = useRef(page);
|
||||
useEffect(() => {
|
||||
// 排除页码防抖
|
||||
prevValueRef.current.page === page.page ? debounceLoad() : loadData()
|
||||
prevValueRef.current = page
|
||||
}, [page])
|
||||
|
||||
return {
|
||||
page: page.page,
|
||||
pageSize: page.pageSize,
|
||||
total,
|
||||
loading,
|
||||
data,
|
||||
setPage: (p) => setPage({ ...page, page: p }),
|
||||
reload: debounceLoad,
|
||||
// 检索
|
||||
search: (keyword) => {
|
||||
setPage({ ...page, page: 1, keyword });
|
||||
},
|
||||
// 数据过滤
|
||||
filterData: (p) => {
|
||||
paramRef.current = { ...paramRef.current, ...p };
|
||||
setPage({ ...page, page: 1 });
|
||||
},
|
||||
// 更新数据
|
||||
refreshData: (compareFn, data) => {
|
||||
// 乐观更新
|
||||
setData(list => {
|
||||
return list.map(item => compareFn(item) ? { ...item, ...data } : item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
1204
src/util/reactflowUtils.ts
Normal file
1204
src/util/reactflowUtils.ts
Normal file
File diff suppressed because it is too large
Load Diff
116
src/util/utils.ts
Normal file
116
src/util/utils.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import axios from "axios";
|
||||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { APITemplateType } from "../types/api";
|
||||
import { checkUpperWords } from "../utils";
|
||||
|
||||
export function classNames(...classes: Array<string>): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export function downloadFile(url, label) {
|
||||
return axios.get(url, { responseType: "blob" }).then((res: any) => {
|
||||
const blob = new Blob([res.data]);
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = label;
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
export function downloadJson(content) {
|
||||
const jsonStr = JSON.stringify(content)
|
||||
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(jsonStr)}`;
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = jsonString;
|
||||
link.download = `sample.json`;
|
||||
|
||||
link.click();
|
||||
}
|
||||
|
||||
export function cn(...inputs: ClassValue[]): string {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
// 交集
|
||||
export function intersectArrays(...arrays) {
|
||||
if (arrays.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 使用第一个数组作为基准
|
||||
const baseArray = arrays[0];
|
||||
|
||||
// 过滤出基准数组中的元素,这些元素在其他所有数组中都存在
|
||||
const intersection = baseArray.filter((element) => {
|
||||
return arrays.every((array) => array.includes(element));
|
||||
});
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// 时间戳转换 天时分秒(dhms)
|
||||
export function formatMilliseconds(ms: number, format: string): string {
|
||||
const secondsInMillisecond = 1;
|
||||
const minutesInMillisecond = secondsInMillisecond * 60;
|
||||
const hoursInMillisecond = minutesInMillisecond * 60;
|
||||
const daysInMillisecond = hoursInMillisecond * 24;
|
||||
|
||||
const days = Math.floor(ms / daysInMillisecond);
|
||||
const remainingHours = ms % daysInMillisecond;
|
||||
const hours = Math.floor(remainingHours / hoursInMillisecond);
|
||||
const remainingMinutes = remainingHours % hoursInMillisecond;
|
||||
const minutes = Math.floor(remainingMinutes / minutesInMillisecond);
|
||||
const remainingSeconds = remainingMinutes % minutesInMillisecond;
|
||||
const seconds = Math.floor(remainingSeconds / secondsInMillisecond);
|
||||
|
||||
let formattedString = format.replace('dd', days.toString());
|
||||
formattedString = formattedString.replace('hh', hours.toString());
|
||||
formattedString = formattedString.replace('mm', minutes.toString());
|
||||
formattedString = formattedString.replace('ss', seconds.toString());
|
||||
|
||||
// Remove any extra spaces
|
||||
// formattedString = formattedString.replace(/\s+/g, ' ').trim();
|
||||
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
export function toTitleCase(str: string | undefined): string {
|
||||
if (!str) return "";
|
||||
let result = str
|
||||
.split("_")
|
||||
.map((word, index) => {
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
return result
|
||||
.split("-")
|
||||
.map((word, index) => {
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export function getFieldTitle(
|
||||
template: APITemplateType,
|
||||
templateField: string
|
||||
): string {
|
||||
return template[templateField].display_name
|
||||
? template[templateField].display_name!
|
||||
: template[templateField].name
|
||||
? toTitleCase(template[templateField].name!)
|
||||
: toTitleCase(templateField);
|
||||
}
|
||||
Reference in New Issue
Block a user