🆕编辑器添加一键生成H5分享海报图功能 | The editor adds a one-click build H5 share poster chart feature

This commit is contained in:
xujiang 2020-11-18 13:11:47 +08:00
parent c27ca0cccd
commit c98dc26094
13 changed files with 331 additions and 503 deletions

View File

@ -11,9 +11,9 @@ export default defineConfig({
devtool: 'source-map', devtool: 'source-map',
antd: {}, antd: {},
title: '趣谈前端-h5-dooring', title: '趣谈前端-h5-dooring',
exportStatic: {}, // exportStatic: {},
base: '/', base: '/',
publicPath: './', publicPath: '/',
outputPath: 'dist', outputPath: 'dist',
esbuild: {}, esbuild: {},
routes: [ routes: [

View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

40
dist/help/index.html vendored
View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

40
dist/ide/index.html vendored
View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

6
dist/index.html vendored
View File

@ -8,7 +8,7 @@
type="image/vnd.microsoft.icon" type="image/vnd.microsoft.icon"
/> />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title> <title>H5编辑器之神-Dooring</title>
<meta <meta
name="description" name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。" content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
@ -24,7 +24,7 @@
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css" href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet" rel="stylesheet"
/> />
<link rel="stylesheet" href="./umi.css" /> <link rel="stylesheet" href="/umi.css" />
<script> <script>
window.routerBase = "/"; window.routerBase = "/";
</script> </script>
@ -35,6 +35,6 @@
<body> <body>
<div id="root"></div> <div id="root"></div>
<script src="./umi.js"></script> <script src="/umi.js"></script>
</body> </body>
</html> </html>

40
dist/login/index.html vendored
View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
href="http://io.nainor.com/uploads/logo_1742fd359da.png"
rel="shortcut icon"
type="image/vnd.microsoft.icon"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>趣谈前端-h5-dooring</title>
<meta
name="description"
content="Dooring是一款功能强大开源免费的H5可视化页面配置解决方案致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。"
/>
<meta
name="keywords"
content="H5,HTML5,javascript,react,nodejs,前端开发,github,开源"
/>
<meta name="author" content="徐小夕" />
<!-- <meta name="robots" content="noindex, nofollow"> -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://cdn.bootcdn.net/ajax/libs/spinkit/2.0.1/spinkit.min.css"
rel="stylesheet"
/>
<link rel="stylesheet" href="./umi.css" />
<script>
window.routerBase = "/";
</script>
<script>
//! umi version: 3.2.23
</script>
</head>
<body>
<div id="root"></div>
<script src="./umi.js"></script>
</body>
</html>

443
dist/umi.js vendored

File diff suppressed because it is too large Load Diff

2
dist/umi.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
import React, { useRef, memo, useMemo, useContext, useState, useEffect } from 'react'; import React, { useRef, memo, useMemo, useContext, useState, useEffect } from 'react';
import { Button, Input, Modal, Select, Upload } from 'antd'; import { Button, Input, Modal, Select, Upload, Tooltip, Badge } from 'antd';
import { import {
ArrowLeftOutlined, ArrowLeftOutlined,
MobileOutlined, MobileOutlined,
@ -12,6 +12,7 @@ import {
CodeOutlined, CodeOutlined,
SketchOutlined, SketchOutlined,
UploadOutlined, UploadOutlined,
InstagramOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { history } from 'umi'; import { history } from 'umi';
import QRCode from 'qrcode.react'; import QRCode from 'qrcode.react';
@ -38,6 +39,8 @@ interface HeaderComponentProps {
const HeaderComponent = memo((props: HeaderComponentProps) => { const HeaderComponent = memo((props: HeaderComponentProps) => {
const { pointData, location, clearData, undohandler, redohandler, importTpl } = props; const { pointData, location, clearData, undohandler, redohandler, importTpl } = props;
const [showModalIframe, setShowModalIframe] = useState(false); const [showModalIframe, setShowModalIframe] = useState(false);
const [showFaceModal, setShowFaceModal] = useState(false);
const [faceUrl, setFaceUrl] = useState('');
const iptRef = useRef<Input>(null); const iptRef = useRef<Input>(null);
const toPreview = () => { const toPreview = () => {
@ -205,6 +208,18 @@ const HeaderComponent = memo((props: HeaderComponentProps) => {
[], [],
); );
const generatePoster = () => {
localStorage.setItem('pointData', JSON.stringify(pointData));
setShowModalIframe(true);
setTimeout(() => {
setShowFaceModal(true);
}, 3600);
};
const handleReloadPage = () => {
document.getElementById('previewPage').contentWindow.location.reload();
};
const { setTheme } = useContext(dooringContext); const { setTheme } = useContext(dooringContext);
return ( return (
<div className={styles.header}> <div className={styles.header}>
@ -288,6 +303,18 @@ const HeaderComponent = memo((props: HeaderComponentProps) => {
<Button type="link" style={{ marginRight: '9px' }} title="重做" onClick={redohandler}> <Button type="link" style={{ marginRight: '9px' }} title="重做" onClick={redohandler}>
<RedoOutlined /> <RedoOutlined />
</Button> </Button>
<Tooltip placement="bottom" title="一键生成海报分享图">
<Badge dot offset={[-18, 10]}>
<Button
type="link"
style={{ marginRight: '6px' }}
onClick={generatePoster}
disabled={!pointData.length}
>
<InstagramOutlined />
</Button>
</Badge>
</Tooltip>
<Button type="link" onClick={toPreview} disabled={!pointData.length}> <Button type="link" onClick={toPreview} disabled={!pointData.length}>
</Button> </Button>
@ -323,19 +350,31 @@ const HeaderComponent = memo((props: HeaderComponentProps) => {
</Button> </Button>
</div> </div>
<Modal <Modal
title="正在生成封面..." title="生成封面...(长时间未反应请点右侧按钮重试)"
visible={showModalIframe} visible={showModalIframe}
footer={null} footer={null}
width={420} width={414}
closable={false} closeIcon={<RedoOutlined />}
destroyOnClose={true} destroyOnClose={true}
onCancel={handleReloadPage}
maskClosable={false}
> >
<iframe <iframe
title="editor" id="previewPage"
src={`/h5_plus/preview?tid=${props.location.query.tid}&gf=1`} src={`/preview?tid=${props.location.query.tid}&gf=1`}
style={{ width: '100%', border: 'none', height: '600px' }} style={{ width: '100%', border: 'none', height: '600px' }}
></iframe> ></iframe>
</Modal> </Modal>
<Modal
title="封面图(右键复制图片)"
visible={showFaceModal}
footer={null}
width={414}
destroyOnClose={true}
onCancel={() => setShowFaceModal(false)}
>
<img src={faceUrl} style={{ width: '100%' }} />
</Modal>
</div> </div>
); );
}); });

View File

@ -6,6 +6,7 @@ import req from '@/utils/req';
import styles from './index.less'; import styles from './index.less';
import { useGetScrollBarWidth } from '@/utils/tool'; import { useGetScrollBarWidth } from '@/utils/tool';
import { LocationDescriptorObject } from 'history-with-query'; import { LocationDescriptorObject } from 'history-with-query';
const isMac = navigator.platform.indexOf('Mac') === 0; const isMac = navigator.platform.indexOf('Mac') === 0;
interface PreviewPageProps { interface PreviewPageProps {
@ -33,20 +34,38 @@ const PreviewPage = memo((props: PreviewPageProps) => {
})); }));
}); });
const [pageData, setPageData] = useState(() => {
let pageConfigStr = localStorage.getItem('pageConfig');
let pageConfig;
try {
pageConfig = JSON.parse(pageConfigStr!) || {};
} catch (err) {
pageConfig = {};
}
return pageConfig;
});
const vw = window.innerWidth; const vw = window.innerWidth;
useEffect(() => { useEffect(() => {
const { tid, gf } = props.location.query!; const { tid, gf } = props.location.query!;
if (!gf) { if (!gf && parent.window.location.pathname === '/preview') {
req req
.get<any, PointDataItem[]>('/visible/preview/get', { params: { tid } }) .get<any, any>('/xxx/xxx/你的自定义接口地址', { params: { tid } })
.then(res => { .then(res => {
const { pageConfig, tpl } = res || { pageConfig: {}, tpl: [] };
// 设置标题
document.title = pageConfig.title || 'H5-Dooring | 强大的H5编辑神器';
// 设置数据源
setPointData( setPointData(
res.map(item => ({ tpl.map(item => ({
...item, ...item,
point: { ...item.point, isDraggable: false, isResizable: false }, point: { ...item.point, isDraggable: false, isResizable: false },
})), })),
); );
setPageData(pageConfig);
}) })
.catch(err => { .catch(err => {
console.error(err); console.error(err);
@ -62,27 +81,31 @@ const PreviewPage = memo((props: PreviewPageProps) => {
}, [props.location.query]); }, [props.location.query]);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const refImgDom = useRef<HTMLDivElement>(null);
const width = useGetScrollBarWidth(ref); const width = useGetScrollBarWidth(ref);
const pcStyle: CSSProperties = useMemo(() => { const pcStyle: CSSProperties = useMemo(() => {
return { return {
width: isMac ? 395 : 375 + width + 1, //小数会有偏差 width: isMac ? 382 : 375 + width + 1, //小数会有偏差
margin: '55px auto', margin: '55px auto',
height: '684px', height: '684px',
overflow: 'auto', overflow: 'auto',
position: 'relative', position: 'relative',
transform: 'scale(0.7) translateY(-80px)', transform: 'scale(0.7) translateY(-80px)',
backgroundColor: pageData.bgColor,
}; };
}, [width]); }, [width]);
const generateImg = (cb: any) => { const generateImg = (cb: any) => {
domtoimage domtoimage
.toBlob(ref.current) .toBlob(refImgDom.current, {
bgcolor: '#fff',
})
.then(function(blob: Blob) { .then(function(blob: Blob) {
const formData = new FormData(); const reader = new FileReader();
formData.append('file', blob, 'tpl.jpg'); reader.onload = function(e) {
req.post('/files/xxx', formData).then((res: any) => { cb && cb(e?.target?.result);
cb && cb(res.url); };
}); reader.readAsDataURL(blob);
}) })
.catch(function(error: any) { .catch(function(error: any) {
console.error('oops, something went wrong!', error); console.error('oops, something went wrong!', error);
@ -91,20 +114,35 @@ const PreviewPage = memo((props: PreviewPageProps) => {
return ( return (
<> <>
<div ref={ref} style={vw > 800 ? pcStyle : {}}> <div
<GridLayout ref={ref}
className={styles.layout} style={
cols={24} vw > 800
rowHeight={2} ? pcStyle
width={vw > 800 ? 375 : vw} : { height: '100vh', overflow: 'auto', backgroundColor: pageData.bgColor }
margin={[0, 0]} }
> >
{pointData.map((value: PointDataItem) => ( <div ref={refImgDom}>
<div className={styles.dragItem} key={value.id} data-grid={value.point}> <GridLayout
<DynamicEngine {...(value.item as any)} /> className={styles.layout}
</div> cols={24}
))} rowHeight={2}
</GridLayout> width={vw > 800 ? 375 : vw}
margin={[0, 0]}
style={{
backgroundColor: pageData.bgColor,
backgroundImage: pageData.bgImage ? `url(${pageData.bgImage[0].url})` : 'initial',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
}}
>
{pointData.map((value: PointDataItem) => (
<div className={styles.dragItem} key={value.id} data-grid={value.point}>
<DynamicEngine {...(value.item as any)} />
</div>
))}
</GridLayout>
</div>
</div> </div>
{vw > 800 ? ( {vw > 800 ? (