fix: add a button to last page on logs

This commit is contained in:
jialin
2025-01-08 15:26:03 +08:00
parent 2a9c501ee2
commit decb3ef2f1
21 changed files with 245 additions and 127 deletions
+4 -4
View File
@@ -1,5 +1,5 @@
import { CloseCircleOutlined } from '@ant-design/icons';
import { Progress } from 'antd';
import { Progress, ProgressProps } from 'antd';
import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer';
import React, { useCallback } from 'react';
@@ -21,6 +21,7 @@ interface SingleImageProps {
autoBgColor?: boolean;
editable?: boolean;
style?: React.CSSProperties;
loadingSize?: ProgressProps['size'];
progressType?: 'line' | 'circle' | 'dashboard';
progressColor?: string;
progressWidth?: number;
@@ -42,10 +43,8 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
dataUrl,
style,
autoBgColor,
progressColor = 'var(--ant-color-primary)',
progressWidth = 2,
preview = true,
progressType = 'dashboard'
loadingSize = 'default'
} = props;
const imgWrapper = React.useRef<HTMLSpanElement>(null);
@@ -137,6 +136,7 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
<Progress
percent={progress}
type="dashboard"
size={loadingSize}
steps={{ count: 50, gap: 2 }}
format={() => (
<span className="font-size-20">{progress}%</span>
+97 -53
View File
@@ -34,6 +34,9 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
onSave,
uploadButton
}) => {
const MIN_SCALE = 0.4;
const MAX_SCALE = 10;
const zoomFactor = 1.1;
const intl = useIntl();
const canvasRef = useRef<HTMLCanvasElement>(null);
const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
@@ -47,25 +50,20 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const autoScale = useRef<number>(1);
const cursorRef = useRef<HTMLDivElement>(null);
const [imgLoaded, setImgLoaded] = useState(false);
let scale = 1;
let offsetX = 0;
let offsetY = 0;
const MIN_SCALE = 0.5;
const MAX_SCALE = 5;
const offsetRef = useRef({ x: 0, y: 0 });
const originPosition = useRef({ x: 0, y: 0 });
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
const overlayCanvas = overlayCanvasRef.current!;
const rect = overlayCanvas.getBoundingClientRect();
// 获取鼠标在画布上的原始坐标
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const transformedX = x - overlayCanvas.width / 2;
const transformedY = y - overlayCanvas.height / 2;
console.log('Mouse Coordinates (Transformed):', transformedX, transformedY);
// 考虑缩放比例和偏移量
const transformedX = (x - offsetRef.current.x) / autoScale.current;
const transformedY = (y - offsetRef.current.y) / autoScale.current;
return { x: transformedX, y: transformedY };
};
@@ -113,6 +111,10 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
ctx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
offscreenCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
originPosition.current = {
x: ctx!.canvas.width / 2,
y: ctx!.canvas.height / 2
};
}, [canvasRef.current, overlayCanvasRef.current]);
const scaleCanvasSize = useCallback(() => {
@@ -439,8 +441,8 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
1
);
canvas!.width = img.width * scale;
canvas!.height = img.height * scale;
canvas!.width = img.width;
canvas!.height = img.height;
autoScale.current = scale / autoScale.current;
@@ -457,6 +459,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
canvas!.width,
canvas!.height
);
canvas!.style.transform = `scale(${scale})`;
resolve();
};
});
@@ -502,61 +505,102 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const y = event.clientY - rect.top;
// 考虑缩放比例和偏移量
const transformedX = (x - offsetX) / scale;
const transformedY = (y - offsetY) / scale;
const transformedX = (x - offsetRef.current.x) / autoScale.current;
const transformedY = (y - offsetRef.current.y) / autoScale.current;
return { x: transformedX, y: transformedY };
};
const getMousePosition = (event: any) => {
const canvas = overlayCanvasRef.current!;
const rect = canvas.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
const { x: originX, y: originY } = originPosition.current;
const canvasX = (mouseX - originX) / autoScale.current;
const canvasY = (mouseY - originY) / autoScale.current;
return { x: canvasX, y: canvasY };
};
const updateCavasTransform = (event: any) => {
const canvas = overlayCanvasRef.current!;
const rect = canvas.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
// 鼠标在中心坐标系中的位置
const { x: originX, y: originY } = originPosition.current;
const canvasX = (mouseX - originX) / autoScale.current;
const canvasY = (mouseY - originY) / autoScale.current;
// 更新缩放比例
// max scale: 2, min scale: 0.3
const zoomIn = event.deltaY < 0;
let newScale = autoScale.current;
if (zoomIn) {
newScale = Math.min(MAX_SCALE, autoScale.current * zoomFactor);
} else {
newScale = Math.max(MIN_SCALE, autoScale.current / zoomFactor);
}
console.log('New Scale:', newScale);
autoScale.current = newScale;
// 更新原点
const neworiginX = mouseX - canvasX * newScale;
const neworiginY = mouseY - canvasY * newScale;
originPosition.current = { x: neworiginX, y: neworiginY };
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
const canvasCtx = canvasRef.current!.getContext('2d')!;
// 更新 Canvas 的变换
overlayCtx.setTransform(
autoScale.current,
0,
0,
autoScale.current,
neworiginX,
neworiginY
);
canvasCtx.setTransform(
autoScale.current,
0,
0,
autoScale.current,
neworiginX,
neworiginY
);
};
const handleOnWheel = (event: WheelEvent) => {
event.preventDefault();
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9;
const newScale = Math.min(
MAX_SCALE,
Math.max(MIN_SCALE, scale * zoomFactor)
);
updateCavasTransform(event);
const rect = canvasRef.current!.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
// 计算新的偏移量
offsetX = mouseX - (mouseX - offsetX) * (newScale / scale);
offsetY = mouseY - (mouseY - offsetY) * (newScale / scale);
// 更新缩放比例
scale = newScale;
// 设置画布的变换
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
overlayCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
const canvasCtx = canvasRef.current!.getContext('2d')!;
canvasCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
overlayCanvasRef.current!.style.transform = `scale(${scale})`;
canvasRef.current!.style.transform = `scale(${scale})`;
console.log('Zoom:', scale, offsetX, offsetY);
overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
canvasRef.current!.style.transform = `scale(${autoScale.current})`;
};
useEffect(() => {
initializeImage();
}, [initializeImage]);
// useEffect(() => {
// const container = containerRef.current;
// if (!container) return;
// if (container) {
// resizeObserver.current = new ResizeObserver(
// _.throttle(handleResize, 100)
// );
// resizeObserver.current.observe(container);
// }
useEffect(() => {
const container = containerRef.current;
if (!container) return;
if (container) {
resizeObserver.current = new ResizeObserver(
_.throttle(handleResize, 100)
);
resizeObserver.current.observe(container);
}
// return () => {
// resizeObserver.current?.disconnect();
// };
// }, [handleResize, containerRef.current]);
return () => {
resizeObserver.current?.disconnect();
};
}, [handleResize, containerRef.current]);
useEffect(() => {
createOffscreenCanvas();
+38 -16
View File
@@ -1,4 +1,8 @@
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import {
DownOutlined,
UpOutlined,
VerticalLeftOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Tooltip } from 'antd';
import React from 'react';
@@ -10,10 +14,11 @@ interface LogsPaginationProps {
pageSize?: number;
onPrev?: () => void;
onNext?: () => void;
onBackend?: () => void;
}
const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
const { page, total, pageSize, onNext, onPrev } = props;
const { page, total, pageSize, onNext, onPrev, onBackend } = props;
const intl = useIntl();
const handleOnPrev = () => {
@@ -27,6 +32,7 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
return (
<div className="pagination">
<Tooltip
placement="left"
title={intl.formatMessage(
{ id: 'models.logs.pagination.prev' },
{ lines: pageSize }
@@ -46,21 +52,37 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
<span className="total">{total}</span>
</span>
{page < total && (
<Tooltip
title={intl.formatMessage(
{ id: 'models.logs.pagination.next' },
{ lines: pageSize }
)}
>
<Button
onClick={handleOnNext}
type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)' }}
<>
<Tooltip
placement="left"
title={intl.formatMessage(
{ id: 'models.logs.pagination.next' },
{ lines: pageSize }
)}
>
<DownOutlined />
</Button>
</Tooltip>
<Button
onClick={handleOnNext}
type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)' }}
>
<DownOutlined />
</Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({ id: 'models.logs.pagination.last' })}
placement="left"
>
<Button
onClick={onBackend}
type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)', marginTop: 10 }}
>
<VerticalLeftOutlined rotate={90} />
</Button>
</Tooltip>
</>
)}
</div>
);
@@ -150,5 +150,10 @@ self.onmessage = function (event) {
content: item.content,
uid: item.uid
}));
console.log('result===', result);
self.postMessage(result);
};
self.onerror = function (event) {
console.error('parse logs error===', event);
};
@@ -11,6 +11,10 @@
background-color: rgba(71, 71, 71, 100%) !important;
}
.ant-btn {
background-color: rgba(71, 71, 71, 50%) !important;
}
.pages {
display: flex;
justify-content: center;
+44 -23
View File
@@ -49,6 +49,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
abort() {
chunkRequedtRef.current?.current?.abort?.();
logParseWorker.current?.terminate?.();
}
}));
@@ -65,6 +66,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
logParseWorker.current.onmessage = (event: any) => {
const res = event.data;
console.log('res===', res);
setLogs(res);
};
@@ -73,7 +75,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
logParseWorker.current.terminate();
}
};
}, [setLogs, logParseWorker.current]);
}, []);
const debounceLoading = _.debounce(() => {
setLoading(false);
@@ -88,26 +90,26 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const getLastPage = (data: string) => {
const list = _.split(data.trim(), '\n');
if (!enableScorllLoad) {
return list.join('\n');
}
if (list.length <= pageSize) {
setTotalPage(1);
return data;
}
let result = '';
console.log('getlastPage===', list.length, enableScorllLoad, pageSize);
setLoading(true);
const totalPage = Math.ceil(list.length / pageSize);
setTotalPage(totalPage);
setPage(() => totalPage);
pageRef.current = totalPage;
totalPageRef.current = totalPage;
const lastPage = list.slice(-pageSize).join('\n');
console.log('list.length===', list.length, totalPage, page);
if (!enableScorllLoad) {
result = list.join('\n');
} else if (list.length <= pageSize) {
setTotalPage(1);
result = data;
} else {
const totalPage = Math.ceil(list.length / pageSize);
setTotalPage(totalPage);
setPage(() => totalPage);
pageRef.current = totalPage;
totalPageRef.current = totalPage;
const lastPage = list.slice(-pageSize).join('\n');
result = lastPage;
}
debounceLoading();
return lastPage;
return result;
};
const getCurrentPage = () => {
@@ -116,7 +118,6 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
if (newPage < 1) {
newPage = 1;
}
console.log('currentpage===', newPage);
const start = (newPage - 1) * pageSize;
const end = newPage * pageSize;
const currentPage = list.slice(start, end).join('\n');
@@ -164,6 +165,20 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
});
}, [totalPage, page, pageSize]);
const handleonBackend = useCallback(() => {
const list = _.split(cacheDataRef.current.trim(), '\n');
let newPage = totalPage;
const start = (newPage - 1) * pageSize;
const end = newPage * pageSize;
const nextPage = list.slice(start, end).join('\n');
setPage(() => newPage);
setScrollPos(['bottom', newPage]);
pageRef.current = newPage;
logParseWorker.current.postMessage({
inputStr: nextPage
});
}, [totalPage, page, pageSize]);
const debounceParseData = _.debounce(() => {
if (pageRef.current === totalPageRef.current) {
logParseWorker.current.postMessage({
@@ -175,20 +190,25 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
}, 100);
const updateContent = (inputStr: string) => {
console.log('inputStr===', inputStr);
const data = inputStr.replace(replaceLineRegex, '\n');
if (isClean(data)) {
cacheDataRef.current = data;
} else {
cacheDataRef.current += data;
}
console.log('page===', pageRef.current, totalPageRef.current);
debounceParseData();
if (pageRef.current === totalPageRef.current) {
logParseWorker.current.postMessage({
inputStr: getLastPage(cacheDataRef.current)
});
} else {
getCurrentPage();
}
};
const createChunkConnection = async () => {
cacheDataRef.current = '';
chunkRequedtRef.current?.current?.abort?.();
chunkRequedtRef.current = setChunkFetch({
url,
params: {
@@ -287,6 +307,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
pageSize={pageSize}
onNext={getNextPage}
onPrev={getPrePage}
onBackend={handleonBackend}
></LogsPagination>
</div>
</div>