fix: add a button to last page on logs
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ export default {
|
|||||||
contentPadding: '12px 16px'
|
contentPadding: '12px 16px'
|
||||||
},
|
},
|
||||||
Tooltip: {
|
Tooltip: {
|
||||||
colorBgSpotlight: 'rgba(110,110,110,1)'
|
colorBgSpotlight: '#3e3e3e'
|
||||||
// sizePopupArrow: 0
|
// sizePopupArrow: 0
|
||||||
},
|
},
|
||||||
Slider: {
|
Slider: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||||
import { Progress } from 'antd';
|
import { Progress, ProgressProps } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import ResizeObserver from 'rc-resize-observer';
|
import ResizeObserver from 'rc-resize-observer';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
@@ -21,6 +21,7 @@ interface SingleImageProps {
|
|||||||
autoBgColor?: boolean;
|
autoBgColor?: boolean;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
|
loadingSize?: ProgressProps['size'];
|
||||||
progressType?: 'line' | 'circle' | 'dashboard';
|
progressType?: 'line' | 'circle' | 'dashboard';
|
||||||
progressColor?: string;
|
progressColor?: string;
|
||||||
progressWidth?: number;
|
progressWidth?: number;
|
||||||
@@ -42,10 +43,8 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
|
|||||||
dataUrl,
|
dataUrl,
|
||||||
style,
|
style,
|
||||||
autoBgColor,
|
autoBgColor,
|
||||||
progressColor = 'var(--ant-color-primary)',
|
|
||||||
progressWidth = 2,
|
|
||||||
preview = true,
|
preview = true,
|
||||||
progressType = 'dashboard'
|
loadingSize = 'default'
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const imgWrapper = React.useRef<HTMLSpanElement>(null);
|
const imgWrapper = React.useRef<HTMLSpanElement>(null);
|
||||||
@@ -137,6 +136,7 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
|
|||||||
<Progress
|
<Progress
|
||||||
percent={progress}
|
percent={progress}
|
||||||
type="dashboard"
|
type="dashboard"
|
||||||
|
size={loadingSize}
|
||||||
steps={{ count: 50, gap: 2 }}
|
steps={{ count: 50, gap: 2 }}
|
||||||
format={() => (
|
format={() => (
|
||||||
<span className="font-size-20">{progress}%</span>
|
<span className="font-size-20">{progress}%</span>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
onSave,
|
onSave,
|
||||||
uploadButton
|
uploadButton
|
||||||
}) => {
|
}) => {
|
||||||
|
const MIN_SCALE = 0.4;
|
||||||
|
const MAX_SCALE = 10;
|
||||||
|
const zoomFactor = 1.1;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
|
const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
@@ -47,25 +50,20 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
const autoScale = useRef<number>(1);
|
const autoScale = useRef<number>(1);
|
||||||
const cursorRef = useRef<HTMLDivElement>(null);
|
const cursorRef = useRef<HTMLDivElement>(null);
|
||||||
const [imgLoaded, setImgLoaded] = useState(false);
|
const [imgLoaded, setImgLoaded] = useState(false);
|
||||||
|
const offsetRef = useRef({ x: 0, y: 0 });
|
||||||
let scale = 1;
|
const originPosition = useRef({ x: 0, y: 0 });
|
||||||
let offsetX = 0;
|
|
||||||
let offsetY = 0;
|
|
||||||
|
|
||||||
const MIN_SCALE = 0.5;
|
|
||||||
const MAX_SCALE = 5;
|
|
||||||
|
|
||||||
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
|
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
const overlayCanvas = overlayCanvasRef.current!;
|
const overlayCanvas = overlayCanvasRef.current!;
|
||||||
const rect = overlayCanvas.getBoundingClientRect();
|
const rect = overlayCanvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
// 获取鼠标在画布上的原始坐标
|
||||||
const x = event.clientX - rect.left;
|
const x = event.clientX - rect.left;
|
||||||
const y = event.clientY - rect.top;
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
const transformedX = x - overlayCanvas.width / 2;
|
// 考虑缩放比例和偏移量
|
||||||
const transformedY = y - overlayCanvas.height / 2;
|
const transformedX = (x - offsetRef.current.x) / autoScale.current;
|
||||||
|
const transformedY = (y - offsetRef.current.y) / autoScale.current;
|
||||||
console.log('Mouse Coordinates (Transformed):', transformedX, transformedY);
|
|
||||||
|
|
||||||
return { x: transformedX, y: transformedY };
|
return { x: transformedX, y: transformedY };
|
||||||
};
|
};
|
||||||
@@ -113,6 +111,10 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
|
||||||
ctx!.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);
|
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]);
|
}, [canvasRef.current, overlayCanvasRef.current]);
|
||||||
|
|
||||||
const scaleCanvasSize = useCallback(() => {
|
const scaleCanvasSize = useCallback(() => {
|
||||||
@@ -439,8 +441,8 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
canvas!.width = img.width * scale;
|
canvas!.width = img.width;
|
||||||
canvas!.height = img.height * scale;
|
canvas!.height = img.height;
|
||||||
|
|
||||||
autoScale.current = scale / autoScale.current;
|
autoScale.current = scale / autoScale.current;
|
||||||
|
|
||||||
@@ -457,6 +459,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
canvas!.width,
|
canvas!.width,
|
||||||
canvas!.height
|
canvas!.height
|
||||||
);
|
);
|
||||||
|
canvas!.style.transform = `scale(${scale})`;
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -502,61 +505,102 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
|
|||||||
const y = event.clientY - rect.top;
|
const y = event.clientY - rect.top;
|
||||||
|
|
||||||
// 考虑缩放比例和偏移量
|
// 考虑缩放比例和偏移量
|
||||||
const transformedX = (x - offsetX) / scale;
|
const transformedX = (x - offsetRef.current.x) / autoScale.current;
|
||||||
const transformedY = (y - offsetY) / scale;
|
const transformedY = (y - offsetRef.current.y) / autoScale.current;
|
||||||
|
|
||||||
return { x: transformedX, y: transformedY };
|
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) => {
|
const handleOnWheel = (event: WheelEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9;
|
updateCavasTransform(event);
|
||||||
const newScale = Math.min(
|
|
||||||
MAX_SCALE,
|
|
||||||
Math.max(MIN_SCALE, scale * zoomFactor)
|
|
||||||
);
|
|
||||||
|
|
||||||
const rect = canvasRef.current!.getBoundingClientRect();
|
overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
|
||||||
const mouseX = event.clientX - rect.left;
|
canvasRef.current!.style.transform = `scale(${autoScale.current})`;
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeImage();
|
initializeImage();
|
||||||
}, [initializeImage]);
|
}, [initializeImage]);
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
// if (!container) return;
|
if (!container) return;
|
||||||
// if (container) {
|
if (container) {
|
||||||
// resizeObserver.current = new ResizeObserver(
|
resizeObserver.current = new ResizeObserver(
|
||||||
// _.throttle(handleResize, 100)
|
_.throttle(handleResize, 100)
|
||||||
// );
|
);
|
||||||
// resizeObserver.current.observe(container);
|
resizeObserver.current.observe(container);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return () => {
|
return () => {
|
||||||
// resizeObserver.current?.disconnect();
|
resizeObserver.current?.disconnect();
|
||||||
// };
|
};
|
||||||
// }, [handleResize, containerRef.current]);
|
}, [handleResize, containerRef.current]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
createOffscreenCanvas();
|
createOffscreenCanvas();
|
||||||
|
|||||||
@@ -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 { useIntl } from '@umijs/max';
|
||||||
import { Button, Tooltip } from 'antd';
|
import { Button, Tooltip } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -10,10 +14,11 @@ interface LogsPaginationProps {
|
|||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
onPrev?: () => void;
|
onPrev?: () => void;
|
||||||
onNext?: () => void;
|
onNext?: () => void;
|
||||||
|
onBackend?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
|
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 intl = useIntl();
|
||||||
|
|
||||||
const handleOnPrev = () => {
|
const handleOnPrev = () => {
|
||||||
@@ -27,6 +32,7 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="pagination">
|
<div className="pagination">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
title={intl.formatMessage(
|
title={intl.formatMessage(
|
||||||
{ id: 'models.logs.pagination.prev' },
|
{ id: 'models.logs.pagination.prev' },
|
||||||
{ lines: pageSize }
|
{ lines: pageSize }
|
||||||
@@ -46,21 +52,37 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
|
|||||||
<span className="total">{total}</span>
|
<span className="total">{total}</span>
|
||||||
</span>
|
</span>
|
||||||
{page < total && (
|
{page < total && (
|
||||||
<Tooltip
|
<>
|
||||||
title={intl.formatMessage(
|
<Tooltip
|
||||||
{ id: 'models.logs.pagination.next' },
|
placement="left"
|
||||||
{ lines: pageSize }
|
title={intl.formatMessage(
|
||||||
)}
|
{ id: 'models.logs.pagination.next' },
|
||||||
>
|
{ lines: pageSize }
|
||||||
<Button
|
)}
|
||||||
onClick={handleOnNext}
|
|
||||||
type="text"
|
|
||||||
shape="circle"
|
|
||||||
style={{ color: 'rgba(255,255,255,.7)' }}
|
|
||||||
>
|
>
|
||||||
<DownOutlined />
|
<Button
|
||||||
</Button>
|
onClick={handleOnNext}
|
||||||
</Tooltip>
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -150,5 +150,10 @@ self.onmessage = function (event) {
|
|||||||
content: item.content,
|
content: item.content,
|
||||||
uid: item.uid
|
uid: item.uid
|
||||||
}));
|
}));
|
||||||
|
console.log('result===', result);
|
||||||
self.postMessage(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;
|
background-color: rgba(71, 71, 71, 100%) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
background-color: rgba(71, 71, 71, 50%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.pages {
|
.pages {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
abort() {
|
abort() {
|
||||||
chunkRequedtRef.current?.current?.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) => {
|
logParseWorker.current.onmessage = (event: any) => {
|
||||||
const res = event.data;
|
const res = event.data;
|
||||||
|
console.log('res===', res);
|
||||||
setLogs(res);
|
setLogs(res);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
logParseWorker.current.terminate();
|
logParseWorker.current.terminate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [setLogs, logParseWorker.current]);
|
}, []);
|
||||||
|
|
||||||
const debounceLoading = _.debounce(() => {
|
const debounceLoading = _.debounce(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -88,26 +90,26 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
const getLastPage = (data: string) => {
|
const getLastPage = (data: string) => {
|
||||||
const list = _.split(data.trim(), '\n');
|
const list = _.split(data.trim(), '\n');
|
||||||
|
let result = '';
|
||||||
if (!enableScorllLoad) {
|
console.log('getlastPage===', list.length, enableScorllLoad, pageSize);
|
||||||
return list.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.length <= pageSize) {
|
|
||||||
setTotalPage(1);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const totalPage = Math.ceil(list.length / pageSize);
|
if (!enableScorllLoad) {
|
||||||
setTotalPage(totalPage);
|
result = list.join('\n');
|
||||||
setPage(() => totalPage);
|
} else if (list.length <= pageSize) {
|
||||||
pageRef.current = totalPage;
|
setTotalPage(1);
|
||||||
totalPageRef.current = totalPage;
|
result = data;
|
||||||
const lastPage = list.slice(-pageSize).join('\n');
|
} else {
|
||||||
console.log('list.length===', list.length, totalPage, page);
|
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();
|
debounceLoading();
|
||||||
return lastPage;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentPage = () => {
|
const getCurrentPage = () => {
|
||||||
@@ -116,7 +118,6 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
if (newPage < 1) {
|
if (newPage < 1) {
|
||||||
newPage = 1;
|
newPage = 1;
|
||||||
}
|
}
|
||||||
console.log('currentpage===', newPage);
|
|
||||||
const start = (newPage - 1) * pageSize;
|
const start = (newPage - 1) * pageSize;
|
||||||
const end = newPage * pageSize;
|
const end = newPage * pageSize;
|
||||||
const currentPage = list.slice(start, end).join('\n');
|
const currentPage = list.slice(start, end).join('\n');
|
||||||
@@ -164,6 +165,20 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
});
|
});
|
||||||
}, [totalPage, page, pageSize]);
|
}, [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(() => {
|
const debounceParseData = _.debounce(() => {
|
||||||
if (pageRef.current === totalPageRef.current) {
|
if (pageRef.current === totalPageRef.current) {
|
||||||
logParseWorker.current.postMessage({
|
logParseWorker.current.postMessage({
|
||||||
@@ -175,20 +190,25 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
const updateContent = (inputStr: string) => {
|
const updateContent = (inputStr: string) => {
|
||||||
console.log('inputStr===', inputStr);
|
|
||||||
const data = inputStr.replace(replaceLineRegex, '\n');
|
const data = inputStr.replace(replaceLineRegex, '\n');
|
||||||
if (isClean(data)) {
|
if (isClean(data)) {
|
||||||
cacheDataRef.current = data;
|
cacheDataRef.current = data;
|
||||||
} else {
|
} else {
|
||||||
cacheDataRef.current += data;
|
cacheDataRef.current += data;
|
||||||
}
|
}
|
||||||
console.log('page===', pageRef.current, totalPageRef.current);
|
if (pageRef.current === totalPageRef.current) {
|
||||||
debounceParseData();
|
logParseWorker.current.postMessage({
|
||||||
|
inputStr: getLastPage(cacheDataRef.current)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
getCurrentPage();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChunkConnection = async () => {
|
const createChunkConnection = async () => {
|
||||||
cacheDataRef.current = '';
|
cacheDataRef.current = '';
|
||||||
chunkRequedtRef.current?.current?.abort?.();
|
chunkRequedtRef.current?.current?.abort?.();
|
||||||
|
|
||||||
chunkRequedtRef.current = setChunkFetch({
|
chunkRequedtRef.current = setChunkFetch({
|
||||||
url,
|
url,
|
||||||
params: {
|
params: {
|
||||||
@@ -287,6 +307,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
|
|||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
onNext={getNextPage}
|
onNext={getNextPage}
|
||||||
onPrev={getPrePage}
|
onPrev={getPrePage}
|
||||||
|
onBackend={handleonBackend}
|
||||||
></LogsPagination>
|
></LogsPagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export default {
|
|||||||
'More {backend} parameter details',
|
'More {backend} parameter details',
|
||||||
'models.logs.pagination.prev': 'Previous {lines} Lines',
|
'models.logs.pagination.prev': 'Previous {lines} Lines',
|
||||||
'models.logs.pagination.next': 'Next {lines} Lines',
|
'models.logs.pagination.next': 'Next {lines} Lines',
|
||||||
|
'models.logs.pagination.last': 'Last Page',
|
||||||
'models.form.localPath': 'Local Path',
|
'models.form.localPath': 'Local Path',
|
||||||
'models.form.filePath': 'Model Path',
|
'models.form.filePath': 'Model Path',
|
||||||
'models.form.backendVersion': 'Backend Version',
|
'models.form.backendVersion': 'Backend Version',
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default {
|
|||||||
'Upload an audio file or start recording',
|
'Upload an audio file or start recording',
|
||||||
'playground.audio.enablemic':
|
'playground.audio.enablemic':
|
||||||
"Enable microphone access in your browser's settings.",
|
"Enable microphone access in your browser's settings.",
|
||||||
'playground.audio.enablemic.doc': 'Refer to',
|
'playground.audio.enablemic.doc': 'Refer to this link',
|
||||||
'playground.audio.startrecord': 'Start Recording',
|
'playground.audio.startrecord': 'Start Recording',
|
||||||
'playground.audio.stoprecord': 'Stop Recording',
|
'playground.audio.stoprecord': 'Stop Recording',
|
||||||
'playground.audio.generating.tips': 'Generated text will appear here.',
|
'playground.audio.generating.tips': 'Generated text will appear here.',
|
||||||
|
|||||||
@@ -55,6 +55,5 @@ export default {
|
|||||||
'Select a label to generate the command and copy it using the copy button.',
|
'Select a label to generate the command and copy it using the copy button.',
|
||||||
'resources.worker.script.install': 'Script Installation',
|
'resources.worker.script.install': 'Script Installation',
|
||||||
'resources.worker.container.install': 'Container Installation(Linux Only)',
|
'resources.worker.container.install': 'Container Installation(Linux Only)',
|
||||||
'resources.worker.cann.tips':
|
'resources.worker.cann.tips': `Set <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES</span> to the required GPU indices. For GPU0 to GPU3, use <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0,1,2,3</span> or <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0-3</span>.`
|
||||||
'Set <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES</span> to 0 for a single GPU or to the index range (e.g., 0-7 for 8 GPUs) for multiple GPUs.'
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export default {
|
|||||||
'models.form.backend_parameters.vllm.tips': '更多 {backend} 参数说明查看',
|
'models.form.backend_parameters.vllm.tips': '更多 {backend} 参数说明查看',
|
||||||
'models.logs.pagination.prev': '上一 {lines} 行',
|
'models.logs.pagination.prev': '上一 {lines} 行',
|
||||||
'models.logs.pagination.next': '下一 {lines} 行',
|
'models.logs.pagination.next': '下一 {lines} 行',
|
||||||
|
'models.logs.pagination.last': '最后一页',
|
||||||
'models.form.localPath': '本地路径',
|
'models.form.localPath': '本地路径',
|
||||||
'models.form.filePath': '模型路径',
|
'models.form.filePath': '模型路径',
|
||||||
'models.form.backendVersion': '后端版本',
|
'models.form.backendVersion': '后端版本',
|
||||||
|
|||||||
@@ -54,5 +54,5 @@ export default {
|
|||||||
'resources.worker.script.install': '脚本安装',
|
'resources.worker.script.install': '脚本安装',
|
||||||
'resources.worker.container.install': '容器安装(仅支持 Linux)',
|
'resources.worker.container.install': '容器安装(仅支持 Linux)',
|
||||||
'resources.worker.cann.tips':
|
'resources.worker.cann.tips':
|
||||||
'若 GPU 数量为 1,则设 ASCEND_VISIBLE_DEVICES=0;若大于 1,则设为索引范围(如 8 张为 0-7)'
|
'按需要挂载的 GPU index 设置 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES</span>,如需挂载 GPU0 - GPU3,则设为 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES=0,1,2,3</span> 或 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES=0-3</span>'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const ActiveTable = () => {
|
|||||||
render: (text: any, record: any) => {
|
render: (text: any, record: any) => {
|
||||||
return (
|
return (
|
||||||
<AutoTooltip ghost>
|
<AutoTooltip ghost>
|
||||||
<span>{text}</span>
|
<span>{text || 'N/A'}</span>
|
||||||
</AutoTooltip>
|
</AutoTooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export async function queryModelScopeModels(
|
|||||||
config?: any
|
config?: any
|
||||||
) {
|
) {
|
||||||
const tagsCriterion = params.tags?.map((tag: string) => {
|
const tagsCriterion = params.tags?.map((tag: string) => {
|
||||||
return { category: 'tags', predicate: 'contains', values: [tag] };
|
return { category: 'libraries', predicate: 'contains', values: [tag] };
|
||||||
});
|
});
|
||||||
const tasksCriterion = params.tasks?.map((task: string) => {
|
const tasksCriterion = params.tasks?.map((task: string) => {
|
||||||
return { category: 'tasks', predicate: 'contains', values: [task] };
|
return { category: 'tasks', predicate: 'contains', values: [task] };
|
||||||
|
|||||||
@@ -303,7 +303,6 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
{ backend: backendParamsTips.backend || '' }
|
{ backend: backendParamsTips.backend || '' }
|
||||||
)}{' '}
|
)}{' '}
|
||||||
<Typography.Link
|
<Typography.Link
|
||||||
style={{ color: 'var(--ant-blue-4)' }}
|
|
||||||
href={backendParamsTips.link}
|
href={backendParamsTips.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
|||||||
return [...shardFileListResult, ...newGeneralFileList];
|
return [...shardFileListResult, ...newGeneralFileList];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const hfFileFilter = (file: any) => {
|
||||||
|
return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf');
|
||||||
|
};
|
||||||
|
|
||||||
// hugging face files
|
// hugging face files
|
||||||
const getHuggingfaceFiles = async () => {
|
const getHuggingfaceFiles = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -139,7 +143,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const list = _.filter(fileList, (file: any) => {
|
const list = _.filter(fileList, (file: any) => {
|
||||||
return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf');
|
return hfFileFilter(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
@@ -148,6 +152,10 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const modelscopeFileFilter = (file: any) => {
|
||||||
|
return filterRegGGUF.test(file.Path) && file.Type === 'blob';
|
||||||
|
};
|
||||||
|
|
||||||
// modelscope files
|
// modelscope files
|
||||||
const getModelScopeFiles = async () => {
|
const getModelScopeFiles = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -161,7 +169,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
const fileList = _.filter(_.get(data, ['Data', 'Files']), (file: any) => {
|
const fileList = _.filter(_.get(data, ['Data', 'Files']), (file: any) => {
|
||||||
return filterRegGGUF.test(file.Path) && file.Type === 'blob';
|
return modelscopeFileFilter(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
const list = _.map(fileList, (item: any) => {
|
const list = _.map(fileList, (item: any) => {
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
url: `${MODELS_API}/${props.modelId}/instances`,
|
url: `${MODELS_API}/${props.modelId}/instances`,
|
||||||
handler: updateHandler
|
handler: updateHandler
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
logsViewerRef.current?.abort();
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
requestRef.current?.current?.cancel?.();
|
requestRef.current?.current?.cancel?.();
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
|
|||||||
href={`${externalRefer.audioPermission}`}
|
href={`${externalRefer.audioPermission}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
style={{
|
style={{
|
||||||
color: 'var(--ant-blue-5)'
|
paddingInline: 0
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{intl.formatMessage({ id: 'playground.audio.enablemic.doc' })}
|
{intl.formatMessage({ id: 'playground.audio.enablemic.doc' })}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form, InputNumber, Slider, Tooltip } from 'antd';
|
import { Form, InputNumber, Slider, Tooltip } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { memo, useCallback, useEffect, useId } from 'react';
|
import { memo, useCallback, useEffect, useId, useState } from 'react';
|
||||||
import CustomLabelStyles from '../style/custom-label.less';
|
import CustomLabelStyles from '../style/custom-label.less';
|
||||||
|
|
||||||
type ParamsSettingsFormProps = {
|
type ParamsSettingsFormProps = {
|
||||||
@@ -29,12 +29,13 @@ type ParamsSettingsProps = {
|
|||||||
globalParams?: ParamsSettingsFormProps;
|
globalParams?: ParamsSettingsFormProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const METAKEYS: Record<string, string> = {
|
const METAKEYS: Record<string, any> = {
|
||||||
seed: 'seed',
|
seed: 'seed',
|
||||||
stop: 'stop',
|
stop: 'stop',
|
||||||
temperature: 'temperature',
|
temperature: 'temperature',
|
||||||
top_p: 'top_p',
|
top_p: 'top_p',
|
||||||
max_tokens: 'n_ctx'
|
n_slot_ctx: 'max_tokens',
|
||||||
|
max_model_len: 'max_tokens'
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
||||||
@@ -56,6 +57,8 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
};
|
};
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const formId = useId();
|
const formId = useId();
|
||||||
|
const [metaData, setMetaData] = useState<Record<string, any>>({});
|
||||||
|
const [firstLoad, setFirstLoad] = useState(true);
|
||||||
|
|
||||||
const handleOnFinish = (values: any) => {
|
const handleOnFinish = (values: any) => {
|
||||||
console.log('handleOnFinish', values);
|
console.log('handleOnFinish', values);
|
||||||
@@ -97,18 +100,16 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
const handleModelChange = (val: string) => {
|
const handleModelChange = (val: string) => {
|
||||||
const model = _.find(modelList, { value: val });
|
const model = _.find(modelList, { value: val });
|
||||||
const modelMeta = model?.meta || {};
|
const modelMeta = model?.meta || {};
|
||||||
const keys = Object.keys(METAKEYS).map((k: string) => {
|
const modelMetaValue = _.pick(modelMeta, _.keys(METAKEYS));
|
||||||
return METAKEYS[k];
|
const obj = Object.entries(METAKEYS).reduce((acc: any, [key, value]) => {
|
||||||
});
|
const val = modelMetaValue[key];
|
||||||
const modelMetaKeys = _.pick(modelMeta, keys);
|
if (val && _.hasIn(modelMetaValue, key)) {
|
||||||
const obj = _.reduce(
|
acc[value] = val;
|
||||||
METAKEYS,
|
}
|
||||||
(result: any, value: any, key: string) => {
|
return acc;
|
||||||
result[key] = modelMetaKeys[value];
|
}, {});
|
||||||
return result;
|
form.setFieldsValue(obj);
|
||||||
},
|
setMetaData(obj);
|
||||||
{}
|
|
||||||
);
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,11 +128,14 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
...mergeData,
|
...mergeData,
|
||||||
model: model
|
model: model
|
||||||
});
|
});
|
||||||
|
setFirstLoad(false);
|
||||||
}, [modelList, showModelSelector, selectedModel]);
|
}, [modelList, showModelSelector, selectedModel]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldsValue(globalParams);
|
if (!firstLoad) {
|
||||||
}, [globalParams]);
|
form.setFieldsValue(globalParams);
|
||||||
|
}
|
||||||
|
}, [globalParams, firstLoad]);
|
||||||
|
|
||||||
const renderLabel = (args: {
|
const renderLabel = (args: {
|
||||||
field: string;
|
field: string;
|
||||||
@@ -196,7 +200,11 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealSelect showSearch={true} options={modelList}></SealSelect>
|
<SealSelect
|
||||||
|
showSearch={true}
|
||||||
|
options={modelList}
|
||||||
|
onChange={handleModelChange}
|
||||||
|
></SealSelect>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -246,7 +254,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
|
|||||||
>
|
>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={2048}
|
defaultValue={2048}
|
||||||
max={16 * 1024}
|
max={metaData.max_tokens || 16 * 1024}
|
||||||
step={1}
|
step={1}
|
||||||
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
|
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
|
||||||
tooltip={{ open: false }}
|
tooltip={{ open: false }}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
</ul>
|
</ul>
|
||||||
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
|
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
|
||||||
<HighlightCode
|
<HighlightCode
|
||||||
code={addWorkerGuide.mac.getToken}
|
code={addWorkerGuide.container.getToken}
|
||||||
theme="dark"
|
theme="dark"
|
||||||
></HighlightCode>
|
></HighlightCode>
|
||||||
<h3>
|
<h3>
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ export const addWorkerGuide: Record<string, any> = {
|
|||||||
registerWorker(params: { server: string; tag: string; token: string }) {
|
registerWorker(params: { server: string; tag: string; token: string }) {
|
||||||
return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
getToken:
|
||||||
|
'docker run -it ${gpustack_container_id} cat /var/lib/gpustack/token'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user