diff --git a/readme.md b/readme.md index 480f375..0ee5a46 100644 --- a/readme.md +++ b/readme.md @@ -1,9 +1,27 @@ -## H5-Visible-Tool +## H5-dooring -H5-Visible-Tool是一款功能强大,开源免费的H5可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。 +H5-Dooring是一款功能强大,开源免费的H5可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。技术栈以react为主, 后台采用nodejs开发。 + +## 已完成功能 +* 1. 组件库拖拽和显示 +* 2. 组件库动态编辑 +* 3. H5页面预览功能 +* 4. 保存H5页面配置文件 +* 5. 保存为模版 +* 6. 移动端跨端适配 +* 7. 媒体组件 + +## 正在完成功能 +* 添加模版库模块 +* 添加在线下载网站代码功能 +* 丰富组件库组件,添加可视化组件 +* 添加配置交互功能 +* 组件细分和代码优化 +* 添加typescript支持和单元测试 ## 持续升级 正在升级1.1版本,敬请期待... ## 技术反馈和交流 +微信:beautifulFront \ No newline at end of file diff --git a/src/.umi/core/history.ts b/src/.umi/core/history.ts index e990622..6d25739 100644 --- a/src/.umi/core/history.ts +++ b/src/.umi/core/history.ts @@ -2,7 +2,7 @@ import { createBrowserHistory } from '/Users/apple/Desktop/github/zhiku.tec/h5-visible-tool/node_modules/@umijs/runtime'; let options = { - "basename": "/" + "basename": "h5_plus" }; if ((window).routerBase) { options.basename = (window).routerBase; diff --git a/src/.umi/core/routes.ts b/src/.umi/core/routes.ts index eaee8f7..56ec8a6 100644 --- a/src/.umi/core/routes.ts +++ b/src/.umi/core/routes.ts @@ -14,13 +14,13 @@ export function getRoutes() { "exact": true }, { - "path": "/preview", - "component": dynamic({ loader: () => import(/* webpackChunkName: 'p__editor__preview' */'/Users/apple/Desktop/github/zhiku.tec/h5-visible-tool/src/pages/editor/preview'), loading: LoadingComponent}), + "path": "/login", + "component": dynamic({ loader: () => import(/* webpackChunkName: 'p__login' */'/Users/apple/Desktop/github/zhiku.tec/h5-visible-tool/src/pages/login'), loading: LoadingComponent}), "exact": true }, { - "path": "/prevH5", - "component": dynamic({ loader: () => import(/* webpackChunkName: 'p__editor__preH5' */'/Users/apple/Desktop/github/zhiku.tec/h5-visible-tool/src/pages/editor/preH5'), loading: LoadingComponent}), + "path": "/preview", + "component": dynamic({ loader: () => import(/* webpackChunkName: 'p__editor__preview' */'/Users/apple/Desktop/github/zhiku.tec/h5-visible-tool/src/pages/editor/preview'), loading: LoadingComponent}), "exact": true } ] diff --git a/src/assets/01.png b/src/assets/01.png deleted file mode 100644 index 45a427c..0000000 Binary files a/src/assets/01.png and /dev/null differ diff --git a/src/assets/02.png b/src/assets/02.png deleted file mode 100644 index 2e74a7c..0000000 Binary files a/src/assets/02.png and /dev/null differ diff --git a/src/assets/03.png b/src/assets/03.png deleted file mode 100644 index 9e25404..0000000 Binary files a/src/assets/03.png and /dev/null differ diff --git a/src/assets/04.png b/src/assets/04.png deleted file mode 100644 index 2f63b8d..0000000 Binary files a/src/assets/04.png and /dev/null differ diff --git a/src/assets/05.png b/src/assets/05.png deleted file mode 100644 index 99c40b6..0000000 Binary files a/src/assets/05.png and /dev/null differ diff --git a/src/assets/ballred.png b/src/assets/ballred.png deleted file mode 100644 index be1d28c..0000000 Binary files a/src/assets/ballred.png and /dev/null differ diff --git a/src/assets/ballwhite.png b/src/assets/ballwhite.png deleted file mode 100644 index 30fa49a..0000000 Binary files a/src/assets/ballwhite.png and /dev/null differ diff --git a/src/assets/code.png b/src/assets/code.png new file mode 100644 index 0000000..68f225a Binary files /dev/null and b/src/assets/code.png differ diff --git a/src/assets/first.png b/src/assets/first.png deleted file mode 100644 index b235e0b..0000000 Binary files a/src/assets/first.png and /dev/null differ diff --git a/src/assets/five.png b/src/assets/five.png deleted file mode 100644 index af65337..0000000 Binary files a/src/assets/five.png and /dev/null differ diff --git a/src/assets/four.png b/src/assets/four.png deleted file mode 100644 index e178a3c..0000000 Binary files a/src/assets/four.png and /dev/null differ diff --git a/src/assets/login_bg.png b/src/assets/login_bg.png new file mode 100644 index 0000000..ef727ee Binary files /dev/null and b/src/assets/login_bg.png differ diff --git a/src/assets/three.png b/src/assets/three.png deleted file mode 100644 index 4ea2d80..0000000 Binary files a/src/assets/three.png and /dev/null differ diff --git a/src/assets/two.png b/src/assets/two.png deleted file mode 100644 index 6635d33..0000000 Binary files a/src/assets/two.png and /dev/null differ diff --git a/src/components/DynamicEngine/components.js b/src/components/DynamicEngine/components.js index 86eed67..e6772ad 100644 --- a/src/components/DynamicEngine/components.js +++ b/src/components/DynamicEngine/components.js @@ -91,7 +91,7 @@ const List = memo((props) => { sourceData.map((item, i) => { return
- {item.desc} + {item.desc}
diff --git a/src/components/Tab/index.js b/src/components/Tab/index.js index 66b0191..47fc561 100644 --- a/src/components/Tab/index.js +++ b/src/components/Tab/index.js @@ -6,11 +6,33 @@ const { Panel } = Tabs; const XTab = (props) => { const { - tabs, + tabs = ['分类一', '分类二'], activeColor, color, fontSize, - sourceData + sourceData = [ + { + "title": "趣谈小课", + "desc": "致力于打造优质小课程", + "link": "xxxxx", + "type": 0, + "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" + }, + { + "title": "趣谈小课", + "desc": "致力于打造优质小课程", + "link": "xxxxx", + "type": 1, + "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" + }, + { + "title": "趣谈小课", + "desc": "致力于打造优质小课程", + "link": "xxxxx", + "type": 0, + "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" + } + ] } = props const tabWrapRef = useRef(null) diff --git a/src/components/Upload/index.js b/src/components/Upload/index.js index d4596a4..f8c04af 100644 --- a/src/components/Upload/index.js +++ b/src/components/Upload/index.js @@ -4,6 +4,8 @@ import { PlusOutlined } from '@ant-design/icons'; import ImgCrop from 'antd-img-crop'; import styles from './index.less'; +const isDev = process.env.NODE_ENV === 'development' + function getBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -62,8 +64,8 @@ class PicturesWall extends React.Component { render() { const { previewVisible, previewImage, fileList, previewTitle } = this.state; const { - // action换上你的服务器接口地址 - action = '', + // 配置自己的服务器地址 + action = isDev ? 'http://192.168.1.6:3000/api/xxx' : 'http://xxxx', headers, withCredentials = true, maxLen = 1 diff --git a/src/pages/editor/Container.js b/src/pages/editor/Container.js index 57b0ae7..c471e9e 100644 --- a/src/pages/editor/Container.js +++ b/src/pages/editor/Container.js @@ -1,16 +1,11 @@ -import React, { useState, useEffect, useRef, memo } from 'react' -import { Button, Input, Collapse, Slider, Empty, Popover, Modal, message } from 'antd' +import React, { useState, useEffect, memo } from 'react' +import { Input, Collapse, Slider, Empty } from 'antd' import { - ArrowLeftOutlined, PieChartOutlined, - ExpandOutlined, - MobileOutlined, - DownloadOutlined, - CopyOutlined + ExpandOutlined } from '@ant-design/icons' import { connect } from 'dva' -import QRCode from 'qrcode.react' -import { saveAs } from 'file-saver' +import HeaderComponent from './components/Header' import SourceBox from './SourceBox' import TargetBox from './TargetBox' import Calibration from 'components/Calibration' @@ -25,9 +20,6 @@ import styles from './index.less' const { Search } = Input; const { Panel } = Collapse; -const { confirm } = Modal; - -const isDev = process.env.NODE_ENV === 'development'; const Container = memo((props) => { const [ scaleNum , setScale ] = useState(1) @@ -37,8 +29,6 @@ const Container = memo((props) => { // 指定画布的id let canvasId = 'js_canvas' - const iptRef = useRef(null) - const backSize = () => { setScale(1) } @@ -47,14 +37,6 @@ const Container = memo((props) => { return
{ text }
} - const toPreview = () => { - localStorage.setItem('pointData', JSON.stringify(pointData)) - savePreview() - setTimeout(() => { - window.open(`/preview?tid=${props.location.query.tid}`) - }, 1000) - } - const handleSliderChange = (v) => { setScale(prev => v >= 150 ? 1.5 : (v / 100)) } @@ -81,42 +63,6 @@ const Container = memo((props) => { }) } - const content = () => { - const { tid } = props.location.query || '' - return - } - - const handleSaveTpl = () => { - confirm({ - title: '确定要保存吗?', - content:
-
- 模版名称: -
-
- 访问链接: -
-
, - okText: '保存', - cancelText: '取消', - onOk() { - let name = iptRef.current.state.value - req.post('/visible/tpl/save', { name, tpl: pointData }).then(res => { - console.log(res) - }) - }, - onCancel() { - console.log('Cancel'); - }, - }); - } - - const downLoadJson = () => { - const jsonStr = JSON.stringify(pointData) - const blob = new Blob([jsonStr], { type: "text/plain;charset=utf-8" }) - saveAs(blob, "template.json") - } - const savePreview = () => { const { tid } = props.location.query || '' req.post('/visible/preview', { tid, tpl: pointData }) @@ -128,24 +74,7 @@ const Container = memo((props) => { return (
-
-
-
-
Dooring
-
-
-
H5可视化编辑器
-
-
- - - - - - - -
-
+
diff --git a/src/pages/editor/components/Header/index.js b/src/pages/editor/components/Header/index.js new file mode 100644 index 0000000..03d00ef --- /dev/null +++ b/src/pages/editor/components/Header/index.js @@ -0,0 +1,112 @@ +import React, { useRef, memo } from 'react' +import { Button, Input, Popover, Modal } from 'antd' +import { + ArrowLeftOutlined, + MobileOutlined, + DownloadOutlined, + CopyOutlined +} from '@ant-design/icons' +import QRCode from 'qrcode.react' +import { saveAs } from 'file-saver' +import req from '@/utils/req' +import Code from '@/assets/code.png' + +import styles from './index.less' + +const { confirm } = Modal; + +const isDev = process.env.NODE_ENV === 'development'; + +const HeaderComponent = memo((props) => { + const { pointData, location } = props + const iptRef = useRef(null) + + const toPreview = () => { + localStorage.setItem('pointData', JSON.stringify(pointData)) + savePreview() + setTimeout(() => { + window.open(isDev ? `/preview?tid=${props.location.query.tid}` : `http://io.nainor.com/h5_plus/preview?tid=${props.location.query.tid}`) + }, 600) + } + + const content = () => { + const { tid } = location.query || '' + return + } + + const handleSaveTpl = () => { + confirm({ + title: '确定要保存吗?', + content:
+
+ 模版名称: +
+
+ 访问链接: +
+
, + okText: '保存', + cancelText: '取消', + onOk() { + let name = iptRef.current.state.value + req.post('/visible/tpl/save', { name, tpl: pointData }).then(res => { + console.log(res) + }) + }, + onCancel() { + console.log('Cancel'); + }, + }); + } + + const useTemplate = () => { + Modal.info({ + title: '该功能正在升级,可以关注下方公众号实时查看动态', + content: ( +
+ 趣谈前端 +
+ ), + okText: '客官知道啦' + }) + } + + const downLoadJson = () => { + const jsonStr = JSON.stringify(pointData) + const blob = new Blob([jsonStr], { type: "text/plain;charset=utf-8" }) + saveAs(blob, "template.json") + } + + const toLogin = () => { + const { tid } = props.location.query || '' + window.location.href = `/h5_plus/login?tid=${tid}` + } + + const savePreview = () => { + const { tid } = props.location.query || '' + req.post('/visible/preview', { tid, tpl: pointData }) + } + + return ( +
+
+
+
Dooring
+
+
+
H5可视化编辑器
+
+
+ + + + + + + +
+
+ ) +}) + +export default HeaderComponent diff --git a/src/pages/editor/components/Header/index.less b/src/pages/editor/components/Header/index.less new file mode 100644 index 0000000..d50ff14 --- /dev/null +++ b/src/pages/editor/components/Header/index.less @@ -0,0 +1,42 @@ +.header { + position: relative; + z-index: 10; + padding-left: 30px; + padding-right: 30px; + display: flex; + align-items: center; + height: 80px; + background: #fff; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + .logoArea { + width: 300px; + .backBtn { + display: inline-block; + padding: 12px 10px; + margin-right: 26px; + background-color: rgba(222, 224, 230, 0.3); + cursor: pointer; + } + .logo { + display: inline-block; + width: 105px; + font-size: 24px; + font-weight: bold; + img { + width: 100%; + } + } + } + .controlArea { + flex: 1; + text-align: center; + .tit { + font-size: 18px; + color: #000; + } + } + .btnArea { + width: 400px; + text-align: right; + } + } \ No newline at end of file diff --git a/src/pages/editor/models/editorModal.js b/src/pages/editor/models/editorModal.js index d224604..3e62fda 100644 --- a/src/pages/editor/models/editorModal.js +++ b/src/pages/editor/models/editorModal.js @@ -41,7 +41,28 @@ export default { } }, effects: { - + // 更新一条数据模型信息 + // *modifyDataModel({ payload }, { call, put }) { + // const modifyDataModel = yield call(mesService.modifyDataModel, payload) + // const activate = yield call(mesService.activateModifiedTableDataModel, modifyDataModel.dataModelId) + // const responseMessage = yield call(mesService.getDetailDataModel, { dataModelId: activate.dataModelId, showDataModelFieldFlag: true }) + // yield put({ + // type: 'receiveDetailDataModel', + // payload: responseMessage && responseMessage + // }) + // }, + + // 创建一条数据模型 + // *createDataModel({ payload }, { call, put }) { + // const responseMessage = yield call(mesService.createDataModel, payload) + // if (responseMessage.dataModelId) { + + // router.push({ + // pathname: '/dataModel/view', + // query: { id: responseMessage.dataModelId } + // }) + // } + // }, }, subscriptions: { setup({ dispatch, history }) { diff --git a/src/pages/editor/preview.js b/src/pages/editor/preview.js index dc77ac8..da4125f 100644 --- a/src/pages/editor/preview.js +++ b/src/pages/editor/preview.js @@ -34,7 +34,7 @@ const PreviewPage = memo((props) => { useEffect(() => { const { tid } = props.location.query req.get('/visible/preview/get', { params: { tid } }).then(res => { - setPointData(res) + setPointData(res.map(item => ({...item, point: {...item.point, isDraggable: false, isResizable: false } }))) }).catch(err => { setTimeout(() => { window.close() diff --git a/src/pages/login/index.less b/src/pages/login/index.less new file mode 100644 index 0000000..dcad791 --- /dev/null +++ b/src/pages/login/index.less @@ -0,0 +1,26 @@ +.loginWrap { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: #f0f0f0 url(../../assets/login_bg.png) center center; + background-size: cover; + .tit { + font-size: 35px; + text-align: center; + padding-bottom: 20px; + margin-bottom: 36px; + border-bottom: 1px solid #f0f0f0; + } + .formWrap { + margin-left: auto; + margin-right: auto; + width: 520px; + padding: 22px 0 20px; + background-color: #fff; + box-shadow: 0 0 20px rgba(0,0,0,.1); + border-radius: 6px; + } +} \ No newline at end of file diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx new file mode 100644 index 0000000..81d4963 --- /dev/null +++ b/src/pages/login/index.tsx @@ -0,0 +1,70 @@ +import { Form, Input, Button, Checkbox } from 'antd'; +import http from '@/utils/req'; +import { history } from 'umi'; +import styles from './index.less'; + +const layout = { + labelCol: { span: 6 }, + wrapperCol: { span: 16 }, +}; +const tailLayout = { + wrapperCol: { offset: 6, span: 16 }, +}; + +const Login = (props) => { + const onFinish = values => { + http.post('/login', {...values}).then(res => { + localStorage.setItem('token', res.token) + localStorage.setItem('user', values.username) + history.push('/') + }) + }; + + const onFinishFailed = errorInfo => { + console.log('Failed:', errorInfo); + }; + + return ( +
+
+
Doring开放平台登录
+ + + + + + + + + + + + + + +
+
+ + ); +}; + +export default Login diff --git a/src/utils/req.ts b/src/utils/req.ts index 651a907..0c268a2 100644 --- a/src/utils/req.ts +++ b/src/utils/req.ts @@ -4,7 +4,8 @@ import { message } from 'antd' const isDev = process.env.NODE_ENV === 'development' const instance = axios.create({ - baseURL: 'xxxxxxx', + // 服务器地址需要自己配置和开发 + baseURL: isDev ? 'http://localhost:3000/xxx' : 'http://xxxxx', timeout: 10000, withCredentials: true }); @@ -24,8 +25,8 @@ instance.interceptors.request.use(function (config) { // 添加响应拦截器 instance.interceptors.response.use(function (response) { - // 对响应数据做点什么, 这里是自定义的信息头,用来给前端说明展示的信息 - if(response.headers['x-x-x'] === 'xxx') { + // 对响应数据做点什么 + if(response.headers['x-show-msg'] === 'zxzk_msg_200') { message.success(response.data.msg); } return response.data.result;