mirror of
https://github.com/XZB-1248/Spark
synced 2025-09-27 20:42:18 +08:00
optimize: reindent front-end with tab
This commit is contained in:
@@ -17,195 +17,195 @@ let bytes = 0;
|
|||||||
let ticks = 0;
|
let ticks = 0;
|
||||||
let title = i18n.t('DESKTOP.TITLE');
|
let title = i18n.t('DESKTOP.TITLE');
|
||||||
function ScreenModal(props) {
|
function ScreenModal(props) {
|
||||||
const [bandwidth, setBandwidth] = useState(0);
|
const [bandwidth, setBandwidth] = useState(0);
|
||||||
const [fps, setFps] = useState(0);
|
const [fps, setFps] = useState(0);
|
||||||
const canvasRef = useCallback((e) => {
|
const canvasRef = useCallback((e) => {
|
||||||
if (e && props.visible && !conn) {
|
if (e && props.visible && !conn) {
|
||||||
canvas = e;
|
canvas = e;
|
||||||
initCanvas(canvas);
|
initCanvas(canvas);
|
||||||
construct(canvas);
|
construct(canvas);
|
||||||
}
|
}
|
||||||
}, [props]);
|
}, [props]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.visible) {
|
if (props.visible) {
|
||||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||||
} else {
|
} else {
|
||||||
if (ws && conn) {
|
if (ws && conn) {
|
||||||
clearInterval(ticker);
|
clearInterval(ticker);
|
||||||
ws.close();
|
ws.close();
|
||||||
conn = false;
|
conn = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.visible, props.device]);
|
}, [props.visible, props.device]);
|
||||||
|
|
||||||
function initCanvas() {
|
function initCanvas() {
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
ctx = canvas.getContext('2d', {alpha: false});
|
ctx = canvas.getContext('2d', {alpha: false});
|
||||||
}
|
}
|
||||||
function construct() {
|
function construct() {
|
||||||
if (ctx !== null) {
|
if (ctx !== null) {
|
||||||
if (ws !== null && conn) {
|
if (ws !== null && conn) {
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
ws = new WebSocket(getBaseURL(true, `api/device/desktop?device=${props.device.id}&secret=${secret}`));
|
ws = new WebSocket(getBaseURL(true, `api/device/desktop?device=${props.device.id}&secret=${secret}`));
|
||||||
ws.binaryType = 'arraybuffer';
|
ws.binaryType = 'arraybuffer';
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
conn = true;
|
conn = true;
|
||||||
}
|
}
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
parseBlocks(e.data, canvas, ctx);
|
parseBlocks(e.data, canvas, ctx);
|
||||||
};
|
};
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn = false;
|
conn = false;
|
||||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ws.onerror = (e) => {
|
ws.onerror = (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn = false;
|
conn = false;
|
||||||
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
message.warn(i18n.t('COMMON.DISCONNECTED'));
|
||||||
} else {
|
} else {
|
||||||
message.warn(i18n.t('COMMON.CONNECTION_FAILED'));
|
message.warn(i18n.t('COMMON.CONNECTION_FAILED'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
clearInterval(ticker);
|
clearInterval(ticker);
|
||||||
ticker = setInterval(() => {
|
ticker = setInterval(() => {
|
||||||
setBandwidth(bytes);
|
setBandwidth(bytes);
|
||||||
setFps(frames);
|
setFps(frames);
|
||||||
bytes = 0;
|
bytes = 0;
|
||||||
frames = 0;
|
frames = 0;
|
||||||
ticks++;
|
ticks++;
|
||||||
if (ticks > 10 && conn) {
|
if (ticks > 10 && conn) {
|
||||||
ticks = 0;
|
ticks = 0;
|
||||||
ws.send(encrypt({
|
ws.send(encrypt({
|
||||||
act: 'DESKTOP_PING'
|
act: 'DESKTOP_PING'
|
||||||
}, secret));
|
}, secret));
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function fullScreen() {
|
function fullScreen() {
|
||||||
try {
|
try {
|
||||||
canvas.requestFullscreen();
|
canvas.requestFullscreen();
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
try {
|
||||||
canvas.webkitRequestFullscreen();
|
canvas.webkitRequestFullscreen();
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
try {
|
||||||
canvas.mozRequestFullScreen();
|
canvas.mozRequestFullScreen();
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
try {
|
||||||
canvas.msRequestFullscreen();
|
canvas.msRequestFullscreen();
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
function refresh() {
|
function refresh() {
|
||||||
if (canvas && props.visible) {
|
if (canvas && props.visible) {
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
canvas.width = 1920;
|
canvas.width = 1920;
|
||||||
canvas.height = 1080;
|
canvas.height = 1080;
|
||||||
initCanvas(canvas);
|
initCanvas(canvas);
|
||||||
construct(canvas);
|
construct(canvas);
|
||||||
} else {
|
} else {
|
||||||
ws.send(encrypt({
|
ws.send(encrypt({
|
||||||
act: 'DESKTOP_SHOT'
|
act: 'DESKTOP_SHOT'
|
||||||
}, secret));
|
}, secret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBlocks(ab, canvas, canvasCtx) {
|
function parseBlocks(ab, canvas, canvasCtx) {
|
||||||
ab = ab.slice(5);
|
ab = ab.slice(5);
|
||||||
let dv = new DataView(ab);
|
let dv = new DataView(ab);
|
||||||
let op = dv.getUint8(0);
|
let op = dv.getUint8(0);
|
||||||
if (op === 3) {
|
if (op === 3) {
|
||||||
handleJSON(ab.slice(1));
|
handleJSON(ab.slice(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (op === 2) {
|
if (op === 2) {
|
||||||
let width = dv.getUint16(1, false);
|
let width = dv.getUint16(1, false);
|
||||||
let height = dv.getUint16(3, false);
|
let height = dv.getUint16(3, false);
|
||||||
if (width === 0 || height === 0) return;
|
if (width === 0 || height === 0) return;
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (op === 0) frames++;
|
if (op === 0) frames++;
|
||||||
bytes += ab.byteLength;
|
bytes += ab.byteLength;
|
||||||
let offset = 1;
|
let offset = 1;
|
||||||
while (offset < ab.byteLength) {
|
while (offset < ab.byteLength) {
|
||||||
let it = dv.getUint16(offset + 0, false); // image type
|
let it = dv.getUint16(offset + 0, false); // image type
|
||||||
let il = dv.getUint16(offset + 2, false); // image length
|
let il = dv.getUint16(offset + 2, false); // image length
|
||||||
let dx = dv.getUint16(offset + 4, false); // image block x
|
let dx = dv.getUint16(offset + 4, false); // image block x
|
||||||
let dy = dv.getUint16(offset + 6, false); // image block y
|
let dy = dv.getUint16(offset + 6, false); // image block y
|
||||||
let bw = dv.getUint16(offset + 8, false); // image block width
|
let bw = dv.getUint16(offset + 8, false); // image block width
|
||||||
let bh = dv.getUint16(offset + 10, false); // image block height
|
let bh = dv.getUint16(offset + 10, false); // image block height
|
||||||
offset += 12;
|
offset += 12;
|
||||||
updateImage(ab.slice(offset, offset + il), it, dx, dy, bw, bh, canvasCtx);
|
updateImage(ab.slice(offset, offset + il), it, dx, dy, bw, bh, canvasCtx);
|
||||||
offset += il;
|
offset += il;
|
||||||
}
|
}
|
||||||
dv = null;
|
dv = null;
|
||||||
}
|
}
|
||||||
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
||||||
if (it === 0) {
|
if (it === 0) {
|
||||||
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
||||||
} else {
|
} else {
|
||||||
createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
|
createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
|
||||||
.then((ib) => {
|
.then((ib) => {
|
||||||
canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
|
canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleJSON(ab) {
|
function handleJSON(ab) {
|
||||||
let data = decrypt(ab, secret);
|
let data = decrypt(ab, secret);
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
if (data?.act === 'WARN') {
|
if (data?.act === 'WARN') {
|
||||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data?.act === 'QUIT') {
|
if (data?.act === 'QUIT') {
|
||||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||||
conn = false;
|
conn = false;
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableModal
|
<DraggableModal
|
||||||
draggable={true}
|
draggable={true}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
destroyOnClose={true}
|
destroyOnClose={true}
|
||||||
modalTitle={`${title} ${formatSize(bandwidth)}/s FPS: ${fps}`}
|
modalTitle={`${title} ${formatSize(bandwidth)}/s FPS: ${fps}`}
|
||||||
footer={null}
|
footer={null}
|
||||||
height={480}
|
height={480}
|
||||||
width={940}
|
width={940}
|
||||||
bodyStyle={{
|
bodyStyle={{
|
||||||
padding: 0
|
padding: 0
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
id='painter'
|
id='painter'
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
style={{width: '100%', height: '100%'}}
|
style={{width: '100%', height: '100%'}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
style={{right:'59px'}}
|
style={{right:'59px'}}
|
||||||
className='header-button'
|
className='header-button'
|
||||||
icon={<FullscreenOutlined />}
|
icon={<FullscreenOutlined />}
|
||||||
onClick={fullScreen}
|
onClick={fullScreen}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
style={{right:'115px'}}
|
style={{right:'115px'}}
|
||||||
className='header-button'
|
className='header-button'
|
||||||
icon={<ReloadOutlined />}
|
icon={<ReloadOutlined />}
|
||||||
onClick={refresh}
|
onClick={refresh}
|
||||||
/>
|
/>
|
||||||
</DraggableModal>
|
</DraggableModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ScreenModal;
|
export default ScreenModal;
|
@@ -1,45 +1,45 @@
|
|||||||
a {
|
a {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-row {
|
.file-row {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-body {
|
.ant-table-body {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-breadcrumb {
|
.ant-breadcrumb {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-table-list-toolbar {
|
.ant-pro-table-list-toolbar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-table-list-toolbar::-webkit-scrollbar {
|
.ant-pro-table-list-toolbar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-progress-square > .ant-progress-outer > .ant-progress-inner {
|
.upload-progress-square > .ant-progress-outer > .ant-progress-inner {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-modal, .editor-modal > .ant-modal-content {
|
.editor-modal, .editor-modal > .ant-modal-content {
|
||||||
top: 0;
|
top: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-modal > .ant-modal-content > .ant-modal-body {
|
.editor-modal > .ant-modal-content > .ant-modal-body {
|
||||||
height: calc(100% - 110px);
|
height: calc(100% - 110px);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -5,90 +5,90 @@ import prebuilt from '../config/prebuilt.json';
|
|||||||
import i18n from "../locale/locale";
|
import i18n from "../locale/locale";
|
||||||
|
|
||||||
function Generate(props) {
|
function Generate(props) {
|
||||||
const initValues = getInitValues();
|
const initValues = getInitValues();
|
||||||
|
|
||||||
async function onFinish(form) {
|
async function onFinish(form) {
|
||||||
if (form?.ArchOS?.length === 2) {
|
if (form?.ArchOS?.length === 2) {
|
||||||
form.os = form.ArchOS[0];
|
form.os = form.ArchOS[0];
|
||||||
form.arch = form.ArchOS[1];
|
form.arch = form.ArchOS[1];
|
||||||
delete form.ArchOS;
|
delete form.ArchOS;
|
||||||
}
|
}
|
||||||
form.secure = location.protocol === 'https:' ? 'true' : 'false';
|
form.secure = location.protocol === 'https:' ? 'true' : 'false';
|
||||||
let basePath = location.origin + location.pathname + 'api/client/';
|
let basePath = location.origin + location.pathname + 'api/client/';
|
||||||
request(basePath + 'check', form).then(res => {
|
request(basePath + 'check', form).then(res => {
|
||||||
if (res.data.code === 0) {
|
if (res.data.code === 0) {
|
||||||
post(basePath += 'generate', form);
|
post(basePath += 'generate', form);
|
||||||
}
|
}
|
||||||
}).catch();
|
}).catch();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInitValues() {
|
function getInitValues() {
|
||||||
let initValues = {
|
let initValues = {
|
||||||
host: location.hostname,
|
host: location.hostname,
|
||||||
port: location.port,
|
port: location.port,
|
||||||
path: location.pathname,
|
path: location.pathname,
|
||||||
ArchOS: ['windows', 'amd64']
|
ArchOS: ['windows', 'amd64']
|
||||||
};
|
};
|
||||||
if (String(location.port).length === 0) {
|
if (String(location.port).length === 0) {
|
||||||
initValues.port = location.protocol === 'https:' ? 443 : 80;
|
initValues.port = location.protocol === 'https:' ? 443 : 80;
|
||||||
}
|
}
|
||||||
return initValues;
|
return initValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
modalProps={{
|
modalProps={{
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
maskClosable: false,
|
maskClosable: false,
|
||||||
}}
|
}}
|
||||||
initialValues={initValues}
|
initialValues={initValues}
|
||||||
onFinish={onFinish}
|
onFinish={onFinish}
|
||||||
submitter={{
|
submitter={{
|
||||||
render: (_, elems) => elems.pop()
|
render: (_, elems) => elems.pop()
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ProFormGroup>
|
<ProFormGroup>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
width="md"
|
width="md"
|
||||||
name="host"
|
name="host"
|
||||||
label={i18n.t('GENERATOR.HOST')}
|
label={i18n.t('GENERATOR.HOST')}
|
||||||
rules={[{
|
rules={[{
|
||||||
required: true
|
required: true
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
width="md"
|
width="md"
|
||||||
name="port"
|
name="port"
|
||||||
label={i18n.t('GENERATOR.PORT')}
|
label={i18n.t('GENERATOR.PORT')}
|
||||||
min={1}
|
min={1}
|
||||||
max={65535}
|
max={65535}
|
||||||
rules={[{
|
rules={[{
|
||||||
required: true
|
required: true
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
</ProFormGroup>
|
</ProFormGroup>
|
||||||
<ProFormGroup>
|
<ProFormGroup>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
width="md"
|
width="md"
|
||||||
name="path"
|
name="path"
|
||||||
label={i18n.t('GENERATOR.PATH')}
|
label={i18n.t('GENERATOR.PATH')}
|
||||||
rules={[{
|
rules={[{
|
||||||
required: true
|
required: true
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
<ProFormCascader
|
<ProFormCascader
|
||||||
width="md"
|
width="md"
|
||||||
name="ArchOS"
|
name="ArchOS"
|
||||||
label={i18n.t('GENERATOR.OS_ARCH')}
|
label={i18n.t('GENERATOR.OS_ARCH')}
|
||||||
request={() => prebuilt}
|
request={() => prebuilt}
|
||||||
rules={[{
|
rules={[{
|
||||||
required: true
|
required: true
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
</ProFormGroup>
|
</ProFormGroup>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Generate;
|
export default Generate;
|
@@ -3,66 +3,66 @@ import React, { useRef, useState } from "react";
|
|||||||
import Draggable from "react-draggable";
|
import Draggable from "react-draggable";
|
||||||
|
|
||||||
function DraggableModal(props) {
|
function DraggableModal(props) {
|
||||||
const [disabled, setDisabled] = useState(true);
|
const [disabled, setDisabled] = useState(true);
|
||||||
const [bounds, setBounds] = useState({
|
const [bounds, setBounds] = useState({
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
});
|
});
|
||||||
const draggleRef = useRef(null);
|
const draggleRef = useRef(null);
|
||||||
|
|
||||||
const onStart = (_event, uiData) => {
|
const onStart = (_event, uiData) => {
|
||||||
const { clientWidth, clientHeight } = window.document.documentElement;
|
const { clientWidth, clientHeight } = window.document.documentElement;
|
||||||
const targetRect = draggleRef.current?.getBoundingClientRect();
|
const targetRect = draggleRef.current?.getBoundingClientRect();
|
||||||
if (!targetRect || disabled) {
|
if (!targetRect || disabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBounds({
|
setBounds({
|
||||||
left: -targetRect.left + uiData.x,
|
left: -targetRect.left + uiData.x,
|
||||||
right: clientWidth - (targetRect.right - uiData.x),
|
right: clientWidth - (targetRect.right - uiData.x),
|
||||||
top: -targetRect.top + uiData.y,
|
top: -targetRect.top + uiData.y,
|
||||||
bottom: clientHeight - (targetRect.bottom - uiData.y),
|
bottom: clientHeight - (targetRect.bottom - uiData.y),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
cursor: 'move',
|
cursor: 'move',
|
||||||
}}
|
}}
|
||||||
onMouseOver={() => {
|
onMouseOver={() => {
|
||||||
if (disabled && props.draggable) setDisabled(false);
|
if (disabled && props.draggable) setDisabled(false);
|
||||||
}}
|
}}
|
||||||
onMouseOut={() => {
|
onMouseOut={() => {
|
||||||
setDisabled(true);
|
setDisabled(true);
|
||||||
}}
|
}}
|
||||||
onFocus={() => {}}
|
onFocus={() => {}}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
setDisabled(true);
|
setDisabled(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.modalTitle}
|
{props.modalTitle}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
modalRender={(modal) => (
|
modalRender={(modal) => (
|
||||||
<Draggable
|
<Draggable
|
||||||
disabled={disabled || !props.draggable}
|
disabled={disabled || !props.draggable}
|
||||||
bounds={bounds}
|
bounds={bounds}
|
||||||
onStart={(event, uiData) => onStart(event, uiData)}
|
onStart={(event, uiData) => onStart(event, uiData)}
|
||||||
>
|
>
|
||||||
<div ref={draggleRef}>{modal}</div>
|
<div ref={draggleRef}>{modal}</div>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DraggableModal;
|
export default DraggableModal;
|
@@ -8,131 +8,131 @@ import DraggableModal from "./modal";
|
|||||||
import {ReloadOutlined} from "@ant-design/icons";
|
import {ReloadOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
function ProcessMgr(props) {
|
function ProcessMgr(props) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'Name',
|
key: 'Name',
|
||||||
title: i18n.t('PROCMGR.PROCESS'),
|
title: i18n.t('PROCMGR.PROCESS'),
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 120
|
width: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Pid',
|
key: 'Pid',
|
||||||
title: 'Pid',
|
title: 'Pid',
|
||||||
dataIndex: 'pid',
|
dataIndex: 'pid',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 40
|
width: 40
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Option',
|
key: 'Option',
|
||||||
width: 40,
|
width: 40,
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (_, file) => renderOperation(file)
|
render: (_, file) => renderOperation(file)
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const options = {
|
const options = {
|
||||||
show: true,
|
show: true,
|
||||||
reload: false,
|
reload: false,
|
||||||
density: false,
|
density: false,
|
||||||
setting: false,
|
setting: false,
|
||||||
};
|
};
|
||||||
const tableRef = useRef();
|
const tableRef = useRef();
|
||||||
const virtualTable = useMemo(() => {
|
const virtualTable = useMemo(() => {
|
||||||
return VList({
|
return VList({
|
||||||
height: 300
|
height: 300
|
||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.visible) {
|
if (props.visible) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [props.device, props.visible]);
|
}, [props.device, props.visible]);
|
||||||
|
|
||||||
function renderOperation(proc) {
|
function renderOperation(proc) {
|
||||||
return [
|
return [
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key='kill'
|
key='kill'
|
||||||
title={i18n.t('PROCMGR.KILL_PROCESS_CONFIRM')}
|
title={i18n.t('PROCMGR.KILL_PROCESS_CONFIRM')}
|
||||||
onConfirm={killProcess.bind(null, proc.pid)}
|
onConfirm={killProcess.bind(null, proc.pid)}
|
||||||
>
|
>
|
||||||
<a>{i18n.t('PROCMGR.KILL_PROCESS')}</a>
|
<a>{i18n.t('PROCMGR.KILL_PROCESS')}</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function killProcess(pid) {
|
function killProcess(pid) {
|
||||||
request(`/api/device/process/kill`, {pid: pid, device: props.device}).then(res => {
|
request(`/api/device/process/kill`, {pid: pid, device: props.device}).then(res => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
message.success(i18n.t('PROCMGR.KILL_PROCESS_SUCCESSFULLY'));
|
message.success(i18n.t('PROCMGR.KILL_PROCESS_SUCCESSFULLY'));
|
||||||
tableRef.current.reload();
|
tableRef.current.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getData(form) {
|
async function getData(form) {
|
||||||
await waitTime(300);
|
await waitTime(300);
|
||||||
let res = await request('/api/device/process/list', {device: props.device});
|
let res = await request('/api/device/process/list', {device: props.device});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
data.data.processes = data.data.processes.sort((first, second) => (second.pid - first.pid));
|
data.data.processes = data.data.processes.sort((first, second) => (second.pid - first.pid));
|
||||||
return ({
|
return ({
|
||||||
data: data.data.processes,
|
data: data.data.processes,
|
||||||
success: true,
|
success: true,
|
||||||
total: data.data.processes.length
|
total: data.data.processes.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return ({data: [], success: false, total: 0});
|
return ({data: [], success: false, total: 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableModal
|
<DraggableModal
|
||||||
draggable={true}
|
draggable={true}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
destroyOnClose={true}
|
destroyOnClose={true}
|
||||||
modalTitle={i18n.t('PROCMGR.TITLE')}
|
modalTitle={i18n.t('PROCMGR.TITLE')}
|
||||||
footer={null}
|
footer={null}
|
||||||
width={400}
|
width={400}
|
||||||
bodyStyle={{
|
bodyStyle={{
|
||||||
padding: 0
|
padding: 0
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ProTable
|
<ProTable
|
||||||
rowKey='pid'
|
rowKey='pid'
|
||||||
tableStyle={{
|
tableStyle={{
|
||||||
paddingTop: '20px',
|
paddingTop: '20px',
|
||||||
minHeight: '355px',
|
minHeight: '355px',
|
||||||
maxHeight: '355px'
|
maxHeight: '355px'
|
||||||
}}
|
}}
|
||||||
scroll={{scrollToFirstRowOnChange: true, y: 300}}
|
scroll={{scrollToFirstRowOnChange: true, y: 300}}
|
||||||
search={false}
|
search={false}
|
||||||
size='small'
|
size='small'
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onLoadingChange={setLoading}
|
onLoadingChange={setLoading}
|
||||||
options={options}
|
options={options}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
request={getData}
|
request={getData}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
actionRef={tableRef}
|
actionRef={tableRef}
|
||||||
components={virtualTable}
|
components={virtualTable}
|
||||||
>
|
>
|
||||||
</ProTable>
|
</ProTable>
|
||||||
<Button
|
<Button
|
||||||
style={{right:'59px'}}
|
style={{right:'59px'}}
|
||||||
className='header-button'
|
className='header-button'
|
||||||
icon={<ReloadOutlined />}
|
icon={<ReloadOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
tableRef.current.reload();
|
tableRef.current.reload();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DraggableModal>
|
</DraggableModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ProcessMgr;
|
export default ProcessMgr;
|
@@ -5,48 +5,48 @@ import i18n from "../locale/locale";
|
|||||||
import {message} from "antd";
|
import {message} from "antd";
|
||||||
|
|
||||||
function Runner(props) {
|
function Runner(props) {
|
||||||
async function onFinish(form) {
|
async function onFinish(form) {
|
||||||
form.device = props.device.id;
|
form.device = props.device.id;
|
||||||
let basePath = location.origin + location.pathname + 'api/device/';
|
let basePath = location.origin + location.pathname + 'api/device/';
|
||||||
request(basePath + 'exec', form).then(res => {
|
request(basePath + 'exec', form).then(res => {
|
||||||
if (res.data.code === 0) {
|
if (res.data.code === 0) {
|
||||||
message.success(i18n.t('RUNNER.EXECUTION_SUCCESS'));
|
message.success(i18n.t('RUNNER.EXECUTION_SUCCESS'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
modalProps={{
|
modalProps={{
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
maskClosable: false,
|
maskClosable: false,
|
||||||
}}
|
}}
|
||||||
title={i18n.t('RUNNER.TITLE')}
|
title={i18n.t('RUNNER.TITLE')}
|
||||||
width={380}
|
width={380}
|
||||||
onFinish={onFinish}
|
onFinish={onFinish}
|
||||||
onVisibleChange={visible => {
|
onVisibleChange={visible => {
|
||||||
if (!visible) props.onCancel();
|
if (!visible) props.onCancel();
|
||||||
}}
|
}}
|
||||||
submitter={{
|
submitter={{
|
||||||
render: (_, elems) => elems.pop()
|
render: (_, elems) => elems.pop()
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
width="md"
|
width="md"
|
||||||
name="cmd"
|
name="cmd"
|
||||||
label={i18n.t('RUNNER.CMD_PLACEHOLDER')}
|
label={i18n.t('RUNNER.CMD_PLACEHOLDER')}
|
||||||
rules={[{
|
rules={[{
|
||||||
required: true
|
required: true
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
width="md"
|
width="md"
|
||||||
name="args"
|
name="args"
|
||||||
label={i18n.t('RUNNER.ARGS_PLACEHOLDER')}
|
label={i18n.t('RUNNER.ARGS_PLACEHOLDER')}
|
||||||
/>
|
/>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Runner;
|
export default Runner;
|
@@ -19,458 +19,458 @@ let ctrl = false;
|
|||||||
let conn = false;
|
let conn = false;
|
||||||
let ticker = 0;
|
let ticker = 0;
|
||||||
function TerminalModal(props) {
|
function TerminalModal(props) {
|
||||||
let os = props.device.os;
|
let os = props.device.os;
|
||||||
let extKeyRef = createRef();
|
let extKeyRef = createRef();
|
||||||
let secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
let secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||||
let termRef = useCallback(e => {
|
let termRef = useCallback(e => {
|
||||||
if (e !== null) {
|
if (e !== null) {
|
||||||
termRef.current = e;
|
termRef.current = e;
|
||||||
if (props.visible) {
|
if (props.visible) {
|
||||||
ctrl = false;
|
ctrl = false;
|
||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
convertEol: true,
|
convertEol: true,
|
||||||
allowTransparency: false,
|
allowTransparency: false,
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
cursorStyle: "block",
|
cursorStyle: "block",
|
||||||
fontFamily: "Hack, monospace",
|
fontFamily: "Hack, monospace",
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
logLevel: "off",
|
logLevel: "off",
|
||||||
})
|
})
|
||||||
fit = new FitAddon();
|
fit = new FitAddon();
|
||||||
termEv = initialize(null);
|
termEv = initialize(null);
|
||||||
term.loadAddon(fit);
|
term.loadAddon(fit);
|
||||||
term.loadAddon(new WebLinksAddon());
|
term.loadAddon(new WebLinksAddon());
|
||||||
term.open(termRef.current);
|
term.open(termRef.current);
|
||||||
fit.fit();
|
fit.fit();
|
||||||
term.clear();
|
term.clear();
|
||||||
window.onresize = onResize;
|
window.onresize = onResize;
|
||||||
ticker = setInterval(() => {
|
ticker = setInterval(() => {
|
||||||
if (conn) sendData({act: 'PING'});
|
if (conn) sendData({act: 'PING'});
|
||||||
}, 10000);
|
}, 10000);
|
||||||
term.focus();
|
term.focus();
|
||||||
doResize();
|
doResize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.visible]);
|
}, [props.visible]);
|
||||||
|
|
||||||
function afterClose() {
|
function afterClose() {
|
||||||
clearInterval(ticker);
|
clearInterval(ticker);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
sendData({act: 'TERMINAL_KILL'});
|
sendData({act: 'TERMINAL_KILL'});
|
||||||
ws.onclose = null;
|
ws.onclose = null;
|
||||||
ws.close();
|
ws.close();
|
||||||
}
|
}
|
||||||
termEv?.dispose();
|
termEv?.dispose();
|
||||||
termEv = null;
|
termEv = null;
|
||||||
fit?.dispose();
|
fit?.dispose();
|
||||||
fit = null;
|
fit = null;
|
||||||
term?.dispose();
|
term?.dispose();
|
||||||
term = null;
|
term = null;
|
||||||
ws = null;
|
ws = null;
|
||||||
conn = false;
|
conn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize(ev) {
|
function initialize(ev) {
|
||||||
ev?.dispose();
|
ev?.dispose();
|
||||||
let buffer = { content: '', output: '' };
|
let buffer = { content: '', output: '' };
|
||||||
let termEv = null;
|
let termEv = null;
|
||||||
// Windows doesn't support pty, so we still use traditional way.
|
// Windows doesn't support pty, so we still use traditional way.
|
||||||
// And we need to handle arrow events manually.
|
// And we need to handle arrow events manually.
|
||||||
if (os === 'windows') {
|
if (os === 'windows') {
|
||||||
termEv = term.onData(onWindowsInput.call(this, buffer));
|
termEv = term.onData(onWindowsInput.call(this, buffer));
|
||||||
} else {
|
} else {
|
||||||
termEv = term.onData(onUnixOSInput.call(this, buffer));
|
termEv = term.onData(onUnixOSInput.call(this, buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
ws = new WebSocket(getBaseURL(true, `api/device/terminal?device=${props.device.id}&secret=${secret}`));
|
ws = new WebSocket(getBaseURL(true, `api/device/terminal?device=${props.device.id}&secret=${secret}`));
|
||||||
ws.binaryType = 'arraybuffer';
|
ws.binaryType = 'arraybuffer';
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
conn = true;
|
conn = true;
|
||||||
}
|
}
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
let data = decrypt(e.data, secret);
|
let data = decrypt(e.data, secret);
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
if (conn) {
|
if (conn) {
|
||||||
if (data?.act === 'TERMINAL_OUTPUT') {
|
if (data?.act === 'TERMINAL_OUTPUT') {
|
||||||
data = ab2str(hex2buf(data?.data?.output));
|
data = ab2str(hex2buf(data?.data?.output));
|
||||||
if (buffer.output.length > 0) {
|
if (buffer.output.length > 0) {
|
||||||
data = buffer.output + data;
|
data = buffer.output + data;
|
||||||
buffer.output = '';
|
buffer.output = '';
|
||||||
}
|
}
|
||||||
if (buffer.content.length > 0) {
|
if (buffer.content.length > 0) {
|
||||||
if (data.length > buffer.content.length) {
|
if (data.length > buffer.content.length) {
|
||||||
if (data.startsWith(buffer.content)) {
|
if (data.startsWith(buffer.content)) {
|
||||||
data = data.substring(buffer.content.length);
|
data = data.substring(buffer.content.length);
|
||||||
buffer.content = '';
|
buffer.content = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buffer.output = data;
|
buffer.output = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
term.write(data);
|
term.write(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data?.act === 'WARN') {
|
if (data?.act === 'WARN') {
|
||||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.UNKNOWN_ERROR'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ws.onclose = (e) => {
|
ws.onclose = (e) => {
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn = false;
|
conn = false;
|
||||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ws.onerror = (e) => {
|
ws.onerror = (e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn = false;
|
conn = false;
|
||||||
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
term.write(`\n${i18n.t('COMMON.DISCONNECTED')}\n`);
|
||||||
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
secret = CryptoJS.enc.Hex.parse(genRandHex(32));
|
||||||
} else {
|
} else {
|
||||||
term.write(`\n${i18n.t('COMMON.CONNECTION_FAILED')}\n`);
|
term.write(`\n${i18n.t('COMMON.CONNECTION_FAILED')}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return termEv;
|
return termEv;
|
||||||
}
|
}
|
||||||
function onWindowsInput(buffer) {
|
function onWindowsInput(buffer) {
|
||||||
let cmd = '';
|
let cmd = '';
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
let history = [];
|
let history = [];
|
||||||
let tempCmd = '';
|
let tempCmd = '';
|
||||||
let tempCursor = 0;
|
let tempCursor = 0;
|
||||||
function clearTerm() {
|
function clearTerm() {
|
||||||
let before = cmd.substring(0, cursor);
|
let before = cmd.substring(0, cursor);
|
||||||
let after = cmd.substring(cursor);
|
let after = cmd.substring(cursor);
|
||||||
term.write('\b'.repeat(wcwidth(before)));
|
term.write('\b'.repeat(wcwidth(before)));
|
||||||
term.write(' '.repeat(wcwidth(cmd)));
|
term.write(' '.repeat(wcwidth(cmd)));
|
||||||
term.write('\b'.repeat(wcwidth(cmd)));
|
term.write('\b'.repeat(wcwidth(cmd)));
|
||||||
}
|
}
|
||||||
return function (e) {
|
return function (e) {
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
if (e === '\r' || e === '\n' || e === ' ') {
|
if (e === '\r' || e === '\n' || e === ' ') {
|
||||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||||
termEv = initialize(termEv);
|
termEv = initialize(termEv);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case '\x1B\x5B\x41': // up arrow.
|
case '\x1B\x5B\x41': // up arrow.
|
||||||
if (index > 0 && index <= history.length) {
|
if (index > 0 && index <= history.length) {
|
||||||
if (index === history.length) {
|
if (index === history.length) {
|
||||||
tempCmd = cmd;
|
tempCmd = cmd;
|
||||||
tempCursor = cursor;
|
tempCursor = cursor;
|
||||||
}
|
}
|
||||||
index--;
|
index--;
|
||||||
clearTerm.call(this);
|
clearTerm.call(this);
|
||||||
cmd = history[index];
|
cmd = history[index];
|
||||||
cursor = cmd.length;
|
cursor = cmd.length;
|
||||||
term.write(cmd);
|
term.write(cmd);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\x1B\x5B\x42': // down arrow.
|
case '\x1B\x5B\x42': // down arrow.
|
||||||
if (index + 1 < history.length) {
|
if (index + 1 < history.length) {
|
||||||
index++;
|
index++;
|
||||||
clearTerm.call(this);
|
clearTerm.call(this);
|
||||||
cmd = history[index];
|
cmd = history[index];
|
||||||
cursor = cmd.length;
|
cursor = cmd.length;
|
||||||
term.write(cmd);
|
term.write(cmd);
|
||||||
} else if (index + 1 <= history.length) {
|
} else if (index + 1 <= history.length) {
|
||||||
clearTerm.call(this);
|
clearTerm.call(this);
|
||||||
index++;
|
index++;
|
||||||
cmd = tempCmd;
|
cmd = tempCmd;
|
||||||
cursor = tempCursor;
|
cursor = tempCursor;
|
||||||
term.write(cmd);
|
term.write(cmd);
|
||||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd.substring(cursor))));
|
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd.substring(cursor))));
|
||||||
tempCmd = '';
|
tempCmd = '';
|
||||||
tempCursor = 0;
|
tempCursor = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\x1B\x5B\x43': // right arrow.
|
case '\x1B\x5B\x43': // right arrow.
|
||||||
if (cursor < cmd.length) {
|
if (cursor < cmd.length) {
|
||||||
term.write('\x1B\x5B\x43'.repeat(wcwidth(cmd[cursor])));
|
term.write('\x1B\x5B\x43'.repeat(wcwidth(cmd[cursor])));
|
||||||
cursor++;
|
cursor++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\x1B\x5B\x44': // left arrow.
|
case '\x1B\x5B\x44': // left arrow.
|
||||||
if (cursor > 0) {
|
if (cursor > 0) {
|
||||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd[cursor-1])));
|
term.write('\x1B\x5B\x44'.repeat(wcwidth(cmd[cursor-1])));
|
||||||
cursor--;
|
cursor--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
if (cmd === 'clear' || cmd === 'cls') {
|
if (cmd === 'clear' || cmd === 'cls') {
|
||||||
clearTerm.call(this);
|
clearTerm.call(this);
|
||||||
term.clear();
|
term.clear();
|
||||||
} else {
|
} else {
|
||||||
term.write('\n');
|
term.write('\n');
|
||||||
sendWindowsInput(cmd + '\n');
|
sendWindowsInput(cmd + '\n');
|
||||||
buffer.content = cmd + '\n';
|
buffer.content = cmd + '\n';
|
||||||
}
|
}
|
||||||
if (cmd.length > 0) history.push(cmd);
|
if (cmd.length > 0) history.push(cmd);
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
cmd = '';
|
cmd = '';
|
||||||
if (history.length > 128) {
|
if (history.length > 128) {
|
||||||
history = history.slice(history.length - 128);
|
history = history.slice(history.length - 128);
|
||||||
}
|
}
|
||||||
tempCmd = '';
|
tempCmd = '';
|
||||||
tempCursor = 0;
|
tempCursor = 0;
|
||||||
index = history.length;
|
index = history.length;
|
||||||
break;
|
break;
|
||||||
case '\x7F': // backspace.
|
case '\x7F': // backspace.
|
||||||
if (cmd.length > 0 && cursor > 0) {
|
if (cmd.length > 0 && cursor > 0) {
|
||||||
cursor--;
|
cursor--;
|
||||||
let charWidth = wcwidth(cmd[cursor]);
|
let charWidth = wcwidth(cmd[cursor]);
|
||||||
let before = cmd.substring(0, cursor);
|
let before = cmd.substring(0, cursor);
|
||||||
let after = cmd.substring(cursor+1);
|
let after = cmd.substring(cursor+1);
|
||||||
cmd = before + after;
|
cmd = before + after;
|
||||||
term.write('\b'.repeat(charWidth));
|
term.write('\b'.repeat(charWidth));
|
||||||
term.write(after + ' '.repeat(charWidth));
|
term.write(after + ' '.repeat(charWidth));
|
||||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after) + charWidth));
|
term.write('\x1B\x5B\x44'.repeat(wcwidth(after) + charWidth));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if ((e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7B)) || e >= '\xA0') {
|
if ((e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7B)) || e >= '\xA0') {
|
||||||
if (cursor < cmd.length) {
|
if (cursor < cmd.length) {
|
||||||
let before = cmd.substring(0, cursor);
|
let before = cmd.substring(0, cursor);
|
||||||
let after = cmd.substring(cursor);
|
let after = cmd.substring(cursor);
|
||||||
cmd = before + e + after;
|
cmd = before + e + after;
|
||||||
term.write(e + after);
|
term.write(e + after);
|
||||||
term.write('\x1B\x5B\x44'.repeat(wcwidth(after)));
|
term.write('\x1B\x5B\x44'.repeat(wcwidth(after)));
|
||||||
} else {
|
} else {
|
||||||
cmd += e;
|
cmd += e;
|
||||||
term.write(e);
|
term.write(e);
|
||||||
}
|
}
|
||||||
cursor += e.length;
|
cursor += e.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function onUnixOSInput(_) {
|
function onUnixOSInput(_) {
|
||||||
return function (e) {
|
return function (e) {
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
if (e === '\r' || e === ' ') {
|
if (e === '\r' || e === ' ') {
|
||||||
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
term.write(`\n${i18n.t('COMMON.RECONNECTING')}\n`);
|
||||||
termEv = initialize(termEv);
|
termEv = initialize(termEv);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendUnixOSInput(e);
|
sendUnixOSInput(e);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWindowsInput(input) {
|
function sendWindowsInput(input) {
|
||||||
if (conn) {
|
if (conn) {
|
||||||
sendData({
|
sendData({
|
||||||
act: 'TERMINAL_INPUT',
|
act: 'TERMINAL_INPUT',
|
||||||
data: {
|
data: {
|
||||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function sendUnixOSInput(input) {
|
function sendUnixOSInput(input) {
|
||||||
if (conn) {
|
if (conn) {
|
||||||
if (ctrl && input.length === 1) {
|
if (ctrl && input.length === 1) {
|
||||||
let charCode = input.charCodeAt(0);
|
let charCode = input.charCodeAt(0);
|
||||||
if (charCode >= 0x61 && charCode <= 0x7A) {
|
if (charCode >= 0x61 && charCode <= 0x7A) {
|
||||||
charCode -= 0x60;
|
charCode -= 0x60;
|
||||||
ctrl = false;
|
ctrl = false;
|
||||||
extKeyRef.current.setCtrl(false);
|
extKeyRef.current.setCtrl(false);
|
||||||
} else if (charCode >= 0x40 && charCode <= 0x5F) {
|
} else if (charCode >= 0x40 && charCode <= 0x5F) {
|
||||||
charCode -= 0x40;
|
charCode -= 0x40;
|
||||||
ctrl = false;
|
ctrl = false;
|
||||||
extKeyRef.current.setCtrl(false);
|
extKeyRef.current.setCtrl(false);
|
||||||
}
|
}
|
||||||
input = String.fromCharCode(charCode);
|
input = String.fromCharCode(charCode);
|
||||||
}
|
}
|
||||||
console.log(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input)));
|
console.log(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input)));
|
||||||
sendData({
|
sendData({
|
||||||
act: 'TERMINAL_INPUT',
|
act: 'TERMINAL_INPUT',
|
||||||
data: {
|
data: {
|
||||||
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
input: CryptoJS.enc.Hex.stringify(CryptoJS.enc.Utf8.parse(input))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function sendData(data) {
|
function sendData(data) {
|
||||||
if (conn) {
|
if (conn) {
|
||||||
ws.send(encrypt(data, secret));
|
ws.send(encrypt(data, secret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doResize() {
|
function doResize() {
|
||||||
let height = document.body.clientHeight;
|
let height = document.body.clientHeight;
|
||||||
let rows = Math.floor(height / 42);
|
let rows = Math.floor(height / 42);
|
||||||
let cols = term?.cols;
|
let cols = term?.cols;
|
||||||
fit?.fit?.();
|
fit?.fit?.();
|
||||||
term?.resize?.(cols, rows);
|
term?.resize?.(cols, rows);
|
||||||
term?.scrollToBottom?.();
|
term?.scrollToBottom?.();
|
||||||
|
|
||||||
if (conn) {
|
if (conn) {
|
||||||
sendData({
|
sendData({
|
||||||
act: 'TERMINAL_RESIZE',
|
act: 'TERMINAL_RESIZE',
|
||||||
data: {
|
data: {
|
||||||
width: cols,
|
width: cols,
|
||||||
height: rows
|
height: rows
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onResize() {
|
function onResize() {
|
||||||
if (typeof doResize === 'function') {
|
if (typeof doResize === 'function') {
|
||||||
debounce(doResize, 70);
|
debounce(doResize, 70);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCtrl(val) {
|
function onCtrl(val) {
|
||||||
term?.focus?.();
|
term?.focus?.();
|
||||||
if (!conn && val) {
|
if (!conn && val) {
|
||||||
extKeyRef.current.setCtrl(false);
|
extKeyRef.current.setCtrl(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctrl = val;
|
ctrl = val;
|
||||||
}
|
}
|
||||||
function onExtKey(val, focus) {
|
function onExtKey(val, focus) {
|
||||||
sendUnixOSInput(val);
|
sendUnixOSInput(val);
|
||||||
if (focus) term?.focus?.();
|
if (focus) term?.focus?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableModal
|
<DraggableModal
|
||||||
draggable={true}
|
draggable={true}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
modalTitle={i18n.t('TERMINAL.TITLE')}
|
modalTitle={i18n.t('TERMINAL.TITLE')}
|
||||||
visible={props.visible}
|
visible={props.visible}
|
||||||
onCancel={props.onCancel}
|
onCancel={props.onCancel}
|
||||||
bodyStyle={{padding: 12}}
|
bodyStyle={{padding: 12}}
|
||||||
afterClose={afterClose}
|
afterClose={afterClose}
|
||||||
destroyOnClose={true}
|
destroyOnClose={true}
|
||||||
footer={null}
|
footer={null}
|
||||||
height={200}
|
height={200}
|
||||||
width={900}
|
width={900}
|
||||||
>
|
>
|
||||||
<ExtKeyboard
|
<ExtKeyboard
|
||||||
ref={extKeyRef}
|
ref={extKeyRef}
|
||||||
onCtrl={onCtrl}
|
onCtrl={onCtrl}
|
||||||
onExtKey={onExtKey}
|
onExtKey={onExtKey}
|
||||||
visible={os!=='windows'}
|
visible={os!=='windows'}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: '0 5px',
|
padding: '0 5px',
|
||||||
backgroundColor: '#000',
|
backgroundColor: '#000',
|
||||||
}}
|
}}
|
||||||
ref={termRef}
|
ref={termRef}
|
||||||
/>
|
/>
|
||||||
</DraggableModal>
|
</DraggableModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtKeyboard extends React.Component {
|
class ExtKeyboard extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.visible = props.visible;
|
this.visible = props.visible;
|
||||||
if (!this.visible) return;
|
if (!this.visible) return;
|
||||||
this.fnKeys = [
|
this.fnKeys = [
|
||||||
{key: '\x1B\x4F\x50', label: 'F1'},
|
{key: '\x1B\x4F\x50', label: 'F1'},
|
||||||
{key: '\x1B\x4F\x51', label: 'F2'},
|
{key: '\x1B\x4F\x51', label: 'F2'},
|
||||||
{key: '\x1B\x4F\x52', label: 'F3'},
|
{key: '\x1B\x4F\x52', label: 'F3'},
|
||||||
{key: '\x1B\x4F\x53', label: 'F4'},
|
{key: '\x1B\x4F\x53', label: 'F4'},
|
||||||
{key: '\x1B\x5B\x31\x35\x7E', label: 'F5'},
|
{key: '\x1B\x5B\x31\x35\x7E', label: 'F5'},
|
||||||
{key: '\x1B\x5B\x31\x37\x7E', label: 'F6'},
|
{key: '\x1B\x5B\x31\x37\x7E', label: 'F6'},
|
||||||
{key: '\x1B\x5B\x31\x38\x7E', label: 'F7'},
|
{key: '\x1B\x5B\x31\x38\x7E', label: 'F7'},
|
||||||
{key: '\x1B\x5B\x31\x39\x7E', label: 'F8'},
|
{key: '\x1B\x5B\x31\x39\x7E', label: 'F8'},
|
||||||
{key: '\x1B\x5B\x32\x30\x7E', label: 'F9'},
|
{key: '\x1B\x5B\x32\x30\x7E', label: 'F9'},
|
||||||
{key: '\x1B\x5B\x32\x31\x7E', label: 'F10'},
|
{key: '\x1B\x5B\x32\x31\x7E', label: 'F10'},
|
||||||
{key: '\x1B\x5B\x32\x33\x7E', label: 'F11'},
|
{key: '\x1B\x5B\x32\x33\x7E', label: 'F11'},
|
||||||
{key: '\x1B\x5B\x32\x34\x7E', label: 'F12'},
|
{key: '\x1B\x5B\x32\x34\x7E', label: 'F12'},
|
||||||
];
|
];
|
||||||
this.fnMenu = (
|
this.fnMenu = (
|
||||||
<Menu onClick={this.onFnKey.bind(this)}>
|
<Menu onClick={this.onFnKey.bind(this)}>
|
||||||
{this.fnKeys.map(e =>
|
{this.fnKeys.map(e =>
|
||||||
<Menu.Item key={e.key}>
|
<Menu.Item key={e.key}>
|
||||||
{e.label}
|
{e.label}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
this.state = {ctrl: false};
|
this.state = {ctrl: false};
|
||||||
}
|
}
|
||||||
|
|
||||||
onCtrl() {
|
onCtrl() {
|
||||||
this.setState({ctrl: !this.state.ctrl});
|
this.setState({ctrl: !this.state.ctrl});
|
||||||
this.props.onCtrl(!this.state.ctrl);
|
this.props.onCtrl(!this.state.ctrl);
|
||||||
}
|
}
|
||||||
onExtKey(key) {
|
onExtKey(key) {
|
||||||
this.props.onExtKey(key, true);
|
this.props.onExtKey(key, true);
|
||||||
}
|
}
|
||||||
onFnKey(e) {
|
onFnKey(e) {
|
||||||
this.props.onExtKey(e.key, false);
|
this.props.onExtKey(e.key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCtrl(val) {
|
setCtrl(val) {
|
||||||
this.setState({ctrl: val});
|
this.setState({ctrl: val});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.visible) return null;
|
if (!this.visible) return null;
|
||||||
return (
|
return (
|
||||||
<Space style={{paddingBottom: 12}}>
|
<Space style={{paddingBottom: 12}}>
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
type={this.state.ctrl?'primary':'default'}
|
type={this.state.ctrl?'primary':'default'}
|
||||||
onClick={this.onCtrl.bind(this)}
|
onClick={this.onCtrl.bind(this)}
|
||||||
>
|
>
|
||||||
CTRL
|
CTRL
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x1B')}
|
onClick={this.onExtKey.bind(this, '\x1B')}
|
||||||
>
|
>
|
||||||
ESC
|
ESC
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x09')}
|
onClick={this.onExtKey.bind(this, '\x09')}
|
||||||
>
|
>
|
||||||
TAB
|
TAB
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x41')}
|
onClick={this.onExtKey.bind(this, '\x1B\x5B\x41')}
|
||||||
>
|
>
|
||||||
⬆
|
⬆
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x42')}
|
onClick={this.onExtKey.bind(this, '\x1B\x5B\x42')}
|
||||||
>
|
>
|
||||||
⬇
|
⬇
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x43')}
|
onClick={this.onExtKey.bind(this, '\x1B\x5B\x43')}
|
||||||
>
|
>
|
||||||
➡
|
➡
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={this.onExtKey.bind(this, '\x1B\x5B\x44')}
|
onClick={this.onExtKey.bind(this, '\x1B\x5B\x44')}
|
||||||
>
|
>
|
||||||
⬅
|
⬅
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
<Dropdown.Button
|
<Dropdown.Button
|
||||||
overlay={this.fnMenu}
|
overlay={this.fnMenu}
|
||||||
>
|
>
|
||||||
{i18n.t('TERMINAL.FUNCTION_KEYS')}
|
{i18n.t('TERMINAL.FUNCTION_KEYS')}
|
||||||
</Dropdown.Button>
|
</Dropdown.Button>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TerminalModal;
|
export default TerminalModal;
|
@@ -1,19 +1,19 @@
|
|||||||
.ant-page-header {
|
.ant-page-header {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-top-nav-header-logo {
|
.ant-pro-top-nav-header-logo {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-button {
|
.header-button {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
font-weight: 700 !important;
|
font-weight: 700 !important;
|
||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
top: 3px !important;
|
top: 3px !important;
|
||||||
height: 50px !important;
|
height: 50px !important;
|
||||||
width: 50px !important;
|
width: 50px !important;
|
||||||
color: rgba(0, 0, 0, 0.45) !important;
|
color: rgba(0, 0, 0, 0.45) !important;
|
||||||
}
|
}
|
@@ -7,35 +7,36 @@ import {ConfigProvider} from "antd";
|
|||||||
import './wrapper.css';
|
import './wrapper.css';
|
||||||
|
|
||||||
function wrapper(props) {
|
function wrapper(props) {
|
||||||
return (
|
return (
|
||||||
<ProLayout
|
<ProLayout
|
||||||
loading={false}
|
loading={false}
|
||||||
title='Spark'
|
title='Spark'
|
||||||
layout='top'
|
logo={null}
|
||||||
navTheme='light'
|
layout='top'
|
||||||
collapsed={true}
|
navTheme='light'
|
||||||
fixedHeader={true}
|
collapsed={true}
|
||||||
contentWidth='fluid'
|
fixedHeader={true}
|
||||||
collapsedButtonRender={Title}
|
contentWidth='fluid'
|
||||||
>
|
collapsedButtonRender={Title}
|
||||||
<PageContainer>
|
>
|
||||||
<ConfigProvider locale={getLang()==='zh-CN'?zhCN:en}>
|
<PageContainer>
|
||||||
{props.children}
|
<ConfigProvider locale={getLang()==='zh-CN'?zhCN:en}>
|
||||||
</ConfigProvider>
|
{props.children}
|
||||||
</PageContainer>
|
</ConfigProvider>
|
||||||
</ProLayout>
|
</PageContainer>
|
||||||
);
|
</ProLayout>
|
||||||
};
|
);
|
||||||
|
}
|
||||||
function Title() {
|
function Title() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
fontWeight: 500
|
fontWeight: 500
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Spark
|
Spark
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default wrapper;
|
export default wrapper;
|
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"mac": {
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
"ram_total": {
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
"disk_usage": {
|
|
||||||
"show": true
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,16 +1,16 @@
|
|||||||
body {
|
body {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image: url('static/bg.svg');
|
background-image: url('static/bg.svg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center 110px;
|
background-position: center 110px;
|
||||||
background-color: #f0f2f5;
|
background-color: #f0f2f5;
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-cell {
|
.ant-table-cell {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
}
|
}
|
@@ -14,41 +14,41 @@ import {translate} from "./utils/utils";
|
|||||||
|
|
||||||
axios.defaults.baseURL = '.';
|
axios.defaults.baseURL = '.';
|
||||||
axios.interceptors.response.use(async res => {
|
axios.interceptors.response.use(async res => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data.hasOwnProperty('code')) {
|
if (data.hasOwnProperty('code')) {
|
||||||
if (data.code !== 0){
|
if (data.code !== 0){
|
||||||
message.warn(translate(data.msg));
|
message.warn(translate(data.msg));
|
||||||
} else {
|
} else {
|
||||||
// The first request will ask user to provide user/pass.
|
// The first request will ask user to provide user/pass.
|
||||||
// If set timeout at the beginning, then timeout warning
|
// If set timeout at the beginning, then timeout warning
|
||||||
// might be triggered before authentication finished.
|
// might be triggered before authentication finished.
|
||||||
axios.defaults.timeout = 5000;
|
axios.defaults.timeout = 5000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve(res);
|
return Promise.resolve(res);
|
||||||
}, err => {
|
}, err => {
|
||||||
// console.error(err);
|
// console.error(err);
|
||||||
if (err.code === 'ECONNABORTED') {
|
if (err.code === 'ECONNABORTED') {
|
||||||
message.error(i18n.t('COMMON.REQUEST_TIMEOUT'));
|
message.error(i18n.t('COMMON.REQUEST_TIMEOUT'));
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
let res = err.response;
|
let res = err.response;
|
||||||
let data = res?.data ?? {};
|
let data = res?.data ?? {};
|
||||||
if (data.hasOwnProperty('code') && data.hasOwnProperty('msg')) {
|
if (data.hasOwnProperty('code') && data.hasOwnProperty('msg')) {
|
||||||
if (data.code !== 0){
|
if (data.code !== 0){
|
||||||
message.warn(translate(data.msg));
|
message.warn(translate(data.msg));
|
||||||
return Promise.resolve(res);
|
return Promise.resolve(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Wrapper><Overview/></Wrapper>}/>
|
<Route path="/" element={<Wrapper><Overview/></Wrapper>}/>
|
||||||
<Route path="*" element={<Err/>}/>
|
<Route path="*" element={<Err/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>,
|
</Router>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
@@ -1,28 +1,28 @@
|
|||||||
import i18n from 'i18next';
|
import i18n from 'i18next';
|
||||||
|
|
||||||
const locales = {
|
const locales = {
|
||||||
'en': 'en',
|
'en': 'en',
|
||||||
'en-US': 'en',
|
'en-US': 'en',
|
||||||
'zh-CN': 'zh-CN',
|
'zh-CN': 'zh-CN',
|
||||||
};
|
};
|
||||||
const lang = navigator.language && navigator.language.length ? navigator.language : 'en';
|
const lang = navigator.language && navigator.language.length ? navigator.language : 'en';
|
||||||
|
|
||||||
let resources = {};
|
let resources = {};
|
||||||
for (const locale in locales) {
|
for (const locale in locales) {
|
||||||
resources[locale] = {
|
resources[locale] = {
|
||||||
translation: require(`./${locales[locale]}.json`),
|
translation: require(`./${locales[locale]}.json`),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n.init({
|
i18n.init({
|
||||||
lng: lang,
|
lng: lang,
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
initImmediate: true,
|
initImmediate: true,
|
||||||
resources
|
resources
|
||||||
});
|
});
|
||||||
|
|
||||||
function getLang() {
|
function getLang() {
|
||||||
return lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getLang };
|
export { getLang };
|
||||||
|
@@ -2,13 +2,13 @@ import React from 'react';
|
|||||||
import i18n from "../locale/locale";
|
import i18n from "../locale/locale";
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
// setTimeout(()=>{
|
// setTimeout(()=>{
|
||||||
// location.href = '#/';
|
// location.href = '#/';
|
||||||
// }, 3000);
|
// }, 3000);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h1 style={{textAlign: 'center', userSelect: 'none'}}>
|
<h1 style={{textAlign: 'center', userSelect: 'none'}}>
|
||||||
{i18n.t('COMMON.PAGE_NOT_FOUND')}
|
{i18n.t('COMMON.PAGE_NOT_FOUND')}
|
||||||
</h1>
|
</h1>
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -11,453 +11,451 @@ import Runner from "../components/runner";
|
|||||||
import {QuestionCircleOutlined} from "@ant-design/icons";
|
import {QuestionCircleOutlined} from "@ant-design/icons";
|
||||||
import i18n from "../locale/locale";
|
import i18n from "../locale/locale";
|
||||||
|
|
||||||
import defaultColumnsState from "../config/columnsState.json";
|
|
||||||
|
|
||||||
// DO NOT EDIT OR DELETE THIS COPYRIGHT MESSAGE.
|
// DO NOT EDIT OR DELETE THIS COPYRIGHT MESSAGE.
|
||||||
console.log("%c By XZB %c https://github.com/XZB-1248/Spark", 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:64px;color:#00bbee;-webkit-text-fill-color:#00bbee;-webkit-text-stroke:1px#00bbee;', 'font-size:12px;');
|
console.log("%c By XZB %c https://github.com/XZB-1248/Spark", 'font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:64px;color:#00bbee;-webkit-text-fill-color:#00bbee;-webkit-text-stroke:1px#00bbee;', 'font-size:12px;');
|
||||||
|
|
||||||
function UsageBar(props) {
|
function UsageBar(props) {
|
||||||
let {usage} = props;
|
let {usage} = props;
|
||||||
usage = usage || 0;
|
usage = usage || 0;
|
||||||
usage = Math.round(usage * 100) / 100;
|
usage = Math.round(usage * 100) / 100;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={props.title??`${usage}%`}
|
title={props.title??`${usage}%`}
|
||||||
overlayInnerStyle={{
|
overlayInnerStyle={{
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
wordBreak: 'keep-all',
|
wordBreak: 'keep-all',
|
||||||
maxWidth: '300px',
|
maxWidth: '300px',
|
||||||
}}
|
}}
|
||||||
overlayStyle={{
|
overlayStyle={{
|
||||||
maxWidth: '300px',
|
maxWidth: '300px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Progress percent={usage} showInfo={false} strokeWidth={12} trailColor='#FFECFF'/>
|
<Progress percent={usage} showInfo={false} strokeWidth={12} trailColor='#FFECFF'/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function overview(props) {
|
function overview(props) {
|
||||||
const [runner, setRunner] = useState(false);
|
const [runner, setRunner] = useState(false);
|
||||||
const [desktop, setDesktop] = useState(false);
|
const [desktop, setDesktop] = useState(false);
|
||||||
const [procMgr, setProcMgr] = useState(false);
|
const [procMgr, setProcMgr] = useState(false);
|
||||||
const [explorer, setExplorer] = useState(false);
|
const [explorer, setExplorer] = useState(false);
|
||||||
const [generate, setGenerate] = useState(false);
|
const [generate, setGenerate] = useState(false);
|
||||||
const [terminal, setTerminal] = useState(false);
|
const [terminal, setTerminal] = useState(false);
|
||||||
const [screenBlob, setScreenBlob] = useState('');
|
const [screenBlob, setScreenBlob] = useState('');
|
||||||
const [isWindows, setIsWindows] = useState(false);
|
const [isWindows, setIsWindows] = useState(false);
|
||||||
const [dataSource, setDataSource] = useState([]);
|
const [dataSource, setDataSource] = useState([]);
|
||||||
const [columnsState, setColumnsState] = useState(getInitColumnsState());
|
const [columnsState, setColumnsState] = useState(getInitColumnsState());
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
key: 'hostname',
|
key: 'hostname',
|
||||||
title: i18n.t('OVERVIEW.HOSTNAME'),
|
title: i18n.t('OVERVIEW.HOSTNAME'),
|
||||||
dataIndex: 'hostname',
|
dataIndex: 'hostname',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'username',
|
key: 'username',
|
||||||
title: i18n.t('OVERVIEW.USERNAME'),
|
title: i18n.t('OVERVIEW.USERNAME'),
|
||||||
dataIndex: 'username',
|
dataIndex: 'username',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 90
|
width: 90
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ping',
|
key: 'ping',
|
||||||
title: 'Ping',
|
title: 'Ping',
|
||||||
dataIndex: 'latency',
|
dataIndex: 'latency',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
renderText: (v) => String(v) + 'ms',
|
renderText: (v) => String(v) + 'ms',
|
||||||
width: 60
|
width: 60
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'cpu_usage',
|
key: 'cpu_usage',
|
||||||
title: i18n.t('OVERVIEW.CPU_USAGE'),
|
title: i18n.t('OVERVIEW.CPU_USAGE'),
|
||||||
dataIndex: 'cpu_usage',
|
dataIndex: 'cpu_usage',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (_, v) => <UsageBar title={renderCPUStat(v.cpu)} {...v.cpu} />,
|
render: (_, v) => <UsageBar title={renderCPUStat(v.cpu)} {...v.cpu} />,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ram_usage',
|
key: 'ram_usage',
|
||||||
title: i18n.t('OVERVIEW.RAM_USAGE'),
|
title: i18n.t('OVERVIEW.RAM_USAGE'),
|
||||||
dataIndex: 'ram_usage',
|
dataIndex: 'ram_usage',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (_, v) => <UsageBar title={renderRAMStat(v.ram)} {...v.ram} />,
|
render: (_, v) => <UsageBar title={renderRAMStat(v.ram)} {...v.ram} />,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'disk_usage',
|
key: 'disk_usage',
|
||||||
title: i18n.t('OVERVIEW.DISK_USAGE'),
|
title: i18n.t('OVERVIEW.DISK_USAGE'),
|
||||||
dataIndex: 'disk_usage',
|
dataIndex: 'disk_usage',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (_, v) => <UsageBar title={renderDiskStat(v.disk)} {...v.disk} />,
|
render: (_, v) => <UsageBar title={renderDiskStat(v.disk)} {...v.disk} />,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'os',
|
key: 'os',
|
||||||
title: i18n.t('OVERVIEW.OS'),
|
title: i18n.t('OVERVIEW.OS'),
|
||||||
dataIndex: 'os',
|
dataIndex: 'os',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 80
|
width: 80
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'arch',
|
key: 'arch',
|
||||||
title: i18n.t('OVERVIEW.ARCH'),
|
title: i18n.t('OVERVIEW.ARCH'),
|
||||||
dataIndex: 'arch',
|
dataIndex: 'arch',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 70
|
width: 70
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ram_total',
|
key: 'ram_total',
|
||||||
title: i18n.t('OVERVIEW.RAM'),
|
title: i18n.t('OVERVIEW.RAM'),
|
||||||
dataIndex: 'ram_total',
|
dataIndex: 'ram_total',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
renderText: formatSize,
|
renderText: formatSize,
|
||||||
width: 70
|
width: 70
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'mac',
|
key: 'mac',
|
||||||
title: 'MAC',
|
title: 'MAC',
|
||||||
dataIndex: 'mac',
|
dataIndex: 'mac',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'lan',
|
key: 'lan',
|
||||||
title: 'LAN',
|
title: 'LAN',
|
||||||
dataIndex: 'lan',
|
dataIndex: 'lan',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'wan',
|
key: 'wan',
|
||||||
title: 'WAN',
|
title: 'WAN',
|
||||||
dataIndex: 'wan',
|
dataIndex: 'wan',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'uptime',
|
key: 'uptime',
|
||||||
title: i18n.t('OVERVIEW.UPTIME'),
|
title: i18n.t('OVERVIEW.UPTIME'),
|
||||||
dataIndex: 'uptime',
|
dataIndex: 'uptime',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
renderText: tsToTime,
|
renderText: tsToTime,
|
||||||
width: 100
|
width: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'net_stat',
|
key: 'net_stat',
|
||||||
title: i18n.t('OVERVIEW.NETWORK'),
|
title: i18n.t('OVERVIEW.NETWORK'),
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
renderText: (_, v) => renderNetworkIO(v),
|
renderText: (_, v) => renderNetworkIO(v),
|
||||||
width: 170
|
width: 170
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'option',
|
key: 'option',
|
||||||
title: i18n.t('OVERVIEW.OPERATIONS'),
|
title: i18n.t('OVERVIEW.OPERATIONS'),
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
ellipsis: false,
|
ellipsis: false,
|
||||||
render: (_, device) => renderOperation(device),
|
render: (_, device) => renderOperation(device),
|
||||||
width: 170
|
width: 170
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const options = {
|
const options = {
|
||||||
show: true,
|
show: true,
|
||||||
density: true,
|
density: true,
|
||||||
setting: true,
|
setting: true,
|
||||||
};
|
};
|
||||||
const tableRef = useRef();
|
const tableRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Auto update is only available when all modal are closed.
|
// Auto update is only available when all modal are closed.
|
||||||
if (!runner && !desktop && !procMgr && !explorer && !generate && !terminal) {
|
if (!runner && !desktop && !procMgr && !explorer && !generate && !terminal) {
|
||||||
let id = setInterval(getData, 3000);
|
let id = setInterval(getData, 3000);
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [runner, desktop, procMgr, explorer, generate, terminal]);
|
}, [runner, desktop, procMgr, explorer, generate, terminal]);
|
||||||
|
|
||||||
function getInitColumnsState() {
|
function getInitColumnsState() {
|
||||||
let data = localStorage.getItem(`columnsState`);
|
let data = localStorage.getItem(`columnsState`);
|
||||||
if (data !== null) {
|
if (data !== null) {
|
||||||
let stateMap = {};
|
let stateMap = {};
|
||||||
try {
|
try {
|
||||||
stateMap = JSON.parse(data);
|
stateMap = JSON.parse(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
stateMap = {};
|
stateMap = {};
|
||||||
}
|
}
|
||||||
return stateMap
|
return stateMap
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem(`columnsState`, JSON.stringify(defaultColumnsState));
|
localStorage.setItem(`columnsState`, JSON.stringify({}));
|
||||||
}
|
return {};
|
||||||
return defaultColumnsState;
|
}
|
||||||
}
|
}
|
||||||
function saveColumnsState(stateMap) {
|
function saveColumnsState(stateMap) {
|
||||||
setColumnsState(stateMap);
|
setColumnsState(stateMap);
|
||||||
localStorage.setItem(`columnsState`, JSON.stringify(stateMap));
|
localStorage.setItem(`columnsState`, JSON.stringify(stateMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCPUStat(cpu) {
|
function renderCPUStat(cpu) {
|
||||||
let { model, usage, cores } = cpu;
|
let { model, usage, cores } = cpu;
|
||||||
usage = Math.round(usage * 100) / 100;
|
usage = Math.round(usage * 100) / 100;
|
||||||
cores = {
|
cores = {
|
||||||
physical: Math.max(cores.physical, 1),
|
physical: Math.max(cores.physical, 1),
|
||||||
logical: Math.max(cores.logical, 1),
|
logical: Math.max(cores.logical, 1),
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{model}
|
{model}
|
||||||
</div>
|
</div>
|
||||||
{i18n.t('OVERVIEW.CPU_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
{i18n.t('OVERVIEW.CPU_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.CPU_LOGICAL_CORES') + i18n.t('COMMON.COLON') + cores.logical}
|
{i18n.t('OVERVIEW.CPU_LOGICAL_CORES') + i18n.t('COMMON.COLON') + cores.logical}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.CPU_PHYSICAL_CORES') + i18n.t('COMMON.COLON') + cores.physical}
|
{i18n.t('OVERVIEW.CPU_PHYSICAL_CORES') + i18n.t('COMMON.COLON') + cores.physical}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function renderRAMStat(info) {
|
function renderRAMStat(info) {
|
||||||
let { usage, total, used } = info;
|
let { usage, total, used } = info;
|
||||||
usage = Math.round(usage * 100) / 100;
|
usage = Math.round(usage * 100) / 100;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{i18n.t('OVERVIEW.RAM_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
{i18n.t('OVERVIEW.RAM_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function renderDiskStat(info) {
|
function renderDiskStat(info) {
|
||||||
let { usage, total, used } = info;
|
let { usage, total, used } = info;
|
||||||
usage = Math.round(usage * 100) / 100;
|
usage = Math.round(usage * 100) / 100;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{i18n.t('OVERVIEW.DISK_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
{i18n.t('OVERVIEW.DISK_USAGE') + i18n.t('COMMON.COLON') + usage + '%'}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
{i18n.t('OVERVIEW.FREE') + i18n.t('COMMON.COLON') + formatSize(total - used)}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
{i18n.t('OVERVIEW.USED') + i18n.t('COMMON.COLON') + formatSize(used)}
|
||||||
<br />
|
<br />
|
||||||
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
{i18n.t('OVERVIEW.TOTAL') + i18n.t('COMMON.COLON') + formatSize(total)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function renderNetworkIO(device) {
|
function renderNetworkIO(device) {
|
||||||
// Make unit starts with Kbps.
|
// Make unit starts with Kbps.
|
||||||
let sent = device.net_sent * 8 / 1024;
|
let sent = device.net_sent * 8 / 1024;
|
||||||
let recv = device.net_recv * 8 / 1024;
|
let recv = device.net_recv * 8 / 1024;
|
||||||
return `${format(sent)} ↑ / ${format(recv)} ↓`;
|
return `${format(sent)} ↑ / ${format(recv)} ↓`;
|
||||||
|
|
||||||
function format(size) {
|
function format(size) {
|
||||||
if (size <= 1) return '0 Kbps';
|
if (size <= 1) return '0 Kbps';
|
||||||
// Units array is large enough.
|
// Units array is large enough.
|
||||||
let k = 1024,
|
let k = 1024,
|
||||||
i = Math.floor(Math.log(size) / Math.log(k)),
|
i = Math.floor(Math.log(size) / Math.log(k)),
|
||||||
units = ['Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
units = ['Kbps', 'Mbps', 'Gbps', 'Tbps'];
|
||||||
return (size / Math.pow(k, i)).toFixed(1) + ' ' + units[i];
|
return (size / Math.pow(k, i)).toFixed(1) + ' ' + units[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function renderOperation(device) {
|
function renderOperation(device) {
|
||||||
let menus = [
|
let menus = [
|
||||||
{key: 'run', name: i18n.t('OVERVIEW.RUN')},
|
{key: 'run', name: i18n.t('OVERVIEW.RUN')},
|
||||||
{key: 'desktop', name: i18n.t('OVERVIEW.DESKTOP')},
|
{key: 'desktop', name: i18n.t('OVERVIEW.DESKTOP')},
|
||||||
{key: 'screenshot', name: i18n.t('OVERVIEW.SCREENSHOT')},
|
{key: 'screenshot', name: i18n.t('OVERVIEW.SCREENSHOT')},
|
||||||
{key: 'lock', name: i18n.t('OVERVIEW.LOCK')},
|
{key: 'lock', name: i18n.t('OVERVIEW.LOCK')},
|
||||||
{key: 'logoff', name: i18n.t('OVERVIEW.LOGOFF')},
|
{key: 'logoff', name: i18n.t('OVERVIEW.LOGOFF')},
|
||||||
{key: 'hibernate', name: i18n.t('OVERVIEW.HIBERNATE')},
|
{key: 'hibernate', name: i18n.t('OVERVIEW.HIBERNATE')},
|
||||||
{key: 'suspend', name: i18n.t('OVERVIEW.SUSPEND')},
|
{key: 'suspend', name: i18n.t('OVERVIEW.SUSPEND')},
|
||||||
{key: 'restart', name: i18n.t('OVERVIEW.RESTART')},
|
{key: 'restart', name: i18n.t('OVERVIEW.RESTART')},
|
||||||
{key: 'shutdown', name: i18n.t('OVERVIEW.SHUTDOWN')},
|
{key: 'shutdown', name: i18n.t('OVERVIEW.SHUTDOWN')},
|
||||||
{key: 'offline', name: i18n.t('OVERVIEW.OFFLINE')},
|
{key: 'offline', name: i18n.t('OVERVIEW.OFFLINE')},
|
||||||
];
|
];
|
||||||
return [
|
return [
|
||||||
<a key='terminal' onClick={setTerminal.bind(null, device)}>{i18n.t('OVERVIEW.TERMINAL')}</a>,
|
<a key='terminal' onClick={setTerminal.bind(null, device)}>{i18n.t('OVERVIEW.TERMINAL')}</a>,
|
||||||
<a key='procmgr' onClick={setProcMgr.bind(null, device.id)}>{i18n.t('OVERVIEW.PROC_MANAGER')}</a>,
|
<a key='procmgr' onClick={setProcMgr.bind(null, device.id)}>{i18n.t('OVERVIEW.PROC_MANAGER')}</a>,
|
||||||
<a key='explorer' onClick={() => {
|
<a key='explorer' onClick={() => {
|
||||||
setExplorer(device.id);
|
setExplorer(device.id);
|
||||||
setIsWindows(device.os === 'windows');
|
setIsWindows(device.os === 'windows');
|
||||||
}}>
|
}}>
|
||||||
{i18n.t('OVERVIEW.EXPLORER')}
|
{i18n.t('OVERVIEW.EXPLORER')}
|
||||||
</a>,
|
</a>,
|
||||||
<TableDropdown
|
<TableDropdown
|
||||||
key='more'
|
key='more'
|
||||||
onSelect={key => callDevice(key, device)}
|
onSelect={key => callDevice(key, device)}
|
||||||
menus={menus}
|
menus={menus}
|
||||||
/>,
|
/>,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function callDevice(act, device) {
|
function callDevice(act, device) {
|
||||||
if (act === 'run') {
|
if (act === 'run') {
|
||||||
setRunner(device);
|
setRunner(device);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (act === 'desktop') {
|
if (act === 'desktop') {
|
||||||
setDesktop(device);
|
setDesktop(device);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (act === 'screenshot') {
|
if (act === 'screenshot') {
|
||||||
request('/api/device/screenshot/get', {device: device.id}, {}, {
|
request('/api/device/screenshot/get', {device: device.id}, {}, {
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if ((res.data.type ?? '').substring(0, 5) === 'image') {
|
if ((res.data.type ?? '').substring(0, 5) === 'image') {
|
||||||
if (screenBlob.length > 0) {
|
if (screenBlob.length > 0) {
|
||||||
URL.revokeObjectURL(screenBlob);
|
URL.revokeObjectURL(screenBlob);
|
||||||
}
|
}
|
||||||
setScreenBlob(URL.createObjectURL(res.data));
|
setScreenBlob(URL.createObjectURL(res.data));
|
||||||
}
|
}
|
||||||
}).catch(catchBlobReq);
|
}).catch(catchBlobReq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: i18n.t('OVERVIEW.OPERATION_CONFIRM').replace('{0}', i18n.t('OVERVIEW.'+act.toUpperCase())),
|
title: i18n.t('OVERVIEW.OPERATION_CONFIRM').replace('{0}', i18n.t('OVERVIEW.'+act.toUpperCase())),
|
||||||
icon: <QuestionCircleOutlined/>,
|
icon: <QuestionCircleOutlined/>,
|
||||||
onOk() {
|
onOk() {
|
||||||
request('/api/device/' + act, {device: device.id}).then(res => {
|
request('/api/device/' + act, {device: device.id}).then(res => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
message.success(i18n.t('OVERVIEW.OPERATION_SUCCESS'));
|
message.success(i18n.t('OVERVIEW.OPERATION_SUCCESS'));
|
||||||
tableRef.current.reload();
|
tableRef.current.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function toolBar() {
|
function toolBar() {
|
||||||
return (
|
return (
|
||||||
<Button type='primary' onClick={setGenerate.bind(null, true)}>{i18n.t('OVERVIEW.GENERATE')}</Button>
|
<Button type='primary' onClick={setGenerate.bind(null, true)}>{i18n.t('OVERVIEW.GENERATE')}</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getData(form) {
|
async function getData(form) {
|
||||||
await waitTime(300);
|
await waitTime(300);
|
||||||
let res = await request('/api/device/list');
|
let res = await request('/api/device/list');
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data.code === 0) {
|
if (data.code === 0) {
|
||||||
let result = [];
|
let result = [];
|
||||||
for (const uuid in data.data) {
|
for (const uuid in data.data) {
|
||||||
let temp = data.data[uuid];
|
let temp = data.data[uuid];
|
||||||
temp.conn = uuid;
|
temp.conn = uuid;
|
||||||
result.push(temp);
|
result.push(temp);
|
||||||
}
|
}
|
||||||
// Iterate all object and expand them.
|
// Iterate all object and expand them.
|
||||||
for (let i = 0; i < result.length; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
for (const k in result[i]) {
|
for (const k in result[i]) {
|
||||||
if (typeof result[i][k] === 'object') {
|
if (typeof result[i][k] === 'object') {
|
||||||
for (const key in result[i][k]) {
|
for (const key in result[i][k]) {
|
||||||
result[i][k + '_' + key] = result[i][k][key];
|
result[i][k + '_' + key] = result[i][k][key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = result.sort((first, second) => {
|
result = result.sort((first, second) => {
|
||||||
let firstEl = first.hostname.toUpperCase();
|
let firstEl = first.hostname.toUpperCase();
|
||||||
let secondEl = second.hostname.toUpperCase();
|
let secondEl = second.hostname.toUpperCase();
|
||||||
if (firstEl < secondEl) return -1;
|
if (firstEl < secondEl) return -1;
|
||||||
if (firstEl > secondEl) return 1;
|
if (firstEl > secondEl) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
result = result.sort((first, second) => {
|
result = result.sort((first, second) => {
|
||||||
let firstEl = first.os.toUpperCase();
|
let firstEl = first.os.toUpperCase();
|
||||||
let secondEl = second.os.toUpperCase();
|
let secondEl = second.os.toUpperCase();
|
||||||
if (firstEl < secondEl) return -1;
|
if (firstEl < secondEl) return -1;
|
||||||
if (firstEl > secondEl) return 1;
|
if (firstEl > secondEl) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
setDataSource(result);
|
setDataSource(result);
|
||||||
return ({
|
return ({
|
||||||
data: result,
|
data: result,
|
||||||
success: true,
|
success: true,
|
||||||
total: result.length
|
total: result.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return ({data: [], success: false, total: 0});
|
return ({data: [], success: false, total: 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Image
|
<Image
|
||||||
preview={{
|
preview={{
|
||||||
visible: screenBlob,
|
visible: screenBlob,
|
||||||
src: screenBlob,
|
src: screenBlob,
|
||||||
onVisibleChange: () => {
|
onVisibleChange: () => {
|
||||||
URL.revokeObjectURL(screenBlob);
|
URL.revokeObjectURL(screenBlob);
|
||||||
setScreenBlob('');
|
setScreenBlob('');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Generate
|
<Generate
|
||||||
visible={generate}
|
visible={generate}
|
||||||
onVisibleChange={setGenerate}
|
onVisibleChange={setGenerate}
|
||||||
/>
|
/>
|
||||||
<Explorer
|
<Explorer
|
||||||
isWindows={isWindows}
|
isWindows={isWindows}
|
||||||
visible={explorer}
|
visible={explorer}
|
||||||
device={explorer}
|
device={explorer}
|
||||||
onCancel={setExplorer.bind(null, false)}
|
onCancel={setExplorer.bind(null, false)}
|
||||||
/>
|
/>
|
||||||
<ProcMgr
|
<ProcMgr
|
||||||
visible={procMgr}
|
visible={procMgr}
|
||||||
device={procMgr}
|
device={procMgr}
|
||||||
onCancel={setProcMgr.bind(null, false)}
|
onCancel={setProcMgr.bind(null, false)}
|
||||||
/>
|
/>
|
||||||
<Runner
|
<Runner
|
||||||
visible={runner}
|
visible={runner}
|
||||||
device={runner}
|
device={runner}
|
||||||
onCancel={setRunner.bind(null, false)}
|
onCancel={setRunner.bind(null, false)}
|
||||||
/>
|
/>
|
||||||
<Desktop
|
<Desktop
|
||||||
visible={desktop}
|
visible={desktop}
|
||||||
device={desktop}
|
device={desktop}
|
||||||
onCancel={setDesktop.bind(null, false)}
|
onCancel={setDesktop.bind(null, false)}
|
||||||
/>
|
/>
|
||||||
<Terminal
|
<Terminal
|
||||||
visible={terminal}
|
visible={terminal}
|
||||||
device={terminal}
|
device={terminal}
|
||||||
onCancel={setTerminal.bind(null, false)}
|
onCancel={setTerminal.bind(null, false)}
|
||||||
/>
|
/>
|
||||||
<ProTable
|
<ProTable
|
||||||
scroll={{
|
scroll={{
|
||||||
x: 'max-content',
|
x: 'max-content',
|
||||||
scrollToFirstRowOnChange: true
|
scrollToFirstRowOnChange: true
|
||||||
}}
|
}}
|
||||||
rowKey='id'
|
rowKey='id'
|
||||||
search={false}
|
search={false}
|
||||||
options={options}
|
options={options}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
columnsState={{
|
columnsState={{
|
||||||
value: columnsState,
|
value: columnsState,
|
||||||
onChange: saveColumnsState
|
onChange: saveColumnsState
|
||||||
}}
|
}}
|
||||||
request={getData}
|
request={getData}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
actionRef={tableRef}
|
actionRef={tableRef}
|
||||||
toolBarRender={toolBar}
|
toolBarRender={toolBar}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
onDataSourceChange={setDataSource}
|
onDataSourceChange={setDataSource}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapper(props) {
|
function wrapper(props) {
|
||||||
let Component = overview;
|
let Component = overview;
|
||||||
return (<Component {...props} key={Math.random()}/>)
|
return (<Component {...props} key={Math.random()}/>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default wrapper;
|
export default wrapper;
|
@@ -1,99 +1,99 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
<g transform="translate(-79.000000, -82.000000)">
|
<g transform="translate(-79.000000, -82.000000)">
|
||||||
<g transform="translate(77.000000, 73.000000)">
|
<g transform="translate(77.000000, 73.000000)">
|
||||||
<g opacity="0.8"
|
<g opacity="0.8"
|
||||||
transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
|
||||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479"
|
<ellipse fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479"
|
||||||
ry="21.766008"></ellipse>
|
ry="21.766008"></ellipse>
|
||||||
<ellipse fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913"
|
<ellipse fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913"
|
||||||
ry="5.21330997"></ellipse>
|
ry="5.21330997"></ellipse>
|
||||||
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z"
|
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z"
|
||||||
fill="#CFDAE6" opacity="0.45"></path>
|
fill="#CFDAE6" opacity="0.45"></path>
|
||||||
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" stroke="#CFDAE6" stroke-width="1.73913043"
|
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" stroke="#CFDAE6" stroke-width="1.73913043"
|
||||||
stroke-linecap="round" stroke-linejoin="round"></path>
|
stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" stroke="#E0B4B7" stroke-width="0.702678964"
|
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" stroke="#E0B4B7" stroke-width="0.702678964"
|
||||||
opacity="0.7" stroke-linecap="round" stroke-linejoin="round"
|
opacity="0.7" stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||||
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" stroke="#BACAD9" stroke-width="0.702678964"
|
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" stroke="#BACAD9" stroke-width="0.702678964"
|
||||||
stroke-linecap="round" stroke-linejoin="round"
|
stroke-linecap="round" stroke-linejoin="round"
|
||||||
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
stroke-dasharray="1.405357899873153,2.108036953469981"></path>
|
||||||
<g transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)"
|
<g transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)"
|
||||||
fill="#CFDAE6">
|
fill="#CFDAE6">
|
||||||
<ellipse opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653"
|
<ellipse opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653"
|
||||||
ry="9.12768076"></ellipse>
|
ry="9.12768076"></ellipse>
|
||||||
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z"
|
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z"
|
||||||
transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
<g transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
|
||||||
<ellipse fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471"
|
<ellipse fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471"
|
||||||
ry="29.1402439"></ellipse>
|
ry="29.1402439"></ellipse>
|
||||||
<ellipse fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275"
|
<ellipse fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275"
|
||||||
ry="21.5853659"></ellipse>
|
ry="21.5853659"></ellipse>
|
||||||
<ellipse stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902"
|
<ellipse stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902"
|
||||||
ry="23.7439024"></ellipse>
|
ry="23.7439024"></ellipse>
|
||||||
<ellipse fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137"
|
<ellipse fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137"
|
||||||
ry="10.7926829"></ellipse>
|
ry="10.7926829"></ellipse>
|
||||||
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z"
|
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z"
|
||||||
fill="#BACAD9"></path>
|
fill="#BACAD9"></path>
|
||||||
<g opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
<g opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
|
||||||
<ellipse cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
<ellipse cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
|
||||||
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z"
|
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z"
|
||||||
transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
|
||||||
</g>
|
</g>
|
||||||
<ellipse fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
<ellipse fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
|
||||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706"
|
<ellipse fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706"
|
||||||
ry="1.61890244"></ellipse>
|
ry="1.61890244"></ellipse>
|
||||||
<ellipse fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275"
|
<ellipse fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275"
|
||||||
ry="2.15853659"></ellipse>
|
ry="2.15853659"></ellipse>
|
||||||
<path d="M28.9985381,29.9671598 L171.151018,132.876024" stroke="#CFDAE6" opacity="0.8"></path>
|
<path d="M28.9985381,29.9671598 L171.151018,132.876024" stroke="#CFDAE6" opacity="0.8"></path>
|
||||||
</g>
|
</g>
|
||||||
<g opacity="0.799999952"
|
<g opacity="0.799999952"
|
||||||
transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
|
||||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407"
|
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407"
|
||||||
ry="11.2941176"></ellipse>
|
ry="11.2941176"></ellipse>
|
||||||
<g transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
<g transform="translate(34.596774, 23.111111)" fill="#BACAD9">
|
||||||
<ellipse opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627"
|
<ellipse opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627"
|
||||||
ry="8.55614973"></ellipse>
|
ry="8.55614973"></ellipse>
|
||||||
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z"></path>
|
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z"></path>
|
||||||
</g>
|
</g>
|
||||||
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" stroke="#CFDAE6"
|
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" stroke="#CFDAE6"
|
||||||
stroke-width="0.941176471"></path>
|
stroke-width="0.941176471"></path>
|
||||||
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186"
|
<ellipse stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186"
|
||||||
ry="3.29411765"></ellipse>
|
ry="3.29411765"></ellipse>
|
||||||
<ellipse fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
<ellipse fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
|
||||||
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" stroke="#CFDAE6"
|
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" stroke="#CFDAE6"
|
||||||
stroke-width="0.941176471"></path>
|
stroke-width="0.941176471"></path>
|
||||||
</g>
|
</g>
|
||||||
<g opacity="0.33"
|
<g opacity="0.33"
|
||||||
transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
|
||||||
<g transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)"
|
<g transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)"
|
||||||
fill="#BACAD9">
|
fill="#BACAD9">
|
||||||
<circle opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
<circle opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
|
||||||
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z"
|
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z"
|
||||||
transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
|
||||||
</g>
|
</g>
|
||||||
<circle fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
<circle fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
|
||||||
<path d="M143.5,88.8126685 L155.070501,17.6038544" stroke="#BACAD9"
|
<path d="M143.5,88.8126685 L155.070501,17.6038544" stroke="#BACAD9"
|
||||||
stroke-width="1.16666667"></path>
|
stroke-width="1.16666667"></path>
|
||||||
<path d="M17.5,37.3333333 L127.466252,97.6449735" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
<path d="M17.5,37.3333333 L127.466252,97.6449735" stroke="#BACAD9" stroke-width="1.16666667"></path>
|
||||||
<polyline stroke="#CFDAE6" stroke-width="1.16666667"
|
<polyline stroke="#CFDAE6" stroke-width="1.16666667"
|
||||||
points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
|
||||||
<path d="M159.833333,99.7453842 L195.416667,89.25" stroke="#E0B4B7" stroke-width="1.16666667"
|
<path d="M159.833333,99.7453842 L195.416667,89.25" stroke="#E0B4B7" stroke-width="1.16666667"
|
||||||
opacity="0.6"></path>
|
opacity="0.6"></path>
|
||||||
<path d="M205.333333,82.1372105 L238.719406,36.1666667" stroke="#BACAD9"
|
<path d="M205.333333,82.1372105 L238.719406,36.1666667" stroke="#BACAD9"
|
||||||
stroke-width="1.16666667"></path>
|
stroke-width="1.16666667"></path>
|
||||||
<path d="M266.723424,132.231988 L207.083333,90.4166667" stroke="#CFDAE6"
|
<path d="M266.723424,132.231988 L207.083333,90.4166667" stroke="#CFDAE6"
|
||||||
stroke-width="1.16666667"></path>
|
stroke-width="1.16666667"></path>
|
||||||
<circle fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
<circle fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
|
||||||
<circle fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
<circle fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
|
||||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
<circle fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
|
||||||
<circle fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
<circle fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
|
||||||
<circle fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
<circle fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -6,210 +6,210 @@ import CryptoJS from "crypto-js";
|
|||||||
|
|
||||||
let orderCompare;
|
let orderCompare;
|
||||||
try {
|
try {
|
||||||
let collator = new Intl.Collator(getLang(), {numeric: true, sensitivity: 'base'});
|
let collator = new Intl.Collator(getLang(), {numeric: true, sensitivity: 'base'});
|
||||||
orderCompare = collator.compare.bind(collator);
|
orderCompare = collator.compare.bind(collator);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
orderCompare = (a, b) => a - b;
|
orderCompare = (a, b) => a - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function request(url, data, headers, ext, noTrans) {
|
function request(url, data, headers, ext, noTrans) {
|
||||||
let _headers = headers ?? {};
|
let _headers = headers ?? {};
|
||||||
_headers = Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, _headers);
|
_headers = Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, _headers);
|
||||||
return axios(Object.assign({
|
return axios(Object.assign({
|
||||||
url: url,
|
url: url,
|
||||||
data: data,
|
data: data,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
transformRequest: noTrans ? [] : [Qs.stringify],
|
transformRequest: noTrans ? [] : [Qs.stringify],
|
||||||
}, ext??{}));
|
}, ext??{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitTime(time) {
|
function waitTime(time) {
|
||||||
time = (time ?? 100);
|
time = (time ?? 100);
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}, time);
|
}, time);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSize(size) {
|
function formatSize(size) {
|
||||||
size = isNaN(size) ? 0 : (size??0);
|
size = isNaN(size) ? 0 : (size??0);
|
||||||
size = Math.max(size, 0);
|
size = Math.max(size, 0);
|
||||||
let k = 1024,
|
let k = 1024,
|
||||||
i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(k)),
|
i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(k)),
|
||||||
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
||||||
result = size / Math.pow(k, i);
|
result = size / Math.pow(k, i);
|
||||||
return (Math.round(result * 100) / 100) + ' ' + units[i];
|
return (Math.round(result * 100) / 100) + ' ' + units[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
function tsToTime(ts) {
|
function tsToTime(ts) {
|
||||||
if (isNaN(ts)) return 'Unknown';
|
if (isNaN(ts)) return 'Unknown';
|
||||||
let hours = Math.floor(ts / 3600);
|
let hours = Math.floor(ts / 3600);
|
||||||
ts %= 3600;
|
ts %= 3600;
|
||||||
let minutes = Math.floor(ts / 60);
|
let minutes = Math.floor(ts / 60);
|
||||||
return `${String(hours) + i18n.t('COMMON.HOURS') + ' ' + String(minutes) + i18n.t('COMMON.MINUTES')}`;
|
return `${String(hours) + i18n.t('COMMON.HOURS') + ' ' + String(minutes) + i18n.t('COMMON.MINUTES')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBaseURL(ws, suffix) {
|
function getBaseURL(ws, suffix) {
|
||||||
if (location.protocol === 'https:') {
|
if (location.protocol === 'https:') {
|
||||||
let scheme = ws ? 'wss' : 'https';
|
let scheme = ws ? 'wss' : 'https';
|
||||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||||
}
|
}
|
||||||
let scheme = ws ? 'ws' : 'http';
|
let scheme = ws ? 'ws' : 'http';
|
||||||
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
return scheme + `://${location.host}${location.pathname}${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function genRandHex(len) {
|
function genRandHex(len) {
|
||||||
return [...Array(len)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
return [...Array(len)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function post(url, data, ext) {
|
function post(url, data, ext) {
|
||||||
let form = document.createElement('form');
|
let form = document.createElement('form');
|
||||||
form.action = url;
|
form.action = url;
|
||||||
form.method = 'POST';
|
form.method = 'POST';
|
||||||
form.target = '_self';
|
form.target = '_self';
|
||||||
|
|
||||||
for (const key in ext) {
|
for (const key in ext) {
|
||||||
form[key] = ext[key];
|
form[key] = ext[key];
|
||||||
}
|
}
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
if (Array.isArray(data[key])) {
|
if (Array.isArray(data[key])) {
|
||||||
for (let i = 0; i < data[key].length; i++) {
|
for (let i = 0; i < data[key].length; i++) {
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
input.name = key;
|
input.name = key;
|
||||||
input.value = data[key][i];
|
input.value = data[key][i];
|
||||||
form.appendChild(input);
|
form.appendChild(input);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let input = document.createElement('input');
|
let input = document.createElement('input');
|
||||||
input.name = key;
|
input.name = key;
|
||||||
input.value = data[key];
|
input.value = data[key];
|
||||||
form.appendChild(input);
|
form.appendChild(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.appendChild(form).submit();
|
document.body.appendChild(form).submit();
|
||||||
form.remove();
|
form.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function translate(text) {
|
function translate(text) {
|
||||||
return text.replace(/\$\{i18n\|([a-zA-Z0-9_.]+)\}/g, (match, key) => {
|
return text.replace(/\$\{i18n\|([a-zA-Z0-9_.]+)\}/g, (match, key) => {
|
||||||
return i18n.t(key);
|
return i18n.t(key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function preventClose(e) {
|
function preventClose(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.returnValue = '';
|
e.returnValue = '';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function catchBlobReq(err) {
|
function catchBlobReq(err) {
|
||||||
let res = err.response;
|
let res = err.response;
|
||||||
if ((res?.data?.type ?? '').startsWith('application/json')) {
|
if ((res?.data?.type ?? '').startsWith('application/json')) {
|
||||||
let data = res?.data ?? {};
|
let data = res?.data ?? {};
|
||||||
data.text().then((str) => {
|
data.text().then((str) => {
|
||||||
let data = {};
|
let data = {};
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(str);
|
data = JSON.parse(str);
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.REQUEST_FAILED'));
|
message.warn(data.msg ? translate(data.msg) : i18n.t('COMMON.REQUEST_FAILED'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hex2buf(hex) {
|
function hex2buf(hex) {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') {
|
||||||
return new Uint8Array([]);
|
return new Uint8Array([]);
|
||||||
}
|
}
|
||||||
let list = hex.match(/.{1,2}/g);
|
let list = hex.match(/.{1,2}/g);
|
||||||
if (list === null) {
|
if (list === null) {
|
||||||
return new Uint8Array([]);
|
return new Uint8Array([]);
|
||||||
}
|
}
|
||||||
return new Uint8Array(list.map(byte => parseInt(byte, 16)));
|
return new Uint8Array(list.map(byte => parseInt(byte, 16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function ab2str(buffer) {
|
function ab2str(buffer) {
|
||||||
const array = new Uint8Array(buffer);
|
const array = new Uint8Array(buffer);
|
||||||
let out, i, len, c;
|
let out, i, len, c;
|
||||||
let char2, char3;
|
let char2, char3;
|
||||||
|
|
||||||
out = "";
|
out = "";
|
||||||
len = array.length;
|
len = array.length;
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
c = array[i++];
|
c = array[i++];
|
||||||
switch (c >> 4) {
|
switch (c >> 4) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
case 7:
|
case 7:
|
||||||
out += String.fromCharCode(c);
|
out += String.fromCharCode(c);
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
case 13:
|
case 13:
|
||||||
char2 = array[i++];
|
char2 = array[i++];
|
||||||
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
char2 = array[i++];
|
char2 = array[i++];
|
||||||
char3 = array[i++];
|
char3 = array[i++];
|
||||||
out += String.fromCharCode(((c & 0x0F) << 12) |
|
out += String.fromCharCode(((c & 0x0F) << 12) |
|
||||||
((char2 & 0x3F) << 6) |
|
((char2 & 0x3F) << 6) |
|
||||||
((char3 & 0x3F) << 0));
|
((char3 & 0x3F) << 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ws2ua(wordArray) {
|
function ws2ua(wordArray) {
|
||||||
const l = wordArray.sigBytes;
|
const l = wordArray.sigBytes;
|
||||||
const words = wordArray.words;
|
const words = wordArray.words;
|
||||||
const result = new Uint8Array(l);
|
const result = new Uint8Array(l);
|
||||||
let i = 0, j = 0;
|
let i = 0, j = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (i === l)
|
if (i === l)
|
||||||
break;
|
break;
|
||||||
const w = words[j++];
|
const w = words[j++];
|
||||||
result[i++] = (w & 0xff000000) >>> 24;
|
result[i++] = (w & 0xff000000) >>> 24;
|
||||||
if (i === l)
|
if (i === l)
|
||||||
break;
|
break;
|
||||||
result[i++] = (w & 0x00ff0000) >>> 16;
|
result[i++] = (w & 0x00ff0000) >>> 16;
|
||||||
if (i === l)
|
if (i === l)
|
||||||
break;
|
break;
|
||||||
result[i++] = (w & 0x0000ff00) >>> 8;
|
result[i++] = (w & 0x0000ff00) >>> 8;
|
||||||
if (i === l)
|
if (i === l)
|
||||||
break;
|
break;
|
||||||
result[i++] = (w & 0x000000ff);
|
result[i++] = (w & 0x000000ff);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function encrypt(data, secret) {
|
function encrypt(data, secret) {
|
||||||
let json = JSON.stringify(data);
|
let json = JSON.stringify(data);
|
||||||
json = CryptoJS.enc.Utf8.parse(json);
|
json = CryptoJS.enc.Utf8.parse(json);
|
||||||
let encrypted = CryptoJS.AES.encrypt(json, secret, {
|
let encrypted = CryptoJS.AES.encrypt(json, secret, {
|
||||||
mode: CryptoJS.mode.CTR,
|
mode: CryptoJS.mode.CTR,
|
||||||
iv: secret,
|
iv: secret,
|
||||||
padding: CryptoJS.pad.NoPadding
|
padding: CryptoJS.pad.NoPadding
|
||||||
});
|
});
|
||||||
return ws2ua(encrypted.ciphertext);
|
return ws2ua(encrypted.ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decrypt(data, secret) {
|
function decrypt(data, secret) {
|
||||||
data = CryptoJS.lib.WordArray.create(data);
|
data = CryptoJS.lib.WordArray.create(data);
|
||||||
let decrypted = CryptoJS.AES.encrypt(data, secret, {
|
let decrypted = CryptoJS.AES.encrypt(data, secret, {
|
||||||
mode: CryptoJS.mode.CTR,
|
mode: CryptoJS.mode.CTR,
|
||||||
iv: secret,
|
iv: secret,
|
||||||
padding: CryptoJS.pad.NoPadding
|
padding: CryptoJS.pad.NoPadding
|
||||||
});
|
});
|
||||||
return ab2str(ws2ua(decrypted.ciphertext).buffer);
|
return ab2str(ws2ua(decrypted.ciphertext).buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {post, request, waitTime, formatSize, tsToTime, getBaseURL, genRandHex, translate, preventClose, catchBlobReq, hex2buf, ab2str, ws2ua, encrypt, decrypt, orderCompare};
|
export {post, request, waitTime, formatSize, tsToTime, getBaseURL, genRandHex, translate, preventClose, catchBlobReq, hex2buf, ab2str, ws2ua, encrypt, decrypt, orderCompare};
|
Reference in New Issue
Block a user