mirror of
https://github.com/MrXujiang/h5-Dooring.git
synced 2025-12-11 17:32:50 +00:00
feat
This commit is contained in:
parent
3720aaa471
commit
2a66dff0e0
@ -1,6 +1,5 @@
|
|||||||
import React, { useMemo } from "react";
|
import React, { useMemo, useState, useCallback } from "react";
|
||||||
import { Controlled } from "react-codemirror2";
|
import { Controlled } from "react-codemirror2";
|
||||||
import { useState } from "react";
|
|
||||||
import { Button, message } from "antd";
|
import { Button, message } from "antd";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import Logo from "@/assets/logo.png";
|
import Logo from "@/assets/logo.png";
|
||||||
@ -13,7 +12,7 @@ require("codemirror/mode/javascript/javascript");
|
|||||||
|
|
||||||
const serverUrl = isDev ? "http://localhost:3000" : "http://localhost:3000";
|
const serverUrl = isDev ? "http://localhost:3000" : "http://localhost:3000";
|
||||||
|
|
||||||
let html = `<!DOCTYPE html>
|
const defaultHtml = `<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@ -48,97 +47,97 @@ let html = `<!DOCTYPE html>
|
|||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function() {
|
export default function CodeEditor() {
|
||||||
const [isUpdate, setUpdate] = useState(false);
|
const [htmlContent, setHtmlContent] = useState(defaultHtml);
|
||||||
const [cursor, setCursor] = useState<CodeMirror.Position>({ line: 1, ch: 1 });
|
const [cursorPosition, setCursorPosition] = useState({ line: 1, ch: 1 });
|
||||||
const [data, setData] = useState<{ data: string }>({ data: html });
|
const [isUpdated, setIsUpdated] = useState(false);
|
||||||
const handleChange = (
|
|
||||||
|
const rect = useGetRect();
|
||||||
|
|
||||||
|
// 计算高度
|
||||||
|
const contentHeight = useMemo(() => {
|
||||||
|
const baseHeight = rect.height - 42 - 1; // 减去顶部高度和滚动条防溢出
|
||||||
|
return Math.max(baseHeight, 694); // 最小高度为 694
|
||||||
|
}, [rect.height]);
|
||||||
|
|
||||||
|
const phoneHeight = 694; // 固定高度,避免大屏幕问题
|
||||||
|
const iframeHeight = phoneHeight - 30 - 24; // 上边距 30,上下 padding 各 12
|
||||||
|
|
||||||
|
// 保存页面
|
||||||
|
const savePage = useCallback(
|
||||||
|
(content?: string) => {
|
||||||
|
const contentToSave = content ?? htmlContent;
|
||||||
|
fetch(`${serverUrl}/dooring/render`, {
|
||||||
|
method: "POST",
|
||||||
|
body: contentToSave,
|
||||||
|
}).then(() => {
|
||||||
|
message.success("已保存");
|
||||||
|
setIsUpdated((prev) => !prev); // 触发重新渲染
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[htmlContent]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 下载 HTML 文件
|
||||||
|
const downloadHtml = useCallback(() => {
|
||||||
|
const file = new File([htmlContent], `${Date.now()}.html`, {
|
||||||
|
type: "text/html;charset=utf-8",
|
||||||
|
});
|
||||||
|
saveAs(file);
|
||||||
|
}, [htmlContent]);
|
||||||
|
|
||||||
|
// 快捷键保存
|
||||||
|
useHotkeys(
|
||||||
|
"ctrl+s",
|
||||||
|
(event) => {
|
||||||
|
savePage();
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
[savePage]
|
||||||
|
);
|
||||||
|
|
||||||
|
// CodeMirror 编辑器事件处理
|
||||||
|
const handleCodeChange = (
|
||||||
_editor: CodeMirror.Editor,
|
_editor: CodeMirror.Editor,
|
||||||
_data: CodeMirror.EditorChange,
|
_data: CodeMirror.EditorChange,
|
||||||
value: string
|
value: string
|
||||||
) => {
|
) => {
|
||||||
setData({ data: value });
|
setHtmlContent(value);
|
||||||
};
|
|
||||||
const fetchPage = useMemo(() => {
|
|
||||||
return (v?: string) => {
|
|
||||||
let res = v ?? data.data;
|
|
||||||
fetch(`${serverUrl}/dooring/render`, { method: "POST", body: res }).then(
|
|
||||||
() => {
|
|
||||||
html = res;
|
|
||||||
message.success("已保存");
|
|
||||||
setUpdate(prev => !prev);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}, [data]);
|
|
||||||
const downLoadHtml = () => {
|
|
||||||
var file = new File([data.data], `${Date.now()}.html`, {
|
|
||||||
type: "text/html;charset=utf-8"
|
|
||||||
});
|
|
||||||
saveAs(file);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCursorChange = (
|
const handleCursorChange = (
|
||||||
_editor: CodeMirror.Editor,
|
_editor: CodeMirror.Editor,
|
||||||
data1: CodeMirror.Position
|
position: CodeMirror.Position
|
||||||
) => {
|
) => {
|
||||||
const { line, ch } = data1;
|
setCursorPosition(position);
|
||||||
setCursor({ line, ch });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useHotkeys<HTMLDivElement>(
|
const handleEditorKeyDown = (editor: CodeMirror.Editor, event: KeyboardEvent) => {
|
||||||
"ctrl+s",
|
if (event.ctrlKey && event.key === "s") {
|
||||||
event => {
|
savePage(editor.getValue());
|
||||||
fetchPage();
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
}
|
||||||
[data]
|
};
|
||||||
);
|
|
||||||
|
|
||||||
const editHotKey = useMemo(() => {
|
// CodeMirror 渲染
|
||||||
return (editor: CodeMirror.Editor, event: KeyboardEvent) => {
|
const codeMirrorRender = useMemo(() => (
|
||||||
if (event.ctrlKey && event.key === "s") {
|
<Controlled
|
||||||
fetchPage(editor.getValue());
|
className={styles.codeWrap}
|
||||||
event.preventDefault();
|
value={htmlContent}
|
||||||
}
|
options={{
|
||||||
};
|
mode: "xml",
|
||||||
}, [fetchPage]);
|
theme: "material",
|
||||||
|
lineNumbers: true,
|
||||||
const CodeMirrorRender = useMemo(() => {
|
}}
|
||||||
return (
|
onBeforeChange={handleCodeChange}
|
||||||
<Controlled
|
onCursor={handleCursorChange}
|
||||||
className={styles.codeWrap}
|
onKeyDown={handleEditorKeyDown}
|
||||||
value={data.data}
|
/>
|
||||||
options={{
|
), [htmlContent]);
|
||||||
mode: "xml",
|
|
||||||
theme: "material",
|
|
||||||
lineNumbers: true
|
|
||||||
}}
|
|
||||||
onBeforeChange={handleChange}
|
|
||||||
cursor={cursor}
|
|
||||||
onCursor={onCursorChange}
|
|
||||||
onKeyDown={editHotKey}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}, [cursor, data.data, editHotKey]);
|
|
||||||
|
|
||||||
const rect = useGetRect();
|
|
||||||
const height = useMemo(() => {
|
|
||||||
let res = rect.height - 42 - 1; //-1防止差值产生滚动条
|
|
||||||
return res < 694 ? 694 : res;
|
|
||||||
}, [rect.height]);
|
|
||||||
|
|
||||||
const phoneHeight = useMemo(() => {
|
|
||||||
//let res = rect.height - 42 - 30 - 1; //30是其上边距
|
|
||||||
//return res < 694 ? 694 : res;
|
|
||||||
return 694; //大屏幕过长,维持高度,需要变高另外处理
|
|
||||||
}, []);
|
|
||||||
const iframeHeight = useMemo(() => {
|
|
||||||
return phoneHeight - 30 - 12 - 12; //上边距30 上下padding 12
|
|
||||||
}, [phoneHeight]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
|
{/* Header */}
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<div className={styles.logoArea}>
|
<div className={styles.logoArea}>
|
||||||
<div className={styles.logo} title="Dooring">
|
<div className={styles.logo} title="Dooring">
|
||||||
@ -149,54 +148,40 @@ export default function() {
|
|||||||
<div className={styles.logoText}>| 在线代码编辑器</div>
|
<div className={styles.logoText}>| 在线代码编辑器</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.operationBar}>
|
<div className={styles.operationBar}>
|
||||||
<Button
|
<Button type="primary" title="保存(ctrl+s)" onClick={savePage} style={{ marginRight: "10px" }}>
|
||||||
type="primary"
|
|
||||||
title="保存(ctrl+s)"
|
|
||||||
onClick={() => fetchPage()}
|
|
||||||
style={{ marginRight: "10px" }}
|
|
||||||
>
|
|
||||||
<SaveOutlined />
|
<SaveOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button type="primary" onClick={downloadHtml} style={{ marginRight: "10px" }}>
|
||||||
type="primary"
|
|
||||||
onClick={downLoadHtml}
|
|
||||||
style={{ marginRight: "10px" }}
|
|
||||||
>
|
|
||||||
下载页面
|
下载页面
|
||||||
</Button>
|
</Button>
|
||||||
<Button danger onClick={downLoadHtml}>
|
<Button danger onClick={downloadHtml}>
|
||||||
一键部署
|
一键部署
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
className={styles.contentWrap}
|
{/* 内容区域 */}
|
||||||
style={{ height: `${height}px`, position: "relative" }}
|
<div className={styles.contentWrap} style={{ height: `${contentHeight}px`, position: "relative" }}>
|
||||||
>
|
{/* 代码编辑器 */}
|
||||||
<div
|
<div className={styles.codeWrap} style={{ height: `${contentHeight}px`, position: "relative" }}>
|
||||||
className={styles.codeWrap}
|
{codeMirrorRender}
|
||||||
style={{ height: `${height}px`, position: "relative" }}
|
|
||||||
>
|
|
||||||
{CodeMirrorRender}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
{/* 预览区域 */}
|
||||||
className={styles.previewWrap}
|
<div className={styles.previewWrap} style={{ height: `${phoneHeight}px` }}>
|
||||||
style={{ height: `${phoneHeight}px` }}
|
|
||||||
>
|
|
||||||
<iframe
|
<iframe
|
||||||
title="preview"
|
title="preview"
|
||||||
src={`${serverUrl}/html?flag=${isUpdate}`}
|
src={`${serverUrl}/html?flag=${isUpdated}`}
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: `${iframeHeight}px`,
|
height: `${iframeHeight}px`,
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
border: "none"
|
border: "none",
|
||||||
}}
|
}}
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user