Skip to content

Commit

Permalink
feat: 🚀 add a new form, pop-up window, and list of operation options
Browse files Browse the repository at this point in the history
  • Loading branch information
limuen committed May 28, 2024
1 parent 43b5839 commit 79b3e95
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 7 deletions.
3 changes: 3 additions & 0 deletions apps/react-low-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
"preview": "vite preview"
},
"dependencies": {
"ahooks": "^3.8.0",
"antd": "^5.15.3",
"dayjs": "^1.11.11",
"highlight.js": "^11.9.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.3"
Expand Down
46 changes: 46 additions & 0 deletions apps/react-low-code/src/components/CodeDrawer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { forwardRef, useImperativeHandle, useState } from "react";
import { Drawer } from "antd";
import highlight from "highlight.js/lib/core";
import typescript from "highlight.js/lib/languages/typescript";
import xml from "highlight.js/lib/languages/xml";
import "highlight.js/styles/atom-one-dark.min.css";

highlight.registerLanguage("typescript", typescript);
highlight.registerLanguage("xml", xml);

export interface CodeDrawerRef {
open: (code: string) => void;
}

export const CodeDrawer = forwardRef<CodeDrawerRef>((_props: any, ref) => {
const [open, setOpen] = useState(false);
const [content, setContent] = useState("");

useImperativeHandle(ref, () => ({
open(content: string) {
setOpen(true);
setContent(highlight.highlight(content, { language: "typescript" }).value);
}
}));

const onClose = () => {
setOpen(false);
};

return (
<Drawer title="代码预览" onClose={onClose} open={open} width={800}>
<div
style={{
background: "#282c34",
color: "#abb2bf",
padding: 16,
minHeight: "calc(100% - 32px)",
overflow: "auto",
boxShadow: "0 0 #0000,0 0 #0000,7px 7px 15px 0 #000"
}}
>
<pre dangerouslySetInnerHTML={{ __html: content }}></pre>
</div>
</Drawer>
);
});
51 changes: 51 additions & 0 deletions apps/react-low-code/src/components/LowCodeUI/ellipsis/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { ReactNode, useEffect, useRef, useState } from "react";
import { Tooltip } from "antd";

interface EllipsisProps {
/** 内容 */
title?: ReactNode;
children?: ReactNode;
[key: string]: any;
}

const Ellipsis: React.FC<EllipsisProps> = props => {
const { title, children, ...restProps } = props;
const titleNode = title || children;
const childrenNode = children || title;
const containerRef = useRef<HTMLDivElement>(null);
const textRef = useRef<HTMLDivElement>(null);
const [open, setOpen] = useState(false);
const [disabled, setDisabled] = useState(false);

const checkOverflow = () => {
if (containerRef.current && textRef.current) {
setDisabled(containerRef.current?.clientWidth >= textRef.current?.scrollWidth);
}
};

useEffect(() => {
checkOverflow();
window.addEventListener("resize", checkOverflow);
return () => {
window.removeEventListener("resize", checkOverflow);
};
}, []);

return (
<Tooltip title={titleNode} open={open} {...restProps}>
<div ref={containerRef} style={{ display: "grid" }}>
<div
ref={textRef}
onMouseEnter={() => setOpen(!disabled)}
onMouseLeave={() => setOpen(false)}
style={{ width: "100%" }}
className="text-overflow"
>
{childrenNode}
</div>
</div>
</Tooltip>
);
};

export default Ellipsis;
21 changes: 21 additions & 0 deletions apps/react-low-code/src/components/LowCodeUI/selection/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.selection-box {
position: relative;
display: flex;
align-items: center;
height: 32px;
padding: 10px;
.selection-icon {
display: inline-block;
width: 3px;
height: 18px;
background: $primary-color;
}
.selection-title {
display: inline-block;
padding-top: 2px;
margin-left: 10px;
font-size: 16px;
font-weight: 600;
color: $primary-color;
}
}
25 changes: 25 additions & 0 deletions apps/react-low-code/src/components/LowCodeUI/selection/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ReactNode } from "react";

export interface SelectionProps {
/** 标题 */
title: ReactNode;
/** 右上角自定义React节点 */
extra?: ReactNode;
/** 内容区域 */
children?: ReactNode;
}

const Selection: React.FC<SelectionProps> = ({ title, extra, children }) => {
return (
<>
<div className="selection-box">
<span className="selection-icon"></span>
<span className="selection-title">{title}</span>
<div style={{ position: "absolute", right: 0 }}>{extra}</div>
</div>
<div style={{ padding: "10px" }}>{children}</div>
</>
);
};

export default Selection;
58 changes: 58 additions & 0 deletions apps/react-low-code/src/components/Operations/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useRef } from "react";
import { Button, Space, Upload } from "antd";
import { StorageName } from "@/constants/storageNames";
import { downloadFile } from "@/utils";
import { CodeDrawer, CodeDrawerRef } from "../CodeDrawer";

interface OperationsProps {
type: StorageName;
saveTemplate: () => void;
previewCode: () => string;
exportCode: () => void;
setTemplateList: (templateList: any[]) => void;
}

const Operations: React.FC<OperationsProps> = ({ type, saveTemplate, previewCode, exportCode, setTemplateList }) => {
const codeDrawerRef = useRef<CodeDrawerRef>(null);

const handlePreview = () => {
if (codeDrawerRef.current) {
codeDrawerRef.current?.open(previewCode());
}
};

const handleExportTemplate = () => {
const template = localStorage.getItem(type) || "[]";
downloadFile(`${type}Config.json`, template);
};

const handleUpload = async (file: File) => {
const text = await file.text();
localStorage.setItem(type, text);
setTemplateList(JSON.parse(text));
return false;
};

return (
<Space style={{ marginBottom: 10 }}>
<Button type="primary" onClick={saveTemplate}>
保存
</Button>
<Button type="primary" onClick={handlePreview}>
预览
</Button>
<Button type="primary" onClick={exportCode}>
导出代码
</Button>
<Button type="primary" onClick={handleExportTemplate}>
导出模板
</Button>
<Upload accept=".json" showUploadList={false} beforeUpload={handleUpload}>
<Button type="primary">导入模板</Button>
</Upload>
<CodeDrawer ref={codeDrawerRef} />
</Space>
);
};

export default Operations;
10 changes: 10 additions & 0 deletions apps/react-low-code/src/constants/storageNames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum StorageNameMap {
component = "componentTemplate",
form = "formTemplate",
modal = "modalTemplate",
table = "tableTemplate"
}

export type StorageName = StorageNameMap.component | StorageNameMap.form | StorageNameMap.modal | StorageNameMap.table;

export const TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
1 change: 1 addition & 0 deletions apps/react-low-code/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "antd/dist/reset.css";
import "./style/index.less";

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
2 changes: 2 additions & 0 deletions apps/react-low-code/src/style/index.less
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import url("./variable.less");

/* global */

html,
Expand Down
9 changes: 9 additions & 0 deletions apps/react-low-code/src/style/variable.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@primary-color: #005dcc;

@title-color: #041c36;
@title-font-size: 24px;
@title-font-weight: 500;

@detail-label-font-size: 14px;
@detail-label-color: #86909c;
@detail-value-color: #1d2129;
22 changes: 22 additions & 0 deletions apps/react-low-code/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 下载文件
export const downloadFile = (fileName: string, content: string, fileType = "text/plain") => {
// 创建Blob对象表示要下载的数据
const blob = new Blob([content], { type: fileType });

// 创建一个指向Blob的URL
const url = URL.createObjectURL(blob);

// 创建隐藏的可下载链接
const link = document.createElement("a");
link.style.display = "none";
link.href = url;
link.download = fileName;

// 触发点击以下载文件
document.body.appendChild(link);
link.click();

// 清理
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
};
10 changes: 10 additions & 0 deletions apps/react-low-code/src/views/lowCode/form/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.hover-dashed-border:hover {
border: 1px dashed #cccccc;
}

.form-hover-padding {
padding: 10px 10px 1px;
&:hover {
padding: 9px 9px 0;
}
}
29 changes: 28 additions & 1 deletion apps/react-low-code/src/views/lowCode/form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
import { Col, Row } from "antd";
import Operations from "@/components/Operations";
import { StorageNameMap } from "@/constants/storageNames";
import "./index.less";

const FormComponent: React.FC = () => {
return <>FormComponent</>;
const saveTemplate = () => {};

const previewCode = () => {
return "代码预览";
};

const exportCode = () => {};

const setTemplateList = () => {};

return (
<Row style={{ height: "100%" }}>
<Col span={16} style={{ padding: "10px 15px 10px", borderRight: "1px solid #ccc" }}>
<Operations
type={StorageNameMap.form}
saveTemplate={saveTemplate}
previewCode={previewCode}
exportCode={exportCode}
setTemplateList={setTemplateList}
/>
</Col>
</Row>
);
};

export default FormComponent;
Loading

0 comments on commit 79b3e95

Please sign in to comment.