Showing preview only (1,684K chars total). Download the full file or copy to clipboard to get everything.
Repository: MrXujiang/h5-Dooring
Branch: master
Commit: 85a184a72bd9
Files: 236
Total size: 1.6 MB
Directory structure:
gitextract_819mm14l/
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ └── custom.md
├── .gitignore
├── .umirc.ts
├── .vscode/
│ └── settings.json
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── SECURITY.md
├── doc/
│ ├── .vuepress/
│ │ ├── config.js
│ │ ├── templates/
│ │ │ └── dev.html
│ │ └── theme/
│ │ ├── enhanceApp.js
│ │ ├── global-components/
│ │ │ └── Home.vue
│ │ ├── index.js
│ │ ├── layouts/
│ │ │ └── Layout.vue
│ │ └── styles/
│ │ ├── _layout.scss
│ │ ├── _reset.scss
│ │ ├── main.scss
│ │ └── palette.styl
│ ├── README.md
│ └── zh/
│ └── guide/
│ ├── README.md
│ ├── building.md
│ ├── componentDev/
│ │ ├── DSLAnalysis.md
│ │ ├── componentStructure.md
│ │ └── dynamicLoading.md
│ ├── deployDev/
│ │ ├── api.md
│ │ ├── deploy.md
│ │ ├── deploy_v6.md
│ │ ├── dir.md
│ │ ├── form.md
│ │ ├── https.md
│ │ ├── log.md
│ │ └── oss.md
│ ├── directoryStructure.md
│ ├── functionRealization/
│ │ ├── download.md
│ │ ├── machinePreview.md
│ │ ├── pagePreview.md
│ │ ├── revocation.md
│ │ ├── saveJson.md
│ │ ├── screenshot.md
│ │ └── templateLibrary.md
│ ├── introduced.md
│ └── startedQuickly.md
├── package.json
├── readme.md
├── server.js
├── src/
│ ├── app.tsx
│ ├── components/
│ │ ├── Calibration/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── ErrorBundaries/
│ │ │ └── index.tsx
│ │ ├── FormComponents/
│ │ │ ├── CardPicker/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Color/
│ │ │ │ └── index.tsx
│ │ │ ├── DataList/
│ │ │ │ ├── editorModal.tsx
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── FormItems/
│ │ │ │ ├── EditorModal.tsx
│ │ │ │ ├── FormItems.tsx
│ │ │ │ ├── formItems.less
│ │ │ │ └── index.tsx
│ │ │ ├── MutiText/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Pos/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Table/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── Upload/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── XEditor/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ └── types.ts
│ │ ├── LoadingCp/
│ │ │ └── index.tsx
│ │ └── Zan/
│ │ ├── index.less
│ │ └── index.tsx
│ ├── core/
│ │ ├── DynamicEngine.tsx
│ │ ├── index.ts
│ │ └── renderer/
│ │ ├── FormRender.tsx
│ │ ├── ViewRender.tsx
│ │ └── viewRender.less
│ ├── global.css
│ ├── layouts/
│ │ ├── index.less
│ │ └── index.tsx
│ ├── materials/
│ │ ├── base/
│ │ │ ├── Carousel/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Form/
│ │ │ │ ├── BaseForm.tsx
│ │ │ │ ├── BasePopoverForm.tsx
│ │ │ │ ├── baseForm.less
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Header/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Icon/
│ │ │ │ ├── icon.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Image/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── List/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── LongText/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Notice/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Qrcode/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── RichText/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Tab/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Text/
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── WhiteTpl/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── common.ts
│ │ ├── media/
│ │ │ ├── Audio/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Calendar/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Map/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Video/
│ │ │ │ ├── index.css
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── schema.ts
│ │ ├── shop/
│ │ │ ├── CardLabel/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Coupons/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── List/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── Tab/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── ZhuanLan/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── schema.ts
│ │ │ │ └── template.ts
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ └── visual/
│ │ ├── Area/
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── Chart/
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── Line/
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── Pie/
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── XProgress/
│ │ │ ├── index.tsx
│ │ │ ├── schema.ts
│ │ │ └── template.ts
│ │ ├── schema.ts
│ │ └── template.ts
│ ├── pages/
│ │ ├── document.ejs
│ │ ├── editor/
│ │ │ ├── Container.tsx
│ │ │ ├── SourceBox.tsx
│ │ │ ├── TargetBox.tsx
│ │ │ ├── components/
│ │ │ │ ├── CanvasControl/
│ │ │ │ │ ├── index.less
│ │ │ │ │ └── index.tsx
│ │ │ │ └── Header/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── index.js
│ │ │ ├── index.less
│ │ │ ├── models/
│ │ │ │ ├── editorModal.js
│ │ │ │ └── editorPcModel.ts
│ │ │ ├── preview.tsx
│ │ │ └── services/
│ │ │ └── editorService.js
│ │ ├── help/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── home/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── ide/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── login/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── mobileTip.tsx
│ │ └── user/
│ │ ├── base.less
│ │ ├── index.less
│ │ └── index.tsx
│ ├── typings.d.ts
│ ├── utils/
│ │ ├── req.ts
│ │ └── tool.ts
│ └── video-react.d.ts
├── tsconfig.json
├── webpack.config.js
├── website/
│ ├── css/
│ │ ├── frameworks.css
│ │ └── style.css
│ ├── index.html
│ └── js/
│ ├── jquery.js
│ ├── main.js
│ └── plugins.js
└── zh.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
# support simple dooring !
# weclome to try this repo.
open_collective: h5-dooring
================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
.umi/
.umi-production/
# production
/dist
/doc-dist
================================================
FILE: .umirc.ts
================================================
import path from "path";
import { defineConfig } from "umi";
export default defineConfig({
dynamicImport: {
loading: "@/components/LoadingCp"
},
dva: {
immer: true
},
devtool: "source-map",
antd: {},
title: "趣谈前端-h5-dooring",
// exportStatic: {},
base: "/",
publicPath: "/",
outputPath: "dist",
esbuild: {},
routes: [
{
exact: false,
path: "/",
component: "@/layouts/index",
routes: [
{
path: "/",
component: "../pages/home"
},
{
path: "/editor",
component: "../pages/editor"
},
{
path: "/ide",
component: "../pages/ide"
},
{
path: "/help",
component: "../pages/help"
},
{
path: "/login",
component: "../pages/login"
},
{
path: "/mobileTip",
component: "../pages/mobileTip"
},
{
path: "/preview",
component: "../pages/editor/preview"
}
]
}
],
theme: {
"primary-color": "#2F54EB"
// "btn-primary-bg": "#2F54EB"
},
extraBabelPlugins: [["import", { libraryName: "zarm", style: true }]],
// sass: {},
alias: {
components: path.resolve(__dirname, "src/components/"),
utils: path.resolve(__dirname, "src/utils/"),
assets: path.resolve(__dirname, "src/assets/")
}
});
================================================
FILE: .vscode/settings.json
================================================
{
"workbench.colorCustomizations": {
"activityBar.background": "#152B60",
"titleBar.activeBackground": "#1D3C86",
"titleBar.activeForeground": "#FBFCFE"
},
"editor.snippetSuggestions": "bottom",
"editor.suggest.snippetsPreventQuickSuggestions": true
}
================================================
FILE: CHANGELOG.md
================================================
## 私有化授权版更新日志
[在线地址](http://h5.dooring.cn/h5_plus)
#### 2.50
1. 复制页面添加数量校验
2. 后台系统模版列表添加“上推荐”按钮
3. 精选模版支持分类,分页
4. 编辑器页面我的页面支持分页, 继续编辑, 按钮放到标题右边
5. 优化前进后退组件配置项文案
6. 解决画布缩放导致的组件拖拽抖动
#### 2.40
1. 图片组件添加查看大图功能
2. 添加组件选中状态及样式优化
3. 添加功能类组件(统计组件)
4. 右键菜单体验优化
5. 删除服务端无用路由,降低内耗
#### 2.30
1. 优化用户注册机制
2. 组件动画添加延迟时间, 添加旋转动画, 优化动画逻辑
3. 优化用户登录态失效逻辑
4. 统计组件
5. 图片库弹窗层级优化
6. 服务器增加数据访问权限, 保障数据安全
#### 2.15
1. 编辑器支持调试模式(支持本地环境一键debug, 支持真机调试)
2. Tab组件支持布局(单列 / 双列)
3. header组件升级, 支持顶部固定, 自定义头部菜单
4. 添加1个行业模版
5. 优化登录逻辑, 自动去空格
6. 编辑器入口页面优化, 重构
7. 表单组件提交逻辑优化,修复参数错误提示
8. 支持自定义上传组件, 组件预览, 审批, 组件管理
#### 2.10
1. 添加入口页加群指引动画, 优化左侧界面
2. 优化拖拽更新算法, 使得拖拽精度达到99.99%, 真正的所搭即所得
3. 删除模版库无用模版, 创建2个新模版
4. 编辑器添加智能引导页面
5. 编辑器支持调试模式(支持本地环境一键debug, 支持真机调试)
6. Tab组件支持布局(单列 / 双列)
#### 2.01
1. 优化编辑器加载性能
2. iframe容器组件添加边框等属性
3. 富文本组件添加背景色配置
4. 修复真机预览时空数据还能显示二维码bug
5. 优化页面高度适配问题, 添加高度适配器
6. 优化组件交互时空链接点击出现message bug
7. 更新dooring文档
#### 2.0
1. 完善数据源功能
2. 轮播图/图片列表/List/文字跑马灯/横向滚动组件数据源对接完毕
3. 组件库支持搜索功能
4. 组件列表支持分页, 提高渲染性能
5. 赞助墙交互优化
6. 界面局部优化
#### 1.99
1. 添加数据源功能
2. 视频组件支持封面图
3. 优化页面DSL结构, 降低了jsonSchema体积30%-50%
4. 官网优化
5. 管理后台替换logo, 部分文案信息
6. 添加图片列表组件
7. 轮播图支持一键绑定数据源
#### 1.98
1. 编辑器功能区添加更多折叠下拉框, 优化头部界面
2. 添加数据源入口和界面
3. 模版库优化, 剔除无用模版, 累计60+模版
4. 入口页添加赞助墙
5. 升级视频组件, 支持弹幕, 截屏, 模式设置等功能
6. 文件上传路径兼容window服务器本地化部署
#### 1.96
1. 修复首页推荐项目外链地址和站内文案
2. 替换Dooring网站logo
3. 优化ios8以下访问H5时可能出现的页面卡顿问题
4. 图片上传组件添加svg, gif图片格式支持
5. 后台管理系统添加一键跳编辑器按钮
6. 服务端编辑侧路由加固
7. 文件上传组件添加自定义上传文档,支持七牛云,腾讯云,阿里oss等第三方图床方式
#### 1.95
1. dooring文档添加更新日志模块
2. dooring增报错监控函数, 提供一键清空缓存按钮和自动重载功能
3. 新增电商商品H5模版
4. 页面配置增加背景模式和背景重复
5. 表单添加字段名配置项
#### 1.94
1. 转盘组件支持转盘交互功能(跳转链接/打开弹窗/自定义代码)
2. 添加网站拦截, 防止页面误关导致页面无法保存
3. 优化页面控制条组件样式
4. 按钮组件添加组件动画
5. 图片组件添加组件动画
6. 媒体组件icon优化
7. 全局错误监控组件添加一键清除缓存功能
#### 1.93
1. 上线源码下载功能
2. 服务端支持下载源码服务和下载次数限制
3. 界面部分文案优化
4. 出码基座优化
5. 抽奖组件支持抽奖后自定义交互(弹窗/链接/自定义代码)
#### 1.92
1. 修复背景图预览适配问题
2. 转盘组件支持中奖后自定义交互/弹窗/自定义代码
3. 界面局部调整
4. 后台管理表单数据支持多键查询
5. 可视化大屏柱状图组件支持实时数据请求
================================================
FILE: Dockerfile
================================================
FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/default.conf
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
================================================
FILE: doc/.vuepress/config.js
================================================
module.exports = {
base: "/doc/",
title: "h5-dooring",
dest: "./doc-dist",
themeConfig: {
search: false,
searchMaxSuggestions: 10,
lastUpdated: "Last Updated",
nav: [
{ text: "首页", link: "/" },
{ text: "文档", link: "/zh/guide/" },
// { text: '1.X', link: '/zh/guide/' },
// { text: '语言', link: '/zh/guide/' },
{ text: "体验", link: "http://h5.dooring.cn" },
{ text: "github", link: "https://github.com/MrXujiang/h5-Dooring" }
],
sidebar: [
{
title: "基本介绍",
path: "/zh/guide/",
collapsable: false,
sidebarDepth: 1
},
{
title: "doring如何工作",
path: "/zh/guide/introduced",
collapsable: false,
sidebarDepth: 1
},
{
title: "快速上手",
path: "/zh/guide/startedQuickly",
collapsable: false,
sidebarDepth: 1
},
{
title: "目录结构",
path: "/zh/guide/directoryStructure",
collapsable: false,
sidebarDepth: 1
},
{
title: "组件开发",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
name: "componentStructure",
title: "组件结构",
path: "/zh/guide/componentDev/componentStructure",
collapsable: false,
sidebarDepth: 2
},
{
name: "DSLAnalysis",
title: "DSL设计",
path: "/zh/guide/componentDev/DSLAnalysis",
collapsable: false,
sidebarDepth: 2
},
{
name: "dynamicLoading",
title: "动态加载",
path: "/zh/guide/componentDev/dynamicLoading",
collapsable: false,
sidebarDepth: 1
}
]
},
{
title: "功能实现",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
title: "模板库",
path: "/zh/guide/functionRealization/templateLibrary",
collapsable: false,
sidebarDepth: 1
},
{
title: "保存json",
path: "/zh/guide/functionRealization/saveJson",
collapsable: false,
sidebarDepth: 1
},
{
title: "下载源码",
path: "/zh/guide/functionRealization/download",
collapsable: false,
sidebarDepth: 1
},
{
title: "网页预览",
path: "/zh/guide/functionRealization/pagePreview",
collapsable: false,
sidebarDepth: 1
},
{
title: "真机预览",
path: "/zh/guide/functionRealization/machinePreview",
collapsable: false,
sidebarDepth: 1
},
{
title: "撤销/重做",
path: "/zh/guide/functionRealization/revocation",
collapsable: false,
sidebarDepth: 1
},
{
title: "截图功能",
path: "/zh/guide/functionRealization/screenshot",
collapsable: false,
sidebarDepth: 1
}
]
},
{
title: "私有化部署和二次开发",
collapsable: false,
sidebarDepth: 1,
type: "group",
children: [
{
title: "私有化部署",
path: "/zh/guide/deployDev/deploy",
collapsable: false,
sidebarDepth: 1
},
{
title: "v6.dooring私有化部署(临时)",
path: "/zh/guide/deployDev/deploy_v6",
collapsable: false,
sidebarDepth: 1
},
{
title: "服务端数据说明",
path: "/zh/guide/deployDev/dir",
collapsable: false,
sidebarDepth: 1
},
{
title: "支持https",
path: "/zh/guide/deployDev/https",
collapsable: false,
sidebarDepth: 1
},
{
title: "接入第三方oss",
path: "/zh/guide/deployDev/oss",
collapsable: false,
sidebarDepth: 1
},
{
title: "获取Form组件的值数据",
path: "/zh/guide/deployDev/form",
collapsable: false,
sidebarDepth: 1
},
{
title: "API接口文档",
path: "/zh/guide/deployDev/api",
collapsable: false,
sidebarDepth: 1
},
{
title: "更新日志",
path: "/zh/guide/deployDev/log",
collapsable: false,
sidebarDepth: 1
}
]
}
]
}
};
================================================
FILE: doc/.vuepress/templates/dev.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="/favcion.png" rel="shortcut icon" type="image/vnd.microsoft.icon" />
<title>dooring-前端开发手册</title>
</head>
<body>
<noscript>
<strong
>We're sorry but winning-components doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
================================================
FILE: doc/.vuepress/theme/enhanceApp.js
================================================
import "./styles/main.scss";
// 使用异步函数也是可以的
export default ({
Vue, // VuePress 正在使用的 Vue 构造函数
options, // 附加到根实例的一些选项
router, // 当前应用的路由实例
siteData, // 站点元数据
isServer // 当前应用配置是处于 服务端渲染 或 客户端
}) => {
// ...做一些其他的应用级别的优化
// console.log({ Vue, options, router, siteData, isServer });
Vue.config.ignoredElements = [
// Use a `RegExp` to ignore all elements that start with "nova-"
// 2.5+ only
//^nova-/,
// /img/,
];
};
================================================
FILE: doc/.vuepress/theme/global-components/Home.vue
================================================
<template>
<main class="home" :aria-labelledby="data.heroText !== null ? 'main-title' : null">
<header class="hero">
<h1 v-if="data.heroText !== null" id="main-title">
<img src="../imgs/common/logo.svg" :alt="data.heroAlt || 'hero'" /><span>{{
data.heroText || $title || "Hello"
}}</span>
</h1>
</header>
<div v-if="data.features && data.features.length" class="features">
<div class="container">
<div v-for="(feature, index) in data.features" :key="index" class="feature">
<div class="feature-index">{{ index + 1 }}</div>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
</div>
</div>
<Content class="theme-default-content custom" />
<div class="hero">
<p v-if="data.actionText && data.actionLink" class="action">
<NavLink class="action-button" :item="actionLink" />
</p>
</div>
<div v-if="data.footer" class="footer">
{{ data.footer }}
</div>
</main>
</template>
<script>
import NavLink from "@vuepress/theme-default/components/NavLink.vue";
export default {
name: "Home",
components: { NavLink },
computed: {
data() {
return this.$page.frontmatter;
},
actionLink() {
return {
link: this.data.actionLink,
text: this.data.actionText,
};
},
},
};
</script>
<style lang="stylus">
$accentColor =#083ac4
$homePageWidth = 1080px
.container
max-width $homePageWidth
margin 0px auto
display block
.home
padding $navbarHeight 0
display block
max-width initial
margin initial
background-color #f5f8fe
.hero
text-align center
img
width: 190px
height 50px
display inline-block
margin initial
margin-bottom 0
margin-right 20px
vertical-align sub
h1
font-size 1.5rem
font-weight 600
h1, .description, .action
margin 1.8rem auto
.description
max-width 35rem
font-size 1.6rem
line-height 1.3
color lighten($textColor, 40%)
.action-button
display inline-block
font-size 1rem
color #fff
background-color $accentColor
padding 0.4rem 2rem
border-radius 4px
transition background-color .1s ease
box-sizing border-box
border-bottom 1px solid darken($accentColor, 10%)
&:hover
background-color lighten($accentColor, 10%)
.features
border-top 1px solid $borderColor
padding 80px 0
background-color #fff
margin-top 2.5rem
.container
display flex
flex-wrap wrap
align-items flex-start
align-content stretch
justify-content space-between
.feature
flex-grow 1
flex-basis 30%
max-width 27%
border-radius 8px
padding 20px 22px
box-shadow 0 0 8px rgba(0,0,0,.1)
.feature-index
border-radius 4px
background-color #083ac4
width 50px
line-height 50px
color #fff
font-size 20px
text-align center
margin-bottom 6px
box-shadow 1px 0 0 0 rgba(0,0,0,0.1)
h2
font-size 1.4rem
font-weight 500
border-bottom none
padding-bottom 0
color lighten($textColor, 10%)
p
color lighten($textColor, 25%)
.footer
padding 2.5rem
border-top 1px solid $borderColor
text-align center
color lighten($textColor, 25%)
@media (max-width: $MQMobile)
.home
.features
flex-direction column
.feature
max-width 100%
padding 0 2.5rem
@media (max-width: $MQMobileNarrow)
.home
padding-left 1.5rem
padding-right 1.5rem
.hero
img
max-height 210px
margin 2rem auto 1.2rem
h1
font-size 2rem
h1, .description, .action
margin 1.2rem auto
.description
font-size 1.2rem
.action-button
font-size 1rem
padding 0.6rem 1.2rem
.feature
h2
font-size 1.25rem
</style>
================================================
FILE: doc/.vuepress/theme/index.js
================================================
module.exports = {
extend: "@vuepress/theme-default"
};
================================================
FILE: doc/.vuepress/theme/layouts/Layout.vue
================================================
<template>
<div
class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
>
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar" />
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
<template #top>
<slot name="sidebar-top" />
</template>
<template #bottom>
<slot name="sidebar-bottom" />
</template>
</Sidebar>
<Home v-if="$page.frontmatter.home" />
<Page v-else :sidebar-items="sidebarItems">
<template #top>
<slot name="page-top" />
</template>
<template #bottom>
<slot name="page-bottom" />
</template>
</Page>
</div>
</template>
<script>
import Navbar from "@parent-theme/components/Navbar.vue";
import Page from "@parent-theme/components/Page.vue";
import Sidebar from "@parent-theme/components/Sidebar.vue";
import { resolveSidebarItems } from "@parent-theme/util";
export default {
name: "Layout",
components: {
Page,
Sidebar,
Navbar,
},
data() {
return {
isSidebarOpen: false,
};
},
computed: {
shouldShowNavbar() {
const { themeConfig } = this.$site;
const { frontmatter } = this.$page;
if (frontmatter.navbar === false || themeConfig.navbar === false) {
return false;
}
return (
this.$title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav ||
this.$themeLocaleConfig.nav
);
},
shouldShowSidebar() {
const { frontmatter } = this.$page;
return !frontmatter.home && frontmatter.sidebar !== false && this.sidebarItems.length;
},
sidebarItems() {
return resolveSidebarItems(
this.$page,
this.$page.regularPath,
this.$site,
this.$localePath
);
},
pageClasses() {
const userPageClass = this.$page.frontmatter.pageClass;
return [
{
"no-navbar": !this.shouldShowNavbar,
"sidebar-open": this.isSidebarOpen,
"no-sidebar": !this.shouldShowSidebar,
},
userPageClass,
];
},
},
mounted() {
this.$router.afterEach(() => {
this.isSidebarOpen = false;
});
},
methods: {
toggleSidebar(to) {
this.isSidebarOpen = typeof to === "boolean" ? to : !this.isSidebarOpen;
this.$emit("toggle-sidebar", this.isSidebarOpen);
},
// side swipe
onTouchStart(e) {
this.touchStart = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY,
};
},
onTouchEnd(e) {
const dx = e.changedTouches[0].clientX - this.touchStart.x;
const dy = e.changedTouches[0].clientY - this.touchStart.y;
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
if (dx > 0 && this.touchStart.x <= 80) {
this.toggleSidebar(true);
} else {
this.toggleSidebar(false);
}
}
},
},
};
</script>
================================================
FILE: doc/.vuepress/theme/styles/_layout.scss
================================================
$accentColor: #083ac4;
.nav-link.external {
.outbound {
display: none;
}
}
.nav-link.external:last-child {
color: #fff;
background-color: $accentColor;
border-color: $accentColor;
padding: 6px 14px;
border-radius: 4px;
}
.doc-main {
padding-top: 88px;
padding-left: 380px;
}
================================================
FILE: doc/.vuepress/theme/styles/_reset.scss
================================================
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/
// * {
// box-sizing: border-box;
// margin: 0;
// padding: 0;
// }
html {
line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers (opinionated).
*/
body {
margin: 0;
padding: 0;
}
/**
* Add the correct display in IE 9-.
*/
article,
aside,
footer,
header,
nav,
section {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1,
h2,
h3,
h4,
h5 {
margin: 0;
padding: 0;
}
/**
* Correct the font size and margin on `p` elements
*/
/* Grouping content
========================================================================== */
/**
* Add the correct display in IE 9-.
* 1. Add the correct display in IE.
*/
figcaption,
figure,
main {
/* 1 */
display: block;
}
/**
* Add the correct margin in IE 8.
*/
figure {
margin: 0;
padding: 0;
}
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* 1. Remove the gray background on active links in IE 10.
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/
a {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
color: #329aff;
}
/**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/
b,
strong {
font-weight: inherit;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font style in Android 4.3-.
*/
dfn {
font-style: italic;
}
/**
* Add the correct background and color in IE 9-.
*/
mark {
background-color: #ff0;
color: #000;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
audio,
video {
display: inline-block;
}
/**
* Add the correct display in iOS 4-7.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Remove the border on images inside links in IE 10-.
*/
img {
border-style: none;
}
/**
* Hide the overflow in IE.
*/
svg:not(:root) {
overflow: hidden;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers (opinionated).
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: sans-serif; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
padding: 0;
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
outline: none;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
button,
html [type="button"], /* 1 */
[type="reset"],
[type="submit"] {
-webkit-appearance: button; /* 2 */
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* 1. Add the correct display in IE 9-.
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Remove the default vertical scrollbar in IE.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in IE 9-.
* 1. Add the correct display in Edge, IE, and Firefox.
*/
details, /* 1 */
menu {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Scripting
========================================================================== */
/**
* Add the correct display in IE 9-.
*/
canvas {
display: inline-block;
}
/**
* Add the correct display in IE.
*/
template {
display: none;
}
/* Hidden
========================================================================== */
/**
* Add the correct display in IE 10-.
*/
[hidden] {
display: none;
}
input {
outline: none;
}
/*reset browser scroll style*/
html,
body {
scrollbar-arrow-color: rgba(209, 213, 219, 1); /**/ /*三角箭头的颜色*/
scrollbar-face-color: rgba(209, 213, 219, 1); /**/ /*立体滚动条的颜色*/
scrollbar-3dlight-color: rgba(209, 213, 219, 1); /**/ /*立体滚动条亮边的颜色*/
scrollbar-highlight-color: rgba(209, 213, 219, 1); /**/ /*滚动条空白部分的颜色*/
scrollbar-shadow-color: rgba(209, 213, 219, 1); /**/ /*立体滚动条阴影的颜色*/
scrollbar-darkshadow-color: rgba(209, 213, 219, 1); /**/ /*立体滚动条强阴影的颜色*/
scrollbar-track-color: rgba(209, 213, 219, 1); /**/ /*立体滚动条背景颜色*/
scrollbar-base-color: rgba(244, 245, 249, 1); /**/ /*滚动条的基本颜色*/
}
/* chrome scroll style */
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
/*定义滚动条轨道 内阴影+圆角*/
::-webkit-scrollbar-track {
background-color: transparent;
}
/*定义滑块*/
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(209, 213, 219, 1);
}
ul li {
// list-style: none;
padding: 0;
margin: 0;
}
dl,
dt,
dd {
margin: 0;
padding: 0;
}
button {
cursor: pointer;
border: none;
}
button[disabled] {
cursor: not-allowed;
}
================================================
FILE: doc/.vuepress/theme/styles/main.scss
================================================
@import 'reset';
@import 'layout';
================================================
FILE: doc/.vuepress/theme/styles/palette.styl
================================================
$accentColor= #083ac4;
================================================
FILE: doc/README.md
================================================
---
content: Home
home: true
#heroImage: ../imgs/common/logo.svg
heroText: 一款所见即所得的H5编辑器
features:
- title: 简洁方便
details: 任何人只需傻瓜式拖拽或进行简单编辑即可生成精美的H5页面
- title: 插拔式体验
details: 产品以GPL协议开源, 授权后可植入任何系统,并支持二次开发
- title: 持续迭代,无限可能
details: 目前正在持续迭代中,后续可根据需求开发功能更强大的可视化系统
actionText: 快速上手 →
actionLink: /zh/guide/
footer: GPL Licensed | Copyright © 2020-present H5-Dooring
---
================================================
FILE: doc/zh/guide/README.md
================================================
<img src="../../img/common/logo.svg" alt="foo">
H5-Dooring 是一款功能强大,高可扩展的 H5 可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳实践。
## 功能特点
🎉 **可扩展,** Dooring 实现了较为完整的业务闭环,并使其模块化,编辑器内部功能接口也全部可以对接不同服务端语言,实现了标准化接口。此外还支持自定义组件,二次开发,设计模板等能力,以满足功能和跨领域的分层需求。
📦 **开箱即用,** Dooring 内置了**表单渲染器、页面渲染器、动态加载内核**等,仅需一套源码即可上手开发。并且还提供针对 React 的定制插件,内涵丰富的功能,可满足日常 80%的页面制作需求。
🚀 **大量自研,** 包含整个编辑器架构、组件设计、文档、请求库封装,后台管理系统等,满足日常项目的周边需求。
🚄 **与时俱进,** 在满足需求的同时,我们也不会停止对新技术的探索。比如更多**营销组件、业务功能,后台管理可视化,PC 页面编辑器,数据大屏定制**等等。
## 为什么选择 Dooring
目前**github**已超过 3000+star,上线 2 个月累计 500+用户使用,解决完善了 100+问题,后续会持续迭代,更新,自研优秀,先进的 lowcode/nocode 解决方案。
================================================
FILE: doc/zh/guide/building.md
================================================
正在建设中...
================================================
FILE: doc/zh/guide/componentDev/DSLAnalysis.md
================================================
# DSL 设计
DSL 层主要约定了 Dooring 组件的数据协议,包括组件的可编辑属性、编辑类型、初始值等,之所以定义一致的协议层,主要是方便后期的组件扩展,配置后移,有助于不同后端语言开发和数据存储,接下来我们看看 header 组件的 schema。
1.editData 可编辑的属性类型 DSL
2.config 可编辑组件的默认属性
```js
const Header: IHeaderSchema = {
editData: [
{
key: "bgColor",
name: "背景色",
type: "Color"
},
{
key: "height",
name: "高度",
type: "Number"
},
{
key: "logo",
name: "logo",
type: "Upload",
isCrop: true,
cropRate: 1000 / 618
},
{
key: "logoText",
name: "logo文字",
type: "Text"
},
{
key: "color",
name: "文字颜色",
type: "Color"
},
{
key: "fontSize",
name: "文字大小",
type: "Number"
}
],
config: {
bgColor: "rgba(245,245,245,1)",
logo: [
{
uid: "001",
name: "image.png",
status: "done",
url: `${serverUrl}/uploads/3_1740be8a482.png`
}
],
logoText: "页头Header",
fontSize: 20,
color: "rgba(47,84,235,1)",
height: 50
}
};
```
由以上代码可知,我们可以在 editData 属性中给组件添加可编辑的属性,比如背景图,然后再 component 中接受属性从而设置样式。
在 config 属性中,我们可以设置组件默认属性值,和 editData 中每一项的 key 一一对应。
================================================
FILE: doc/zh/guide/componentDev/componentStructure.md
================================================
<!--
* @Date: 2021-01-17 12:25:33
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 19:42:42
* @FilePath: /github-h5-Dooring/doc/zh/guide/componentDev/componentStructure.md
-->
# 组件结构
dooring 的组件设计包含以下 3 个部分组件:
1、component 组件主体
2、schema 组件的 DSL,结构协议层
3、template 定义了组件的类型、外观、从属关系,后期考虑纳入 schema
接下来我会介绍一个基本的组件主体设计,以为 template 设计,在下一章会具体介绍 schema 部分。
## 组件设计
我们这里拿基本的 header 组件来举例,如下是 header 组件的代码:
```jsx
interface HeaderPropTypes extends IHeaderConfig {
isTpl: boolean;
}
const Header = memo((props: HeaderPropTypes) => {
const { bgColor, logo, logoText, fontSize, color } = props;
return props.isTpl ? (
<div>
<img style={{ width: "100%" }} src={logos} alt="" />
</div>
) : (
<header className={styles.header} style={{ backgroundColor: bgColor }}>
<div className={styles.logo}>
<img src={logo && logo[0].url} alt={logoText} />
</div>
<div className={styles.title} style={{ fontSize, color }}>
{logoText}
</div>
</header>
);
});
```
我们只需要按照上面的方式编写组件即可,props 是 DSL 定义的数据层,用来控制组件的 shape,也就是组件的表现。我们看看 header 对应的 template。
## template 设计
```js
const template = {
type: "Header",
h: 28,
displayName: "页头组件"
};
export default template;
```
以上就是我们 template 的结构,type 用来定义组件的类型,方便渲染器动态查找,h 代表组件的初始化高度,我们可以自由设置。displayName 是组件的中文名,用来在左侧组件面板中展示,方便用户理解,我们可以在 template 中自定义更多辅助信息,方便使用者更高效的使用我们的编辑器。
## schema 设计
开发一个自定义组件需要包含 3 部分, `Component`, `Schema` 和 `Template`. 接下来我们看一下 `Header` 组件的 `Schema`.
```js
import {
IColorConfigType,
INumberConfigType,
ITextConfigType,
IUploadConfigType,
TColorDefaultType,
TNumberDefaultType,
TTextDefaultType,
TUploadDefaultType
} from "@/components/FormComponents/types";
import { baseConfig, baseDefault, ICommonBaseType } from "../../common";
export type THeaderEditData = Array<
IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType
>;
export interface IHeaderConfig extends ICommonBaseType {
bgColor: TColorDefaultType;
logo: TUploadDefaultType;
logoText: TTextDefaultType;
fontSize: TNumberDefaultType;
color: TColorDefaultType;
height: TNumberDefaultType;
}
export interface IHeaderSchema {
editData: THeaderEditData;
config: IHeaderConfig;
}
const Header: IHeaderSchema = {
editData: [
...baseConfig,
{
key: "bgColor",
name: "背景色",
type: "Color"
},
{
key: "height",
name: "高度",
type: "Number"
},
{
key: "logo",
name: "logo",
type: "Upload",
isCrop: true,
cropRate: 1000 / 618
},
{
key: "logoText",
name: "logo文字",
type: "Text"
},
{
key: "color",
name: "文字颜色",
type: "Color"
},
{
key: "fontSize",
name: "文字大小",
type: "Number"
}
],
config: {
bgColor: "rgba(0,0,0,1)",
logo: [
{
uid: "001",
name: "image.png",
status: "done",
url: "http://49.234.61.19/uploads/3_1740be8a482.png"
}
],
logoText: "页头Header",
fontSize: 20,
color: "rgba(255,255,255,1)",
height: 50,
...baseDefault
}
};
export default Header;
```
`editData`表示组件的可编辑属性, 我们可以自定义哪些组件可编辑. `config`为组件接收的属性, 和`editData`数组项中的`key`一一对应.
### 组件编辑区属性类型
`Dooring`组件编辑面板有如下对应编辑类型:
- Upload 上传组件
- Text 文本框
- RichText 富文本
- TextArea 多行文本
- Number 数字输入框
- DataList 列表编辑器
- FileList 文件列表编辑器
- InteractionData 交互设置
- Color 颜色面板
- MutiText 多文本
- Select 选择下拉框
- Radio 单选框
- Switch 开关切换
- CardPicker 卡片面板
- Table 表格编辑器
- Pos 坐标编辑器
- FormItems 表单设计器
================================================
FILE: doc/zh/guide/componentDev/dynamicLoading.md
================================================
<!--
* @Date: 2021-01-17 14:24:40
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 19:42:53
* @FilePath: /github-h5-Dooring/doc/zh/guide/componentDev/dynamicLoading.md
-->
# 组件动态加载
目前 H5-Dooring 的组件都是通过动态加载的方式引入,好处是我们在页面中只会加载我们需要的组件,不需要的组件不会被加载,这样可以提高页面加载的速度,这样做也会出现一些问题,比如一个长页面,配置了很多组件,那么一个页面加载过程可以会触发多次请求,目前还没有遇到性能问题,但后续会逐渐优化这个问题。
## umi3 提供的 dynamic
目前组件的动态加载我们采用的 umi 的 dynamic 方案,基于它我们上层封装了一个组件动态加载器,原理如下:
<img src="../../../img/componentDev/dynamic.png" alt="foo">
具体代码可以参考 Dooring 的 Github 地址:[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)
================================================
FILE: doc/zh/guide/deployDev/api.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**后端部分主要使用 `Nodejs` 开发, 为了满足更多定制化需求和服务的可移植性, 特意编写了 API 接口文档,
方便大家使用不同的后端语言实现服务接入.
- 注意: 接口统一前缀为`/api/v0`
## 用户相关
### 用户登录
用户登录接口
- `POST` /vip/check
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -----: |
| n | true | string | 用户名 |
| co | true | string | 密码 |
返回示例
```json
{
"result": {
"n": "test",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"rp": "AAAAA",
"maxage": 300000
}
}
```
### 注销登录
注销接口
- `POST` /vip/checkout
返回示例
```json
{
"result": null,
"msg": "退出成功"
}
```
### 权限控制
不同用户级别所访问的页面权限不同, 这块可结合服务端已有代码设计属于自己的权限字段, 地址为`server/src/router`
### 用户列表
获取用户列表接口
- `GET` /vip/all
获取用户列表需要账号满足以下条件:
- 已登录
- 为超级管理员
返回示例
```json
[
{
"id": "",
"n": "test",
"co": "123456",
"od": [],
"h5": [
{
"t": "23242ED",
"n": "测试页面"
}
],
"wx": "Mr_xuxiaoxi",
"rp": "AAAAA"
}
]
```
### 添加用户
添加用户接口
- `POST` /vip/add
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| -------- | :------: | :----: | -----: |
| nickname | true | string | 用户名 |
| wx | true | string | 微信号 |
| co | true | string | 密码 |
注: co 是由笔者写的`加密算法`实现, 不需要手动填写, 详情见`dooirng`后台管理/用户管理页面.
返回示例
```json
{
"id": "3422EF",
"n": "test",
"wx": "Mr_xuxiaoxi",
"co": "123456",
"od": [],
"h5": [],
"tpl": [],
"rp": "AAAAA",
"h5Num": 10,
"tplNum": 3
}
```
### 生成登录码
生成登录码接口
- `GET` /vip/gcode
先决条件:
- 用户已登陆
- 为超级管理员
注: 生成登录码是由笔者写的`加密算法`实现, 不需要手动实现, 如果有自定义需求, 可以自行二次开发实现.
返回示例
```json
{
"co": "1x2fgggteee3456_zdd4"
}
```
`说明:` 为了保护用户信息安全, 返回的登录码是加密后的密文, 会调用笔者写的`xib.xip`方法进行加密, 如果想看到原始密码, 需要调用`xib.uxip`进行解密.
### 获取用户真实密码
获取用户真实密码接口
- `GET` /vip/gcode/get
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -----------: |
| co | true | string | 加密后的密码 |
返回示例
```json
{
"co": "12345678"
}
```
### 修改用户信息
修改用户接口
- `POST` /vip/edit
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| -------- | :------: | :----: | ------: |
| id | false | string | 用户 ID |
| nickname | false | string | 用户名 |
| co | false | string | 登录码 |
| wx | false | string | 微信号 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "修改成功"
}
```
### 删除用户
删除用户接口
- `DELETE` /vip/del
先决条件:
- 用户已登陆
- 为超级管理员
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ------: |
| id | true | string | 用户 ID |
| wx | true | string | 微信号 |
| n | true | string | 用户名 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## H5 页面管理
### 获取 H5 数据
- `GET` /visible/h5/get
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ---------: |
| tid | true | string | H5 唯一 id |
返回示例
```json
{
"pageConfig": {},
"tpl": [
{
"id": "879742",
"item": {
"type": "Carousel",
"config": {
"direction": "left",
"swipeable": false,
"autoPlay": false,
"imgList": [
{
"id": "1",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/1_1740bd7c3dc.png"
}
]
},
{
"id": "2",
"title": "趣谈小课1",
"desc": "致力于打造优质小课程",
"link": "xxxxx",
"imgUrl": [
{
"uid": "001",
"name": "image.png",
"status": "done",
"url": "http://io.nainor.com/uploads/2_1740bd8d525.png"
}
]
}
],
"tplImg": "http://io.nainor.com/uploads/carousal_17442e1420f.png"
},
"h": 82,
"editableEl": [
{
"key": "direction",
"name": "方向",
"type": "Radio",
"range": [
{
"key": "down",
"text": "从上到下"
},
{
"key": "left",
"text": "从左到右"
}
]
},
{
"key": "swipeable",
"name": "是否可拖拽",
"type": "Switch"
},
{
"key": "autoPlay",
"name": "是否自动播放",
"type": "Switch"
},
{
"key": "imgList",
"name": "图片列表",
"type": "DataList"
}
],
"category": "base"
},
"point": {
"i": "x-0",
"x": 0,
"y": 13,
"w": 24,
"h": 82,
"isBounded": true
},
"status": "inToCanvas"
},
{
"id": "481194",
"item": {
"type": "Form",
"config": {
"title": "表单定制组件",
"fontSize": 18,
"titColor": "rgba(60,60,60,1)",
"titWeight": "400",
"bgColor": "rgba(255,255,255,1)",
"btnColor": "rgba(20,54,226,100)",
"btnTextColor": "rgba(255,255,255,1)",
"api": "",
"formControls": [
{
"id": "1",
"type": "Text",
"label": "姓名",
"placeholder": "请输入姓名"
},
{
"id": "2",
"type": "Number",
"label": "年龄",
"placeholder": " 请输入年龄"
},
{
"id": "4",
"type": "MySelect",
"label": "爱好",
"options": [
{
"label": "选项一",
"value": "1"
},
{
"label": "选项二",
"value": "2"
},
{
"label": "选项三",
"value": "3"
}
]
}
]
},
"h": 172,
"category": "base"
},
"point": {
"i": "x-1",
"x": 0,
"y": 98,
"w": 24,
"h": 172,
"isBounded": true
},
"status": "inToCanvas"
}
]
}
```
### 保存 H5 数据
- `POST` /visible/h5/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ---------- | :------: | :----: | ------------------: |
| pageConfig | false | object | H5 页面配置数据 |
| tpl | true | object | H5 页面组件配置数据 |
| tid | true | string | H5 页面唯一 id |
参数示例
```json
{
"pageConfig": {
"bgColor": "rgba(151,25,25,1)",
"title": "医院宣传页"
},
"tpl": [],
"tid": "EF123D3"
}
```
返回示例
```json
{
"state": 200,
"result": {
"tid": "EF123D3"
},
"msg": "保存成功"
}
```
### 删除 H5 数据
- `DELETE` /visible/h5/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -------------: |
| tid | true | string | H5 页面唯一 id |
返回示例
```json
{
"state": 200,
"result": [
{
"tid": "EF123D3",
"name": "test页面"
},
{
"tid": "EF123D6",
"name": "test2页面"
}
],
"msg": "删除成功"
}
```
## H5 表单数据管理
### 保存表单数据
- `POST` /vip/h5/form/post
| 参数名 | 是否必选 | 类型 | 说明 |
| -------------- | :------: | :----: | --------------: |
| tid(query) | true | string | H5 页面唯一 id |
| formData(body) | true | array | H5 页面表单数据 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "表单提交成功"
}
```
### 批量导入表单数据
- `POST` /vip/h5/form/import
| 参数名 | 是否必选 | 类型 | 说明 |
| -------------- | :------: | :----: | ------------------: |
| tid(query) | true | string | H5 页面唯一 id |
| formData(body) | true | array | H5 页面表单数据集合 |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "批量导入成功"
}
```
### 删除表单数据
- `DELETE` /vip/h5/form/del
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | -------------: |
| tid | true | string | H5 页面唯一 id |
| ID | true | string | 表单专属 id |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 模版管理
### 获取模版库
- `GET` /visible/tpls/free
返回示例
```json
{
"state": 200,
"result": [
{
"img": "http://xxx/uploads/tpl_175adabd8dd.jpg",
"name": "合作模版",
"tid": "B73349B6"
}
]
}
```
### 保存模版
- `POST` /visible/tpl/save
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ---------- | :------: | :----: | --------------: |
| name | true | string | H5 模版名称 |
| cate | true | string | H5 模版分类 |
| img | false | string | H5 模版封面图 |
| tpl | true | array | H5 模版数据 |
| pageConfig | false | object | H5 模版全局配置 |
返回示例
```json
{
"state": 200,
"result": {
"tid": "B73349B6"
},
"msg": "保存成功"
}
```
### 删除模版
- `DELETE` /visible/tpl/del
先决条件:
- 用户已登陆
| 参数名 | 是否必选 | 类型 | 说明 |
| ------ | :------: | :----: | ---------: |
| tid | true | string | H5 模版 id |
返回示例
```json
{
"state": 200,
"result": null,
"msg": "删除成功"
}
```
## 文件上传
## 数据统计
### 数据大盘接口
### 页面埋点
================================================
FILE: doc/zh/guide/deployDev/deploy.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
私有化部署需要获取 4 个核心项目包, 包括
- H5 编辑器(h5_plus)
- H5 基座(h5)
- Dooring 管理后台(Dooring-Admin)
- 服务端项目(Server)
获取以上四个核心源码工程需要满足商业授权协议, 具体可联系作者[徐小夕](http://h5.dooring.cn/uploads/WechatIMG3_1758e9753e2.jpeg)
### 部署架构图
<img src="../../../img/common/deploy.png" alt="H5-dooring部署">
部署流程如下:
1. 下载 4 个源码工程, 安装依赖(npm install 或 yarn)
2. 打包 3 个前端工程至`server`的 static 目录下
3. 在`server`下本地运行 `yarn start` 或 `npm start` 启动服务端进行本地测试
4. 打包服务端代码, `yarn build` 生成 `dist` 目录, 建议使用 `pm2` 做`nodejs`服务的负载均衡, 运行 `pm2 start dist/index.js`启动生产环境代码
也可以将以上步骤集成到 gitlab 等 CI, CD 服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
### 步骤 3.4 详细流程
#### 1. 安装项目环境
服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
npm install
```
#### 2. 修改项目域名
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com`或`http://xxx.com:8080`(如非 80 端口)
#### 3. 编译项目
执行`npm run build`编译项目, 生成`dist`目录
#### 4. 运行项目
在项目根目录执行 `pm2 start dist/index.js`启动项目
================================================
FILE: doc/zh/guide/deployDev/deploy_v6.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
私有化部署需要获取 3 个核心项目包, 包括
- 可视化大屏编辑器(v6.dooring/)
- v6 管理后台(v6.dooring/manage)
- 服务端项目(v6.dooring/server)
获取以上三个核心源码工程需要满足商业授权协议, 具体可参考[商业授权方案](http://h5.dooring.cn/h5_plus/price)
### 部署架构图
<img src="../../../img/common/v6.deploy.png" alt="H5-dooring部署">
部署流程如下:
1. 下载 3 个源码工程, 安装依赖(npm install 或 yarn)
2. 打包 2 个前端工程至`server`的 static 目录下
3. 在`server`下本地运行 `yarn start` 或 `npm start` 启动服务端进行本地测试
4. 打包服务端代码, `yarn build` 生成 `dist` 目录, 建议使用 `pm2` 做`nodejs`服务的负载均衡, 运行 `pm2 start dist/index.js`启动生产环境代码
也可以将以上步骤集成到 gitlab 等 CI, CD 服务中, 进行自动化打包发布, 或者采用`docker`进行容器化部署.
### 步骤 3.4 详细流程
#### 1. 安装项目环境
服务器需提前安装 node 和 pm2, 将本项目上传至服务器指定的目录(如/www/activity), 进入项目目录, 执行:
```
npm install
```
#### 2. 修改项目域名
进入`./src/config/index.js`, 修改`staticPath`变量为当前服务器域名/ip, 如`http://xxx.com`或`http://xxx.com:8080`(如非 80 端口)
#### 3. 编译项目
执行`npm run build`编译项目, 生成`dist`目录
#### 4. 运行项目
在项目根目录执行 `pm2 start dist/index.js`启动项目
================================================
FILE: doc/zh/guide/deployDev/dir.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
服务端主要是我们的`server`工程, 数据主要存放在`server/public`下, 具体数据指代含义我们接下来会详细介绍.
- bed 存放图片库中的分类图片, 私有化部署的用户可以直接在此处扩充图片(更好的建议是直接存到第三方图床)
- h5 用户保存的 h5 数据文件, 一个页面对应一个 json 文件
- h5_tpl 平台保存的模版数据文件夹
- xxx.json 模版页面文件
- tpls.json 模版库中的模版列表数据, 可以手动清空
- h5_vip 会员数据目录
- form 会员制作的含表单页面的表单收集数据
- view.json 用户浏览量数据
- vip.json 会员列表数据
- vipCard.json 会员订单数据(暂时无用, 可删除)
- image.json 图片库, 主要用来渲染页面的图片库数据
- city.json 省市 3 级联动数据, 为表单组件提供数据支持
================================================
FILE: doc/zh/guide/deployDev/form.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
### 获取 Form 组件的值数据
Form 表单组件在`editor`目录下`src/components/BasicShop/BasicComponents`位置.
Form 组件是`Dooring`的核心组件之一, 内部的值通过 Form 组件内部收集, 当然我们也可以暴露出来让其他交互或者组件消费(需要一定的二次开发), 关键代码如下:
```js
req
.post(`/vip/h5/form/post${location.search}`, { ...fields, ...formData })
.then(res => {
if (type === "link") {
// 解析参数
let isPre = content.indexOf("?") < 0;
let query = { dr: Date.now(), from: urlParmas.tid };
try {
query = params ? { ...JSON.parse(params), ...query } : query;
} catch (err) {
console.log(err);
}
// 跳转
if (content.indexOf("http") > -1) {
window.location.href = content + urlencode(query, isPre);
return;
}
history.push(`/m?tid=${content}&${urlencode(query)}`);
} else if (type === "modal") {
setVisible(true);
} else if (type === "code") {
eval(content);
}
});
```
数据收集提交的核心代码在 Form 组件的第 56-149 行, 也就是`submit`方法. 表单组件收集到的数据统一存放在代码中的`formData`字段, 所以要想在其他地方获取用户表单填写的值, 我们只需要手动将`formData`传递出去, 或者挂载到全局(如 window 对象, localStorage, indexedDB 等).
================================================
FILE: doc/zh/guide/deployDev/https.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
目前**H5-Dooring**全面支持 https 部署, 具体方式方案如下.
### 前端工程
我们需要在前端工程中的`src/pages/document.ejs`中的`head`中添加如下代码:
```html
<meta
http-equiv="Content-Security-Policy"
content="upgrade-insecure-requests"
/>
```
目的是强制将页面中 HTTP 请求转换为 HTTPS.
### 服务器工程
#### 1. 申请 SSL 证书
#### 2. 生成 server.csr+server.key
#### 3. 通过证书链生成.pem 文件
#### 在`server`中的`src/index.js`按如下方式修改
```js
// 忽略部分无影响代码
import https from "https";
// 你的ssl存放路径, 建议直接放在server目录下
const filePath = path.join(__dirname, "../ssl");
// 启动逻辑
async function start() {
// https配置
const httpsOptions = {
key: fs.readFileSync(path.join(filePath, "3536084__doctopia.com.cn.key")), //ssl文件路径
cert: fs.readFileSync(path.join(filePath, "3536084__doctopia.com.cn.pem")) //ssl文件路径
};
// https服务
const server = https.createServer(httpsOptions, app.callback());
const io = require("socket.io")(server);
// 忽略其他无影响代码
// https默认443, 这里我们可以走公共配置
server.listen(443, () => {
console.log(`服务器地址:${config.staticPath}`);
});
}
start();
```
================================================
FILE: doc/zh/guide/deployDev/log.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
### 更新日志
#### 1.99
1. 添加数据源功能
2. 视频组件支持封面图
3. 优化页面 DSL 结构, 降低了 jsonSchema 体积 30%-50%
4. 官网优化
5. 管理后台替换 logo, 部分文案信息
6. 添加图片列表组件
7. 轮播图支持一键绑定数据源
#### 1.98
1. 编辑器功能区添加更多折叠下拉框, 优化头部界面
2. 添加数据源入口和界面
3. 模版库优化, 剔除无用模版, 累计 60+模版
4. 入口页添加赞助墙
5. 升级视频组件, 支持弹幕, 截屏, 模式设置等功能
6. 文件上传路径兼容 window 服务器本地化部署
#### 1.96
1. 修复首页推荐项目外链地址和站内文案
2. 替换 Dooring 网站 logo
3. 优化 ios8 以下访问 H5 时可能出现的页面卡顿问题
4. 图片上传组件添加 svg, gif 图片格式支持
5. 后台管理系统添加一键跳编辑器按钮
6. 服务端编辑侧路由加固
7. 文件上传组件添加自定义上传文档,支持七牛云,腾讯云,阿里 oss 等第三方图床方式
#### 1.95
1. dooring 文档添加更新日志模块
2. dooring 增报错监控函数, 提供一键清空缓存按钮和自动重载功能
3. 新增电商商品 H5 模版
4. 页面配置增加背景模式和背景重复
5. 表单添加字段名配置项
#### 1.94
1. 转盘组件支持转盘交互功能(跳转链接/打开弹窗/自定义代码)
2. 添加网站拦截, 防止页面误关导致页面无法保存
3. 优化页面控制条组件样式
4. 按钮组件添加组件动画
5. 图片组件添加组件动画
6. 媒体组件 icon 优化
7. 全局错误监控组件添加一键清除缓存功能
#### 1.93
1. 上线源码下载功能
2. 服务端支持下载源码服务和下载次数限制
3. 界面部分文案优化
4. 出码基座优化
5. 抽奖组件支持抽奖后自定义交互(弹窗/链接/自定义代码)
#### 1.92
1. 修复背景图预览适配问题
2. 转盘组件支持中奖后自定义交互/弹窗/自定义代码
3. 界面局部调整
4. 后台管理表单数据支持多键查询
5. 可视化大屏柱状图组件支持实时数据请求
================================================
FILE: doc/zh/guide/deployDev/oss.md
================================================
<!--
* @Date: 2021-01-20 23:25:29
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-01-22 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/deployDev/deploy.md
-->
**H5-Dooring**全面支持第三方对象存储服务, 我们以七牛云对象存储为例.
### 前端上传文件到 oss
首先我们需要在第三方对象储存服务中配置对应的服务和域名. 其次安装对应的 sdk, 如七牛云 sdk:
```js
import * as qiniu from "qiniu-js";
```
其次我们修改`h5_plus`工程的`Upload`组件, 详细地址为`src/core/FormComponents/Upload`.
修改内容如下:
```js
const fileName = file.name;
const suffix = "自定义文件后缀";
const putExtra = {
fname: fileName,
params: {}
};
const uid = +new Date() + uuid(16, 8) + suffix;
// 使用七牛云上传api, 前提是提前在前端拿到对应的ticket, 可以通过请求的方式获取
const observe = qiniu.upload(
file,
uid,
this.state.qnToken.ticket,
putExtra,
{}
);
observe.subscribe(
() => {},
null,
res => {
// 拼接路径
const url = `${this.state.qnToken.domain}/${res.key}`;
// 存库
const fileList = [{ uid, name: fileName, status: "done", url }];
this.setState({
curImgUrl: url,
fileList
});
this.props.onChange && this.props.onChange(fileList);
}
);
```
其他 oss 服务类似, 如果不清楚如何配置, 可以在[H5-Dooring 官网](http://h5.dooring.cn/)中找到我们.
### 如何接入任何第三方上传服务
首先我们的上传组件`Upload`使用内部的服务接口来实现上传功能, 所以需要给组件的`action`赋值, 如下:
```jsx
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
onRemove={this.handleRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={sdk_upload_api || action}
withCredentials={withCredentials}
headers={{
"x-requested-with": localStorage.getItem("user") || "",
authorization: localStorage.getItem("token") || "",
...headers
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
如果需要集成第三方 oss, 如七牛云, 阿里 oss 等, 我们需要将`Upload`组件的`action`属性设置为空字符串, 其次删除`onChange`属性, 上传操作统一在`beforeUpload`中进行. 案例如下:
```jsx
<Upload
fileList={fileList}
action=""
onPreview={this.handlePreview}
onRemove={this.onRemove}
name="file"
listType="picture-card"
className={styles.avatarUploader}
headers={{ ...headers }}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
```
自定义上传的核心逻辑放在了`beforeUpload`上. 我们具体看看`beforeUpload`这个方法如何实现.
```js
handleBeforeUpload = (file: RcFile) => {
// 1. 限制图片类型
const isJpgOrPng =
file.type === "image/jpeg" ||
file.type === "image/png" ||
file.type === "image/jpg" ||
file.type === "image/gif";
if (!isJpgOrPng) {
message.error("只能上传格式为jpeg/png/gif的图片");
}
// 限制上传文件大小
const isLt3M = file.size / 1024 / 1024 < 3;
if (!isLt3M) {
message.error("图片必须小于3MB!");
}
if (isJpgOrPng && isLt3M) {
// 3. 正常上传逻辑
const fileName = file.name;
// 3.1 调用oss接口, 将图片上传oss
// 3.2 将接口返回的url信息, 组装成fileList数据结构, 并更新state
const fileList = [{ uid, name: fileName, status: "done", url }];
this.setState({
curImgUrl: url,
fileList
});
// 3.3 将数据传给上层保存
this.props.onChange && this.props.onChange(fileList);
}
return isJpgOrPng && isLt3M;
};
```
================================================
FILE: doc/zh/guide/directoryStructure.md
================================================
```
src
├─ assets
│ ├─ header.png
│ ├─ form.png
│ ├─ footer.png
│ ├─ icon.png
│ ├─ picture.png
├─ components
│ ├─ BackTop
│ │ └─ index.js
│ ├─ BasicShop
│ │ ├─ BasicComponents
│ │ │ ├─ Card
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Carousel
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Footer
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Form
│ │ │ │ ├─ BaseForm.tsx
│ │ │ │ ├─ BasePopoverForm.tsx
│ │ │ │ ├─ baseForm.less
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Header
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Icon
│ │ │ │ ├─ icon.ts
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Image
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ LongText
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Nav
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Notice
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Qrcode
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ RichText
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Text
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ WhiteTpl
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ XButton
│ │ │ │ ├─ Modal.tsx
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ schema.ts
│ │ │ └─ template.ts
│ │ ├─ MediaComponents
│ │ │ ├─ Audio
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Calendar
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Map
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Video
│ │ │ │ ├─ index.css
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ schema.ts
│ │ │ └─ template.ts
│ │ ├─ ShopComponents
│ │ │ ├─ CardLabel
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Coupons
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ List
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Tab
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ ZhuanLan
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ schema.ts
│ │ │ └─ template.ts
│ │ ├─ VisualComponents
│ │ │ ├─ Area
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Chart
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Funnel
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Line
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Pie
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ Radar
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ WordCloud
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ XProgress
│ │ │ │ ├─ index.less
│ │ │ │ ├─ index.tsx
│ │ │ │ ├─ schema.ts
│ │ │ │ └─ template.ts
│ │ │ ├─ schema.ts
│ │ │ └─ template.ts
│ │ ├─ common.ts
│ │ └─ schema.ts
│ ├─ Calibration
│ │ ├─ index.less
│ │ └─ index.tsx
│ ├─ ErrorBundaries
│ │ └─ index.tsx
│ ├─ LoadingCp
│ │ └─ index.tsx
│ ├─ ModalTpl
│ │ ├─ cate.js
│ │ ├─ index.js
│ │ └─ index.less
│ └─ Zan
│ ├─ index.less
│ └─ index.tsx
├─ core
│ ├─ FormComponents
│ │ ├─ CardPicker
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ Color
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ DataList
│ │ │ ├─ editorModal.tsx
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ FormItems
│ │ │ ├─ EditorModal.tsx
│ │ │ ├─ FormItems.tsx
│ │ │ ├─ formItems.less
│ │ │ └─ index.tsx
│ │ ├─ InteractionData
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ MutiText
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ Pos
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ Table
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ Upload
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ ├─ XEditor
│ │ │ ├─ index.less
│ │ │ └─ index.tsx
│ │ └─ types.ts
│ ├─ DynamicEngine.tsx
│ ├─ FormRender.tsx
│ ├─ ViewRender.tsx
│ └─ viewRender.less
├─ layouts
│ ├─ __tests__
│ │ └─ index.test.js
│ ├─ index.less
│ └─ index.tsx
├─ pages
│ ├─ __tests__
│ │ └─ index.test.js
│ ├─ editor
│ │ ├─ components
│ │ │ ├─ AvatorGroup
│ │ │ │ └─ index.tsx
│ │ │ ├─ CanvasControl
│ │ │ │ ├─ index.less
│ │ │ │ └─ index.tsx
│ │ │ └─ Header
│ │ │ ├─ index.js
│ │ │ └─ index.less
│ │ ├─ models
│ │ │ └─ editorModal.js
│ │ ├─ services
│ │ │ └─ editorService.js
│ │ ├─ Container.js
│ │ ├─ SourceBox.tsx
│ │ ├─ TargetBox.js
│ │ ├─ index.js
│ │ ├─ index.less
│ │ └─ preview.tsx
│ ├─ help
│ │ ├─ index.less
│ │ └─ index.tsx
│ ├─ home
│ │ ├─ index.less
│ │ └─ index.tsx
│ ├─ ide
│ │ ├─ _draft.tsx
│ │ ├─ index.less
│ │ └─ index.tsx
│ ├─ login
│ │ ├─ index.less
│ │ └─ index.tsx
│ ├─ document.ejs
│ └─ mobileTip.js
├─ utils
│ ├─ req.ts
│ └─ tool.ts
├─ app.tsx
└─ global.css
```
================================================
FILE: doc/zh/guide/functionRealization/download.md
================================================
<!--
* @Date: 2021-01-17 14:26:00
* @LastEditors: xuxiaoxi
* @LastEditTime: 2021-05-17 21:32:58
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/saveJson.md
-->
## 下载源码
目前 Dooring 已支持下载源码功能, 我们可以使用编辑器页面头部的下载按钮来实现下载用户搭建的 H5 源码.
<img src="../../../img/functionRealization/down.png" alt="foo">
源码下载之后是完整的 React 项目源代码, 开发人员可以直接根据自己的业务需求来二次编写代码来满足不同的业务需求.
在拿到源码之后, 我们需要进入项目, 使用 npm 或者 yarn 安装项目依赖, 如下:
```bash
npm install
// 或者
yarn
```
之后我们就可以本地运行项目了:
```bash
npm start
// 或者
yarn start
```
因为源码工程采用`umi3.0`搭建, 所以代码配置可以参考`umi3.0`规范, 比如路由配置, `history`模式, 打包路径等, 二次开发完成之后, 我们可以执行:
```bash
npm run build
// 或者
yarn build
```
将项目打包成 html, 以便部署到任何服务器中.
================================================
FILE: doc/zh/guide/functionRealization/machinePreview.md
================================================
<!--
* @Date: 2021-01-17 14:27:13
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:49:26
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/machinePreview.md
-->
# 真机预览
真机预览和网页预览的流程类似,工作流程如下:
<img src="../../../img/functionRealization/preview-machine.png" alt="foo">
由于不同机型预览的效果有些许不同,最终效果以实际看到的为主。
================================================
FILE: doc/zh/guide/functionRealization/pagePreview.md
================================================
<!--
* @Date: 2021-01-17 14:26:41
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:49:18
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/pagePreview.md
-->
# 网页预览
我们看看网页预览的工作流程:
<img src="../../../img/functionRealization/preview-flow.png" alt="foo">
前端预览界面:
<img src="../../../img/functionRealization/preview-page.png" alt="foo">
================================================
FILE: doc/zh/guide/functionRealization/revocation.md
================================================
<!--
* @Date: 2021-01-17 14:27:28
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:50:17
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/revocation.md
-->
# 撤销/重做
撤销重做我们主要使用了 redux-undo 这个库,配合 Dva 使用,具体使用方法参考如下操作:
```js
import { createLogger } from "redux-logger";
import { message } from "antd";
import undoable, { StateWithHistory } from "redux-undo";
import { Reducer, AnyAction } from "redux";
export const dva = {
config: {
onAction: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
onReducer: (reducer: Reducer<any, AnyAction>) => {
let undoReducer = undoable(reducer);
return function(state: StateWithHistory<any>, action: AnyAction) {
let newState = undoReducer(state, action);
let router = newState.present.router
? newState.present.router
: newState.present.routing;
return { ...newState, router: router };
};
}
}
};
```
以上我们就实现了全局配置 redux-undo,在撤销重做按钮中我们就可以触发对应的方法来实现撤销重做的功能,其次我们还使用了 redux-logger 来实现 redux 的日志输出。
================================================
FILE: doc/zh/guide/functionRealization/saveJson.md
================================================
<!--
* @Date: 2021-01-17 14:26:00
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:32:58
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/saveJson.md
-->
# 保存 json
我们配置好 H5 页面之后,如果希望其他人观看,我们可以保存页面并发送链接。但是如果有多人协作的需求,比如一个 H5 页面可能由多个人完成,这个时候该怎么实现呢?基于已有的方案,我们可以采用 socket 实现多人协同编辑,但是成本比较大,所有这里我们提供了保存 json 的功能。
我们可以将配置好的页面导出为 json,发送给另一个人,这样另一个人通过导入该 json 文件可以实时看到当前的页面,这里还是依靠我们的页面渲染引擎 viewEngine。实现思路也很简单,可以在 github[[https://github.com/MrXujiang/h5-Dooring](https://github.com/MrXujiang/h5-Dooring)]上参考体验。
================================================
FILE: doc/zh/guide/functionRealization/screenshot.md
================================================
<!--
* @Date: 2021-01-17 14:27:49
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:49:46
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/screenshot.md
-->
# 截图功能
截图功能这里我们主要使用了 dom-to-image 这个库,来将 html 转化为图片,并进行分享。
<img src="../../../img/functionRealization/screenshot.png" alt="foo">
================================================
FILE: doc/zh/guide/functionRealization/templateLibrary.md
================================================
<!--
* @Date: 2021-01-17 14:25:29
* @LastEditors: chentianshang
* @LastEditTime: 2021-01-17 21:48:34
* @FilePath: /github-h5-Dooring/doc/zh/guide/functionRealization/templateLibrary.md
-->
## 模板库实现思路
我们目前开放了模板库功能,一方面我们会定期配置行业模板,另一个方面 Dooring 还支持用户自己配置模板,可以一键保存到云端供用户使用。我们也可以将模板变成自己的页面共享给其他人。实现方式本质上是保存用户的配置信息,上传到服务器中做存储,在后台提供了管理模板的模块,可以修改,删除模板。如下图所示:
### 模板前台展示:
<img src="../../../img/functionRealization/template-ft.png" alt="foo">
### 模板后台展示:
<img src="../../../img/functionRealization/template-bg.png" alt="foo">
================================================
FILE: doc/zh/guide/introduced.md
================================================
<img src="../../img/common/framework.png" alt="foo">
注:灰色部分还未实现,正在更新中...
================================================
FILE: doc/zh/guide/startedQuickly.md
================================================
# 快速上手
## 从零搭建一个 H5 表单页面
<iframe src="//player.bilibili.com/player.html?aid=715343955&bvid=BV1QQ4y1Z725&cid=332145157&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 100%;height: 420px; margin-top: 20px"> </iframe>
## 环境准备
首先得有 node,并确保 node 版本是 `10.13` 或以上,(mac/win 下推荐使用 n 来管理 node 版本)
```
$ node-v
v10.13.0
```
注:推荐使用 yarn 管理 npm 依赖
## 源码工程
| h5_plus(编辑器项目) | admin(管理后台) | Server(服务端项目) |
| ------------------- | --------------- | ------------------ |
本地拿到源码工程之后先安装对应依赖,在对应工程目录里执行 yarn 命令,等待依赖安装完成。
## 本地运行
1.首先本地启动 server,在 src 目录的 index.js 中修改跨域白名单,改为本地的 ip+端口,如http://192.167.0.3:8000
2.其次本地启动 h5_plus,启动完毕在浏览器打开对应的启动地址即可查看,如下:
<img src="../../img/common/home.png" alt="foo">
================================================
FILE: package.json
================================================
{
"name": "h5-dooring",
"version": "1.3.0",
"description": "H5-Dooring是一款功能强大,开源免费的H5可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的H5落地页最佳实践。技术栈以react为主, 后台采用nodejs开发。",
"private": false,
"author": {
"name": "徐小夕",
"email": "xujiang156@qq.com",
"url": "http://h5.dooring.cn/h5_visible"
},
"keywords": [
"h5 editor",
"h5",
"react",
"antd",
"react-dnd",
"web visible"
],
"contributors": [
"徐小夕 <xujiang156@qq.com> (https://github.com/MrXujiang))",
"mokinzhao <37622852@qq.com> (https://github.com/mokinzhao))",
"yehuozhili <yehuozhili@outlook.com> (https://github.com/yehuozhili))"
],
"scripts": {
"start": "export NODE_OPTIONS=--openssl-legacy-provider && umi dev -- editor",
"start:win": "set NODE_OPTIONS=--openssl-legacy-provider && umi dev -- editor",
"build": "export NODE_OPTIONS=--openssl-legacy-provider && umi build",
"build:win": "set NODE_OPTIONS=--openssl-legacy-provider && umi build",
"server": "node server.js",
"dev": "http-server dist",
"docs:dev": "vuepress dev doc",
"docs:build": "vuepress build doc",
"test-demo": "http-server dist",
"postinstall": "umi generate tmp",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"test": "umi-test",
"test:coverage": "umi-test --coverage",
"nocompress": "cross-env RM_TMPDIR=none COMPRESS=none umi build"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,less,md,json}": [
"prettier --write"
],
"*.ts?(x)": [
"prettier --parser=typescript --write"
]
},
"homepage": "http://h5.dooring.cn",
"repository": {
"type": "git",
"url": "git+https://github.com/MrXujiang/h5-Dooring.git"
},
"bugs": {
"url": "https://github.com/MrXujiang/h5-Dooring/issues"
},
"dependencies": {
"@ant-design/icons": "^4.2.1",
"@antv/f2": "^3.7.7",
"@visactor/vchart": "^1.12.1",
"@uiw/react-baidu-map": "^1.17.3",
"@umijs/plugin-esbuild": "^1.0.1",
"@umijs/plugin-sass": "^1.1.1",
"@umijs/preset-react": "1.x",
"@umijs/test": "^3.2.19",
"antd": "^4.7.0",
"antd-img-crop": "^3.10.0",
"axios": "^0.19.2",
"braft-editor": "^2.3.9",
"chatbot-antd": "^0.6.0",
"codemirror": "^5.57.0",
"dom-to-image": "^2.6.0",
"file-saver": "^2.0.2",
"http-server": "^0.12.3",
"keymaster": "^1.6.2",
"qrcode.react": "^1.0.0",
"react": "^16.12.0",
"react-audio-player": "^0.14.0",
"react-codemirror2": "^7.2.1",
"react-color": "^2.18.1",
"react-contexify": "^4.1.1",
"react-dnd": "^11.1.3",
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "^16.12.0",
"react-draggable": "^4.4.3",
"react-draggable-ball": "^0.1.0",
"react-grid-layout": "^1.0.0",
"react-hotkeys-hook": "^2.3.1",
"react-text-loop": "^2.3.0",
"redux-undo": "^1.0.1",
"socket.io-client": "^2.3.0",
"umi": "^3.2.19",
"video-react": "^0.14.1",
"xlsx": "^0.16.7",
"yh-react-popover": "^0.3.0",
"yorkie": "^2.0.0",
"zarm": "^2.5.1"
},
"license": "GPL-3.0",
"devDependencies": {
"@types/classnames": "^2.2.10",
"@types/codemirror": "^0.0.98",
"@types/events": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/node": "^14.6.2",
"@types/qrcode.react": "^1.0.1",
"@types/react-color": "^3.0.4",
"@types/react-grid-layout": "^1.1.0",
"@types/redux-logger": "^3.0.8",
"@types/xlsx": "^0.0.36",
"@typescript-eslint/eslint-plugin": "4.1.1",
"@typescript-eslint/parser": "4.1.1",
"babel-eslint": "10.x",
"babel-plugin-import": "^1.13.0",
"cross-env": "^7.0.2",
"eslint": "6.x",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "4.x",
"eslint-plugin-import": "2.x",
"eslint-plugin-jsx-a11y": "6.x",
"eslint-plugin-react": "7.x",
"eslint-plugin-react-hooks": "2.x",
"koa": "^2.13.0",
"koa-body": "^4.2.0",
"koa-logger": "^3.2.1",
"koa-static": "^5.0.0",
"koa2-cors": "^2.0.6",
"lint-staged": "^10.0.7",
"prettier": "^1.19.1",
"redux-logger": "^3.0.6",
"sass-loader": "^9.0.3",
"typescript": "^4.0.2",
"vuepress": "^1.8.0"
}
}
================================================
FILE: readme.md
================================================
> > Make H5 as easy as building blocks!
<p align="center">
<img src="http://cdn.dooring.cn/dr/logo.ff7fc6bb.png" width="260" alt="H5编辑器,H5制作神器,H5 editor,lowcode">
</p>
<h1 align="center">Welcome to H5-Dooring 👋</h1>
<p>
<img alt="Version" src="https://img.shields.io/badge/version-1.2-blue.svg?cacheSeconds=2592000" />
<a href="https://juejin.im/post/6864410873709592584/" target="_blank">
<img alt="Documentation" src="https://img.shields.io/badge/documentation-yes-brightgreen.svg" />
</a>
<a href="#" target="_blank">
<img alt="license:GPL3.0" src="https://img.shields.io/badge/license-GPL3.0-yellow.svg" />
</a>
</p>
> H5-Dooring is a powerful, open source, free H5 visual page configuration solution dedicated to providing a simple, convenient, professional and reliable, unlimited set of H5 landing page best practices. The technology stack is mainly react, developed in the background using nodejs.
<img src="http://cdn.dooring.cn/dr/123.png" alt="H5-Dooring编辑器预览图" width="100%" />
| home🏠 | demo✨ | doc📦 | tutorial | wiki |
| ------------------------------- | ------------------------------------ | ------------------------------------ | -------------------------------------------------------------- | ---------------------------------------------------- |
| [website](https://dooring.vip) | [Demo](https://dooring.vip) | [Document](https://dooring.vip/doc) | [视频&Video](https://www.zhihu.com/zvideo/1406394315950653440) | [wiki](https://github.com/MrXujiang/h5-Dooring/wiki) |
> ✨ note: If the official visit is too slow, visit the [H5-Dooring for Singapore](https://dooring.vip)
🎉福利🎉: 最近基于nextjs实现的多维表格正式上线, 前100名用户仅需399元, 感兴趣可以参考体验一下.
- 多维表格编辑器: http://pxcharts.com
English | [简体中文](./zh.md)
new doc: [private deployment process](http://h5.dooring.cn/docz/source-list/H5-Dooring/deploy_en)
website: [H5-Dooring](https://dooring.net)
tech blog: [sharing of technology](https://dev.to/alex_xu/we-made-a-page-visualization-to-build-an-open-source-project-1l1p)
Related products:
- [V6.Dooring | Large screen visual editor](https://github.com/MrXujiang/v6.dooring.public)
- [dooring-electron-lowcode | Dooring desktop software](https://github.com/MrXujiang/dooring-electron-lowcode)
- [flowmix/docx | 多模态文档引擎](http://flowmix.tunrtip.cn/docx)
- [Dooring-Saas](https://dooring.vip)
- [Dooring 智图](https://magic.dooring.vip)
## Author
👤 **alex_xu**
- Website: http://h5.dooring.cn
- Github: [@MrXujiang](https://github.com/MrXujiang)
- new tech share: [Dooring2.7+最新技术分享&复盘](https://github.com/MrXujiang/h5-Dooring/issues/145)
## Star History
[](https://star-history.com/#MrXujiang/h5-Dooring&Date)
## 🤝 Contributing and support
Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/MrXujiang/h5-Dooring/issues).
Give a ⭐️ if this project helped you!
## Privatization Deployment documentation
- address: http://h5.dooring.cn/docz
- ⭐️ lowcode component list: http://h5.dooring.cn/docz/components/intro
- List of answers: http://h5.dooring.cn/docz/source-list/H5-Dooring/guide
- gitee mini code: https://gitee.com/lowcode-china/h5_-dooring
### Features
1. editor
- [x] Guides
- [x] The underlying component
- [x] Visual components
- [x] Media components
- [x] Product components
- [x] Dragper
- [x] Configure the panel
- [x] Form designer
- [x] (Multi) Page management (copy, edit, delete, new)
- [x] Component animation
- [x] Component interaction
- [x] Data source management
- [x] Quick preview
- [x] Real machine preview
- [x] Undo and redo
- [x] WeChat shares
- [x] shortcut key
- [x] The template library
- [x] Desktop software, Dooring-electron, supports offline use
2) Enhanced features
- [x] Upload json, convert to H5 with one click
- [x] Photo gallery
- [x] Code capability (download source, download dis package)
3) backend API
- [x] Create, save, and update your work
- [x] User management, rights management
- [x] One-click intelligent analysis
- [x] Data look
- [x] Form data collection
- [x] Form data presentation
- [x] Form data analysis, one-click export excel, form multi-condition search
- [x] Preview online
- [x] QR code preview
- [x] Template management
- [x] Code interface
## Update the log
1. Whether the video component adds a full-screen configuration item when playing
2. Fixes a click failure of the icon component
3. The editor supports multi-size switching and canvas size customization
4. Fix multilingual issues with background management analytics forms
5. The component library supports user-defined selection
## Technology Sharing
- [(10 月最新) 前端图形学实战: 从零开发几何画板(vue3 + vite 版](https://github.com/MrXujiang/h5-Dooring/issues/149)
- [几何学在前端边界计算中的应用和原理分析(一)](https://github.com/MrXujiang/h5-Dooring/issues/148)
### The back-end section
The back-end part because of the knowledge points involved is more, is not the focus of this article, so here is a few points, you can use completely different technology to achieve back-office services, such as PHP, Java, Python or Egg. The author here is using the "koa" .
Specific code can refer to the full stack development article:
- [基于 Koa + React + TS 从零开发全栈文档编辑器](https://mp.weixin.qq.com/s?__biz=MzU2Mzk1NzkwOA==&mid=2247486910&idx=2&sn=7ce865dd8a8f6769439f0e8eebb72212&chksm=fc531445cb249d534a7d8a362ad40d26bc90f2d2e867385768ee19575e32826fcbe419fcbe0b&token=297396546&lang=zh_CN#rd)
- [Dooring 后台接口文档](http://h5.dooring.cn/doc/zh/guide/deployDev/api.html)
- [私有化部署](http://h5.dooring.cn/h5_plus/price)
## Install
1. Download the code
```sh
git clone https://github.com/MrXujiang/h5-Dooring.git
```
2. Go to the project catalog
```sh
cd ./h5-Dooring
```
3. Install the dependency package
```sh
yarn pkg
```
## Usage
Launch the app locally
```sh
yarn start
or
cnpm run start
```
local visit:
```
http://localhost:port/h5_plus
```
注意: 如果 window 系统下无法启动, 请移步 [dooring-electron](https://github.com/H5-Dooring/dooring-electron-lowcode)
## How to run the downloaded code ?
1. The compression package can be unzipped directly to the server root, and access to the root address is sufficient
2. 'vscode' installs the Live Server plug-in, unzips the downloaded compression package into a folder, opens with 'vscode', clicks on Live Server, and notes that to remove the 'index .html' of the startup path, change it to '/'
If you find that the local boot component drag and drop encountered strange errors, is the third-party component in the development environment bug, you can take a step to solve:
> If you find that the local start-up component drag encountered strange errors, is a bug that should be a third-party component in the development environment, can be resolved in a way:
```sh
yarn dev
or
cnpm run dev
```
The premise is to install the http-server module first.
## Browsers support
Modern browsers does not support IE browser
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>IEdge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)</br>Safari |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Edge | last 2 versions | last 2 versions | last 2 versions |
## Contact us
<img src="http://next-admin.com/tech.png" alt="line" width=120 />
Twitter Account:@H5Dooring
## Partner project
- [ react-cropper-pro - 轻量强大的图片上传/裁切/压缩组件](https://github.com/MrXujiang/react-cropper-pro)
- [mitu-editor - 轻量级且强大的图片编辑器](https://github.com/H5-Dooring/mitu-editor)
- [powerNice - 一款轻量级文档管理编辑器](http://h5.dooring.cn/powernice/views)
- [rc-drag - 基于 react 的轻量级拖拽缩放组件](https://github.com/MrXujiang/rc-drag)
- [ t-nav - 开箱即用的开源导航项目](https://github.com/MrXujiang/t-nav)
- [Luckysheet - 强大的在线 excel 编辑器](https://github.com/mengshukeji/Luckysheet)
- [Blink - 一款自定义的生成故障艺术动画的组件库](https://github.com/MrXujiang/blink)
- [frontend-developer-roadmap | 一个能提高开发者工作效率的前端 js 库汇总](https://github.com/MrXujiang/frontend-developer-roadmap)
* [react-form-simple - 基于 react 开发的高性能表单库](https://github.com/easy-form/react-form-simple)
* [lucky-canvas 抽奖插件 | 一个支持 H5, 微信小程序, React 的抽奖插件](https://github.com/LuckDraw/lucky-canvas)
* [vue-admin-box | 免费并且开源的中后台管理系统模板](https://github.com/cmdparkour/vue-admin-box)
* [基于 antd 开箱即用的后台管理模版 ant-simple-pro](https://github.com/lgf196/ant-simple-pro)
* [使用 gin+vue 进行极速开发的全栈开发基础平台](https://github.com/flipped-aurora/gin-vue-admin)
* [DevUI 中后台产品开源前端解决方案](https://github.com/DevCloudFE/ng-devui)
## Sponsored
Open source is not easy, with your sponsorship, we will do better
<img src="http://cdn.dooring.cn/dr/WechatIMG2.jpeg" width="180px" />
## Technical feedback and communication
wechat:beautifulFront
<img src="http://cdn.dooring.cn/dr/qtqd_code.png" width="180px" />
================================================
FILE: server.js
================================================
const Koa = require("koa");
const { resolve } = require("path");
const staticServer = require("koa-static");
const koaBody = require("koa-body");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const app = new Koa();
app.use(staticServer(resolve(__dirname, "./static")));
app.use(koaBody());
app.use(logger());
// 设置跨域
app.use(
cors({
origin: function(ctx) {
if (ctx.url.indexOf("/dooring") > -1) {
return "*"; // 允许来自所有域名请求
}
return "";
},
exposeHeaders: ["WWW-Authenticate", "Server-Authorization", "x-test-code"],
maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒
credentials: true,
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: [
"Content-Type",
"Authorization",
"Accept",
"x-requested-with",
"Content-Encoding"
]
})
);
let htmlStr = "";
app.use(async (ctx, next) => {
console.log(ctx.url);
if (ctx.url === "/dooring/render") {
htmlStr = ctx.request.body;
ctx.body = "success";
} else if (ctx.url.indexOf("/html") === 0) {
ctx.type = "html";
ctx.body = htmlStr;
}
});
app.listen(3000);
================================================
FILE: src/app.tsx
================================================
import { createLogger } from "redux-logger";
import { message } from "antd";
import undoable, { StateWithHistory } from "redux-undo";
import { Reducer, AnyAction } from "redux";
export const dva = {
config: {
[process.env.NODE_ENV === "development" ? "onAction" : ""]: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
onReducer: (reducer: Reducer<any, AnyAction>) => {
let undoReducer = undoable(reducer);
return function(state: StateWithHistory<any>, action: AnyAction) {
let newState = undoReducer(state, action);
let router = newState.present.router
? newState.present.router
: newState.present.routing;
return { ...newState, router: router };
};
}
}
};
================================================
FILE: src/components/Calibration/index.less
================================================
.calibration {
width: calc(200% - 50px);
height: 200%;
position: relative;
white-space: nowrap;
pointer-events: none;
user-select: none;
:global(.calibrationNumber) {
font-size: 12px;
color: #888;
}
}
================================================
FILE: src/components/Calibration/index.tsx
================================================
import React, { useState, useEffect, useRef, useCallback } from "react";
import styles from "./index.less";
export interface calibrationTypes {
width: number;
height: number;
}
export type CalibrationTypes = {
direction: "up" | "left" | "right";
multiple: number;
id: string;
};
export default function Calibration(props: CalibrationTypes) {
const { direction, multiple } = props;
const [calibrationLength, setCalibration] = useState<calibrationTypes>({
width: 0,
height: 0
});
const calibrationRef = useRef<HTMLDivElement>(null);
const generateElement = useCallback(
(item?: boolean, num?: number) => {
if (calibrationRef.current) {
let createSpan = document.createElement("div");
createSpan.className = "calibrationLine";
createSpan.style.backgroundColor = "#ccc";
calibrationRef.current.style.display = "flex";
calibrationRef.current.style.justifyContent = "space-between";
if (direction === "up") {
calibrationRef.current.style.marginLeft = "50px";
createSpan.style.width = "1px";
createSpan.style.height = "6px";
createSpan.style.display = "inline-block";
} else {
calibrationRef.current.style.flexDirection = "column";
createSpan.style.height = "1px";
createSpan.style.width = "6px";
}
if (item) {
let createSpanContent = document.createElement("span");
if (direction === "up") {
createSpan.style.height = "12px";
createSpanContent.style.transform = "translate3d(-4px, 20px, 0px)";
createSpan.style.transform = "translateY(0px)";
} else {
createSpan.style.width = "12px";
createSpanContent.style.paddingLeft = "20px";
}
createSpanContent.style.display = "block";
createSpanContent.className = "calibrationNumber";
createSpanContent.innerHTML = num! * 5 + "";
createSpan.appendChild(createSpanContent);
}
calibrationRef.current.appendChild(createSpan);
}
},
[direction]
);
useEffect(() => {
if (calibrationRef.current) {
let calibration = calibrationRef.current.getBoundingClientRect();
setCalibration({ width: calibration.width, height: calibration.height });
let length = direction === "up" ? calibration.width : calibration.height;
for (let i = 0; i < length / 5; i++) {
if (i % 10 === 0) {
generateElement(true, i);
} else {
generateElement();
}
}
}
}, [direction, generateElement]);
useEffect(() => {
if (calibrationRef.current) {
let width = calibrationLength.width
? calibrationLength.width
: calibrationRef.current.getBoundingClientRect().width;
let height = calibrationLength.height
? calibrationLength.height
: calibrationRef.current.getBoundingClientRect().height;
let arr = [
...Array.from(
calibrationRef.current.querySelectorAll(".calibrationLine")
)
];
if (arr.length) {
if (direction === "up") {
calibrationRef.current.style.width =
parseFloat(multiple.toFixed(1)) * width + "px";
arr.forEach(el => {
let dom = [
...Array.from(el.querySelectorAll(".calibrationNumber"))
][0] as HTMLElement;
if (dom) {
dom.style.transform = `translate3d(-4px, 16px, 0px) scale(${(
multiple + 0.1
).toFixed(1)})`;
}
});
} else {
calibrationRef.current.style.height =
parseFloat(multiple.toFixed(1)) * height + "px";
arr.forEach(el => {
let dom = [
...Array.from(el.querySelectorAll(".calibrationNumber"))
][0] as HTMLElement;
if (dom) {
dom.style.transform = `translate3d(-4px, -8px, 0px) scale(${(
multiple + 0.1
).toFixed(1)})`;
}
});
}
}
}
}, [calibrationLength.height, calibrationLength.width, direction, multiple]);
return <div className={styles.calibration} ref={calibrationRef}></div>;
}
================================================
FILE: src/components/ErrorBundaries/index.tsx
================================================
import React, { ErrorInfo, PropsWithChildren } from "react";
interface ErrorBoundaryState {
hasError: boolean;
}
class ErrorBoundary extends React.Component<
PropsWithChildren<{}>,
ErrorBoundaryState
> {
constructor(props: PropsWithChildren<{}>) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(_error: Error, _info: ErrorInfo) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error to an error reporting service
//logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
================================================
FILE: src/components/FormComponents/CardPicker/index.less
================================================
.pickerWrap {
display: flex;
flex-wrap: wrap;
.picker {
display: inline-block;
padding: 10px;
border: 2px solid transparent;
cursor: pointer;
&:hover {
border-color: #4091f7;
}
&.selected {
border-color: #4091f7;
}
}
}
================================================
FILE: src/components/FormComponents/CardPicker/index.tsx
================================================
import { useState, useEffect, memo } from "react";
import classnames from "classnames";
import Icon from "@/materials/base/Icon";
import styles from "./index.less";
import React from "react";
import { IconTypes } from "@/materials/base/Icon/schema";
import { ICardPickerConfigType } from "../types";
interface CardPickerType
extends Omit<ICardPickerConfigType<IconTypes>, "type" | "key" | "name"> {
onChange?: (v: string) => void;
type: IconTypes;
}
export default memo((props: CardPickerType) => {
const { type, icons, onChange } = props;
const [selected, setSelected] = useState<IconTypes>(type);
const handlePicker = (v: IconTypes) => {
if (onChange) {
onChange(v);
return;
}
setSelected(v);
};
useEffect(() => {
setSelected(type);
}, [type]);
return (
<div className={styles.pickerWrap}>
{icons.map((item, i) => {
return (
<span
className={classnames(
styles.picker,
selected === item ? styles.selected : ""
)}
onClick={() => handlePicker(item)}
key={i}
>
<Icon type={item} size={20} color={"#4091f7"} spin={false} />
</span>
);
})}
</div>
);
});
================================================
FILE: src/components/FormComponents/Color/index.tsx
================================================
import React from "react";
import { SketchPicker, ColorResult } from "react-color";
import { rgba2Obj } from "@/utils/tool";
export type ColorConfigType = string;
//value 初始值传来,onchange item给的回调
interface ColorProps {
value?: ColorConfigType;
onChange?: (v: ColorConfigType) => void;
}
class colorPicker extends React.Component<ColorProps> {
state = {
displayColorPicker: false,
color: rgba2Obj(this.props.value)
};
handleClick = () => {
this.setState({ displayColorPicker: !this.state.displayColorPicker });
};
handleClose = () => {
this.setState({ displayColorPicker: false });
};
handleChange = (color: ColorResult) => {
this.setState({ color: color.rgb });
this.props.onChange &&
this.props.onChange(
`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`
);
};
render() {
return (
<div>
<div
style={{
// padding: '5px',
background: "#fff",
borderRadius: "1px",
boxShadow: "0 0 0 1px rgba(0,0,0,.1)",
display: "inline-block",
cursor: "pointer"
}}
onClick={this.handleClick}
>
<div
style={{
width: "20px",
height: "20px",
borderRadius: "2px",
background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
}}
/>
</div>
{this.state.displayColorPicker ? (
<React.Fragment>
<div
style={{
position: "absolute",
zIndex: 2000
}}
>
<SketchPicker
color={this.state.color}
onChange={this.handleChange}
/>
</div>
<div
style={{
position: "fixed",
top: "0px",
right: "0px",
bottom: "0px",
left: "0px",
zIndex: 1000
}}
onClick={this.handleClose}
/>
</React.Fragment>
) : null}
</div>
);
}
}
export default colorPicker;
================================================
FILE: src/components/FormComponents/DataList/editorModal.tsx
================================================
import React, { memo, useEffect, FC } from "react";
import { Form, Select, Input, Modal, Button } from "antd";
import Upload from "../Upload";
import { Store } from "antd/lib/form/interface";
import { TDataListDefaultTypeItem } from "../FormEditor/types";
// import styles from './index.less';
const normFile = (e: any) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
};
const { Option } = Select;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 }
};
export type EditorModalProps = {
visible: boolean;
onCancel:
| ((e: React.MouseEvent<HTMLElement, MouseEvent>) => void)
| undefined;
item?: TDataListDefaultTypeItem;
onSave: Function;
cropRate: number;
};
const EditorModal: FC<EditorModalProps> = props => {
const { item, onSave, visible, onCancel, cropRate } = props;
const onFinish = (values: Store) => {
console.log(values);
onSave && onSave(values);
};
const handleOk = () => {
form
.validateFields()
.then(values => {
if (item) {
values.id = item.id;
onSave && onSave(values);
}
})
.catch(err => {
console.log(err);
});
};
const [form] = Form.useForm();
useEffect(() => {
if (form && item && visible) {
form.resetFields();
}
}, [form, item, visible]);
return (
<>
{!!item && (
<Modal
title="编辑数据源"
closable={false}
visible={visible}
onOk={handleOk}
okText="确定"
forceRender
footer={
<Button type={"primary"} onClick={() => handleOk()}>
确定
</Button>
}
>
<Form
form={form}
name={`form_editor_modal`}
{...formItemLayout}
onFinish={onFinish}
initialValues={item}
>
<Form.Item
label="标题"
name="title"
rules={[{ required: true, message: "请输入标题!" }]}
>
<Input />
</Form.Item>
<Form.Item label="描述" name="desc">
<Input />
</Form.Item>
<Form.Item label="链接地址" name="link">
<Input />
</Form.Item>
{!!window["currentCates"] && (
<Form.Item
label="分类"
name="type"
rules={[{ required: true, message: "请选择分类!" }]}
>
<Select placeholder="请选择">
{window["currentCates"].map((v, i) => {
return (
<Option value={i} key={i}>
{v}
</Option>
);
})}
</Select>
</Form.Item>
)}
<Form.Item
label="上传图片"
name="imgUrl"
valuePropName="fileList"
getValueFromEvent={normFile}
>
<Upload cropRate={cropRate} isCrop />
</Form.Item>
</Form>
</Modal>
)}
</>
);
};
export default memo(EditorModal);
================================================
FILE: src/components/FormComponents/DataList/index.less
================================================
.dataList {
padding: 6px 10px;
border: 1px solid #f0f0f0;
text-align: justify;
padding-left: 10px;
padding-top: 10px;
}
.listItem {
position: relative;
padding-bottom: 6px;
margin-bottom: 6px;
border-bottom: 1px solid #f0f0f0;
&:hover {
.actionBar {
display: block;
}
}
&:last-child {
border-bottom: none;
margin-bottom: 0;
}
.tit {
font-weight: bold;
padding-bottom: 5px;
}
.desc {
font-size: 12px;
color: #ccc;
}
.actionBar {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: none;
background: #fff;
box-shadow: -20px 0 10px 10px #fff;
.action {
margin-right: 18px;
cursor: pointer;
&:last-child {
cursor: move;
}
}
}
}
================================================
FILE: src/components/FormComponents/DataList/index.tsx
================================================
import React, { memo, useState, useEffect, useCallback } from "react";
import {
EditOutlined,
MinusCircleOutlined,
MenuOutlined
} from "@ant-design/icons";
import { Button } from "antd";
import {
DragSource,
DropTarget,
DndProvider,
ConnectDropTarget,
DragSourceSpec,
DropTargetConnector,
DragSourceMonitor,
DragSourceConnector,
DropTargetSpec,
ConnectDragSource,
ConnectDragPreview
} from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import EditorModal from "./editorModal";
import { uuid } from "@/utils/tool";
import styles from "./index.less";
import { TDataListDefaultType, TDataListDefaultTypeItem } from "../types";
type ListItemProps = DndItemProps & {
isDragging: boolean;
connectDragSource: ConnectDragSource;
connectDragPreview: ConnectDragPreview;
connectDropTarget: ConnectDropTarget;
};
function ListItem(props: ListItemProps) {
const {
title,
desc,
onDel,
onEdit,
// 这些 props 由 React DnD注入,参考`collect`函数定义
isDragging,
connectDragSource,
connectDragPreview,
connectDropTarget
} = props;
const opacity = isDragging ? 0.5 : 1;
return connectDropTarget(
// 列表项本身作为 Drop 对象
connectDragPreview(
// 整个列表项作为跟随拖动的影像
<div className={styles.listItem} style={Object.assign({}, { opacity })}>
<div className={styles.tit}>{title}</div>
<div className={styles.desc}>{desc}</div>
<div className={styles.actionBar}>
<span className={styles.action} onClick={() => onEdit()}>
<EditOutlined />
</span>
<span className={styles.action} onClick={() => onDel()}>
<MinusCircleOutlined />
</span>
{connectDragSource(
<span className={styles.action}>
<MenuOutlined />
</span>
) // 拖动图标作为 Drag 对象
}
</div>
</div>
)
);
}
type DndItemProps = TDataListDefaultTypeItem & {
onDel: Function;
onEdit: Function;
key: number;
find: Function;
move: Function;
type?: number;
};
const type = "item";
type DragObject = {
id: string;
originalIndex: number;
};
const dragSpec: DragSourceSpec<DndItemProps, DragObject> = {
// 拖动开始时,返回描述 source 数据。后续通过 monitor.getItem() 获得
beginDrag: props => ({
id: props.id,
originalIndex: props.find(props.id).index
}),
// 拖动停止时,处理 source 数据
endDrag(props, monitor) {
const { id: droppedId, originalIndex } = monitor.getItem();
const didDrop = monitor.didDrop();
// source 是否已经放置在 target
if (!didDrop) {
return props.move(droppedId, originalIndex);
}
}
};
const dragCollect = (
connect: DragSourceConnector,
monitor: DragSourceMonitor
) => ({
connectDragSource: connect.dragSource(), // 用于包装需要拖动的组件
connectDragPreview: connect.dragPreview(), // 用于包装需要拖动跟随预览的组件
isDragging: monitor.isDragging() // 用于判断是否处于拖动状态
});
const dropSpec: DropTargetSpec<DndItemProps> = {
canDrop: () => false, // item 不处理 drop
hover(props, monitor) {
const { id: draggedId } = monitor.getItem();
const { id: overId } = props;
// 如果 source item 与 target item 不同,则交换位置并重新排序
if (draggedId !== overId) {
const { index: overIndex } = props.find(overId);
props.move(draggedId, overIndex);
}
}
};
const dropCollect = (connect: DropTargetConnector) => ({
connectDropTarget: connect.dropTarget() // 用于包装需接收拖拽的组件
});
const DndItem = DropTarget(
type,
dropSpec,
dropCollect
)(DragSource(type, dragSpec, dragCollect)(ListItem));
export type DataListMemo = {
onChange?: (v: TDataListDefaultType) => void;
value?: TDataListDefaultType;
cropRate: number;
};
export type DataListType = DataListMemo & {
connectDropTarget: ConnectDropTarget;
};
const List = function(props: DataListType) {
const { onChange, value, connectDropTarget, cropRate } = props;
const [list, setList] = useState(value);
const [visible, setVisible] = useState(false);
const [curItem, setCurItem] = useState<TDataListDefaultTypeItem>();
const handleDel = (id: string) => {
if (value && onChange) {
let newVal = value.filter(item => id !== item.id);
onChange(newVal);
}
};
const find = (id: string) => {
const item = list!.find(c => `${c.id}` === id)!;
return {
item,
index: list!.indexOf(item!)
};
};
const move = (id: string, toIndex: number) => {
const { item, index } = find(id);
const oldList = [...list!];
oldList.splice(index, 1);
oldList.splice(toIndex, 0, item);
if (onChange) {
onChange(oldList);
return;
}
setList(oldList);
};
const handleCancel = useCallback(() => {
console.log("a");
setVisible(false);
}, []);
const handleEdit = useCallback((item: TDataListDefaultTypeItem) => {
console.log("b");
setVisible(true);
setCurItem(item);
}, []);
const handleSave = useCallback(
(item: TDataListDefaultTypeItem) => {
console.log("c");
setVisible(false);
if (onChange) {
onChange(list!.map(p => (p.id === item.id ? item : p)));
return;
}
setList(prev => prev!.map(p => (p.id === item.id ? item : p)));
},
[list, onChange]
);
const handleAdd = () => {
const item = {
title: "新增项标题",
desc: "新增项描述",
id: uuid(8, 10),
imgUrl: [],
link: ""
};
if (onChange) {
onChange([...list!, item]);
return;
}
setList([...list!, item]);
};
useEffect(() => {
setList(value);
}, [value]);
return connectDropTarget(
<div className={styles.dataList}>
{!!(list && list.length) &&
list.map((item, i) => (
<DndItem
{...item}
onDel={() => handleDel(item.id)}
onEdit={() => handleEdit(item)}
key={i}
id={`${item.id}`}
find={find}
move={move}
/>
))}
<div style={{ marginTop: "10px" }}>
<Button onClick={handleAdd} block>
添加
</Button>
</div>
<EditorModal
visible={visible}
onCancel={handleCancel}
item={curItem}
onSave={handleSave}
cropRate={cropRate}
/>
</div>
);
};
const DndList = DropTarget(type, {}, connect => ({
connectDropTarget: connect.dropTarget()
}))(List);
// 将 HTMLBackend 作为参数传给 DragDropContext
export default memo((props: DataListMemo) => {
return (
<DndProvider backend={HTML5Backend}>
<DndList {...props} />
</DndProvider>
);
});
================================================
FILE: src/components/FormComponents/FormItems/EditorModal.tsx
================================================
import React, { FC, memo, useEffect } from "react";
import { Form, Select, Input, Modal, Button, InputNumber } from "antd";
import { baseFormOptionsType } from "../types";
import Color from "../Color";
const { Option } = Select;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 14 }
};
interface EditorModalProps {
item: any;
onSave: (data: any) => void;
visible: boolean;
}
const EditorModal: FC<EditorModalProps> = props => {
const { item, onSave, visible } = props;
const onFinish = (values: any) => {
onSave && onSave(values);
};
const handleOk = () => {
form
.validateFields()
.then(values => {
values.id = item.id;
onSave && onSave(values);
})
.catch(err => {
console.log(err);
});
};
const [form] = Form.useForm();
useEffect(() => {
if (form && item && visible) {
form.resetFields();
}
}, [form, item, visible]);
return (
<>
{!!item && (
<Modal
title="编辑表单组件"
footer={
<div>
<Button type="primary" onClick={() => handleOk()}>
确定
</Button>
</div>
}
forceRender
visible={visible}
onOk={handleOk}
closable={false}
>
<Form
form={form}
name={`formItem_editor_modal`}
{...formItemLayout}
onFinish={onFinish}
initialValues={item}
>
{
<Form.Item label="类型" name="type" hidden>
<Input />
</Form.Item>
}
{!!item.label && (
<Form.Item
label="字段名"
name="label"
rules={[{ required: true, message: "请输入字段名!" }]}
>
<Input />
</Form.Item>
)}
{!!item.fontSize && (
<Form.Item
label="字体大小"
name="fontSize"
rules={[{ required: true, message: "请输入字体大小!" }]}
>
<InputNumber min={12} max={30} defaultValue={14} />
</Form.Item>
)}
{!!item.color && (
<Form.Item
label="文字颜色"
name="color"
rules={[{ required: true, message: "请输入文字颜色!" }]}
>
<Color />
</Form.Item>
)}
{!!item.placeholder && (
<Form.Item label="提示文本" name="placeholder">
<Input placeholder="请输入提示文本" />
</Form.Item>
)}
{!!item.options && (
<Form.Item
label="选项源"
name="options"
rules={[{ required: true, message: "选项不能为空!" }]}
>
<Select
placeholder="请输入"
mode="tags"
labelInValue
maxTagCount={39}
maxTagTextLength={16}
>
{item.options.map((v: baseFormOptionsType, i: number) => {
return (
<Option value={v.value} key={i}>
{v.label}
</Option>
);
})}
</Select>
</Form.Item>
)}
</Form>
</Modal>
)}
</>
);
};
export default memo(EditorModal);
================================================
FILE: src/components/FormComponents/FormItems/FormItems.tsx
================================================
import React, {
memo,
RefObject,
useCallback,
useEffect,
useState
} from "react";
import BaseForm from "@/materials/base/Form/BaseForm";
import BasePopoverForm from "@/materials/base/Form/BasePopoverForm";
import EditorModal from "./EditorModal";
import { MinusCircleFilled, EditFilled, PlusOutlined } from "@ant-design/icons";
import styles from "./formItems.less";
import { baseFormUnion, TFormItemsDefaultType } from "../types";
import { uuid } from "@/utils/tool";
import { Button } from "antd";
import MyPopover from "yh-react-popover";
// import { Popconfirm } from 'antd';
const formTpl: TFormItemsDefaultType = [
{
id: "1",
type: "Text",
label: "文本框",
placeholder: "请输入文本"
},
{
id: "2",
type: "Textarea",
label: "长文本框",
placeholder: "请输入长文本请输入长文本"
},
{
id: "3",
type: "Number",
label: "数值",
placeholder: " 请输入数值"
},
{
id: "4",
type: "MyRadio",
label: "单选框",
options: [
{ label: "选项一", value: "1" },
{ label: "选项二", value: "2" }
]
},
{
id: "5",
type: "MySelect",
label: "下拉选择框",
options: [
{ label: "选项一", value: "1" },
{ label: "选项二", value: "2" },
{ label: "选项三", value: "3" }
]
},
{
id: "6",
type: "Date",
label: "日期框",
placeholder: ""
},
{
id: "7",
type: "MyTextTip",
label: "纯文本",
fontSize: 12,
color: "rgba(0,0,0,1)"
}
];
interface FormItemsProps {
formList?: TFormItemsDefaultType;
onChange?: (v: TFormItemsDefaultType) => void;
data: any;
rightPannelRef: RefObject<HTMLDivElement>;
}
const FormItems = (props: FormItemsProps) => {
const { formList, onChange, rightPannelRef } = props;
const [formData, setFormData] = useState<TFormItemsDefaultType>(
formList || []
);
const [visible, setVisible] = useState(false);
const [curItem, setCurItem] = useState<baseFormUnion>();
const [force, setforce] = useState<{ force: Function }>({
force: () => {}
});
const handleAddItem = (item: baseFormUnion) => {
let tpl = formTpl.find(v => v.type === item.type);
let newData = [...formData, { ...tpl!, id: uuid(6, 10) }];
setFormData(newData);
onChange && onChange(newData);
force.force();
};
const handleEditItem = (item: baseFormUnion) => {
setVisible(true);
setCurItem(item);
};
const handleDelItem = (item: baseFormUnion) => {
let newData = formData.filter(v => v.id !== item.id);
setFormData(newData);
onChange && onChange(newData);
};
const handleSaveItem = (data: baseFormUnion) => {
let newData = formData.map(v => (v.id === data.id ? data : v));
setFormData(newData);
onChange && onChange(newData);
setVisible(false);
};
const callback = useCallback((v: Function) => {
console.log(v);
setforce({ force: v });
}, []);
useEffect(() => {
let listenner: (e: Event) => void;
if (rightPannelRef.current) {
listenner = () => {
force.force();
};
rightPannelRef.current.addEventListener("scroll", listenner);
}
return () => {
if (rightPannelRef.current) {
// eslint-disable-next-line react-hooks/exhaustive-deps
rightPannelRef.current.removeEventListener("scroll", listenner);
}
};
}, [force, rightPannelRef]);
return (
<div className={styles.formItemWrap}>
<div className={styles.formTitle}>表单控件</div>
<div className={styles.editForm}>
{formData.map((item: baseFormUnion, i: number) => {
let FormItem = BaseForm[item.type];
return (
<div className={styles.formItem} key={i}>
<div className={styles.disClick}>
<FormItem {...item} />
</div>
<div className={styles.deleteWrap}>
<span
className={styles.operationBtn}
onClick={() => handleDelItem(item)}
>
<MinusCircleFilled />
</span>
</div>
<div className={styles.editWrap}>
<span
className={styles.operationBtn}
onClick={() => handleEditItem(item)}
>
<EditFilled />
</span>
</div>
</div>
);
})}
<div className={styles.formAddWrap}>
<MyPopover
content={
<>
<div className={styles.formTpl} style={{ color: "red" }}>
{formTpl.map((item, i) => {
let FormItem = BasePopoverForm[item.type];
return (
<div
className={styles.formItem}
key={i}
onClick={() => handleAddItem(item)}
>
<div
className={styles.disClick}
style={{
display: "flex",
flexDirection: "column",
overflow: "row",
marginTop: "10px"
}}
>
<FormItem {...item} />
</div>
</div>
);
})}
</div>
{/* <a style={{color: 'red'}} onClick={() => setFormTplVisible(false)}>Close</a> */}
</>
}
directions={"LB"}
innerConstDomStyle={{ display: "block" }}
constDomStyle={{ display: "block" }}
callback={callback}
>
<Button style={{ width: "100%" }} block icon={<PlusOutlined />}>
添加
</Button>
</MyPopover>
</div>
</div>
<EditorModal item={curItem} onSave={handleSaveItem} visible={visible} />
</div>
);
};
export default memo(FormItems);
================================================
FILE: src/components/FormComponents/FormItems/formItems.less
================================================
.formItemWrap {
.formTitle {
width: 56px;
height: 20px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: bold;
color: #000000;
line-height: 20px;
}
.editForm {
text-align: left;
width: 251px;
.formItem {
position: relative;
padding-left: 2px;
.common {
position: absolute;
top: 19px;
box-shadow: 0 0 20px #fff;
.operationBtn {
margin-right: 15px;
display: inline-block;
cursor: pointer;
}
}
.deleteWrap {
.common;
left: 0;
}
.editWrap {
.common;
right: -18px;
}
}
.formAddWrap {
font-size: 14px;
font-weight: 400;
color: #4a4a4a;
line-height: 20px;
background-color: #2f54eb;
}
}
.formAddWrap {
.formTpl {
margin-top: 12px;
border-top: 1px dashed #ccc;
padding-top: 16px;
background-color: #4a4a4a;
.formItem {
button,
[type="button"] {
color: #fff;
background-color: #4a4a4a;
border: 1px solid #fff;
border-radius: 4px 0px 0px 0px;
}
position: relative;
border: 1px solid #ccc;
margin-bottom: 2px;
background-color: #4a4a4a;
cursor: pointer;
.disClick {
pointer-events: none;
color: #fff;
}
&:hover {
border-color: #2f54eb;
.addBtn {
display: inline-block;
}
}
.addBtn {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: none;
padding: 3px 6px;
color: #fff;
border-radius: 3px;
background-color: #2f54eb;
cursor: pointer;
}
}
}
}
}
================================================
FILE: src/components/FormComponents/FormItems/index.tsx
================================================
import FormItems from "./FormItems";
export default FormItems;
================================================
FILE: src/components/FormComponents/MutiText/index.less
================================================
.mutiText {
.iptWrap {
margin-bottom: 12px;
display: flex;
.delBtn {
// font-size: 14px;
margin-left: 12px;
cursor: pointer;
align-self: center;
}
}
}
================================================
FILE: src/components/FormComponents/MutiText/index.tsx
================================================
import React, { memo, useEffect } from "react";
import { Input, Button, Popconfirm } from "antd";
import { MinusCircleFilled } from "@ant-design/icons";
import styles from "./index.less";
import { TMutiTextDefaultType } from "../types";
type MultiTextProps = {
onChange?: (v: TMutiTextDefaultType) => void;
value?: TMutiTextDefaultType;
};
export default memo(function MutiText(props: MultiTextProps) {
const { value, onChange } = props;
const handleAdd = () => {
onChange && onChange([...value!, "新增项目"]);
};
const handleDel = (index: number) => {
let newList = value!.filter((_item, i) => i !== index);
onChange && onChange(newList);
};
const handleChange = (
index: number,
e: React.ChangeEvent<HTMLInputElement>
) => {
let newList = value!.map((item, i) =>
i === index ? e.target.value : item
);
onChange && onChange(newList);
};
useEffect(() => {
window["currentCates"] = value!;
return () => {
window["currentCates"] = null;
};
}, [value]);
return (
<div className={styles.mutiText}>
{value && value.length ? (
value!.map((item, i) => {
return (
<div className={styles.iptWrap} key={i}>
<Input defaultValue={item} onChange={e => handleChange(i, e)} />
<Popconfirm
title="确定要删除吗?"
onConfirm={() => handleDel(i)}
placement="leftTop"
okText="确定"
cancelText="取消"
>
<span className={styles.delBtn}>
<MinusCircleFilled />
</span>
</Popconfirm>
</div>
);
})
) : (
<div className={styles.iptWrap}>
<Input />
</div>
)}
{value && value.length < 3 && (
<div className={styles.iptWrap}>
<Button block onClick={handleAdd}>
添加项目
</Button>
</div>
)}
</div>
);
});
================================================
FILE: src/components/FormComponents/Pos/index.less
================================================
.posIpt {
display: flex;
justify-content: flex-end;
margin-right: -10px;
.posItem {
margin-right: 10px;
span {
margin-right: 3px;
}
}
}
================================================
FILE: src/components/FormComponents/Pos/index.tsx
================================================
import React, { memo, useState, useEffect } from "react";
import { InputNumber } from "antd";
import styles from "./index.less";
import { TPosDefaultType, TPosItem } from "../types";
type PosProps = {
value?: TPosDefaultType;
onChange?: (v: TPosItem | string) => void;
};
export default memo(function Pos(props: PosProps) {
const { value, onChange } = props;
let _this: typeof Pos = Pos;
const handleChange = (index: number, v: TPosItem | string) => {
let arr: any = value || [];
arr[index] = v;
onChange && onChange(arr);
};
return (
<div className={styles.posIpt}>
<div className={styles.posItem}>
<span>x: </span>
<InputNumber
defaultValue={value && value[0]}
onChange={handleChange.bind(_this, 0)}
/>
</div>
<div className={styles.posItem}>
<span>y: </span>
<InputNumber
defaultValue={value && value[1]}
onChange={handleChange.bind(_this, 1)}
/>
</div>
</div>
);
});
================================================
FILE: src/components/FormComponents/Table/index.less
================================================
:global(.editable-cell) {
position: relative;
}
:global(.editable-cell-value-wrap) {
padding: 5px 12px;
cursor: pointer;
}
:global(.editable-row) {
&:hover :global(.editable-cell-value-wrap) {
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 4px 11px;
}
}
:global([data-theme="dark"]) {
:global(.editable-row) {
&:hover {
:global(.editable-cell-value-wrap) {
border: 1px solid #434343;
}
}
}
}
.apiForm {
.formItem {
margin-bottom: 16px;
}
}
================================================
FILE: src/components/FormComponents/Table/index.tsx
================================================
import React, {
useContext,
useState,
useEffect,
useRef,
memo,
RefObject
} from "react";
import { Table, Input, Button, Popconfirm, Form, Modal, Upload } from "antd";
import { ColumnsType } from "antd/lib/table";
import { uuid } from "@/utils/tool";
import XLSX from "xlsx";
// 下方样式主要为全局样式,暂时不可删
import styles from "./index.less";
const EditableContext = React.createContext<any>(null);
interface Item {
key: string;
name: string;
age: string;
address: string;
}
interface EditableRowProps {
index: number;
}
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: string;
record: any;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef.current?.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log("Save failed:", errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[
{
required: true,
message: `${title} 是必填的.`
}
]}
>
<Input
ref={(inputRef as unknown) as () => RefObject<HTMLInputElement>}
onPressEnter={save}
onBlur={save}
/>
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
class EditableTable extends React.Component<any, any> {
columns: (
| {
title: string;
dataIndex: string;
width: string;
editable: boolean;
render?: undefined;
}
| {
title: string;
dataIndex: string;
render: (text: string, record: any) => JSX.Element | null;
width?: undefined;
editable?: undefined;
}
)[];
apiForm: {
api: string;
header: string;
dataField: string;
};
constructor(props: any) {
super(props);
this.columns = [
{
title: "名字",
dataIndex: "name",
width: "180px",
editable: true
},
{
title: "值",
dataIndex: "value",
width: "120px",
editable: true
},
{
title: "操作",
dataIndex: "operation",
render: (text: string, record) =>
this.state.dataSource.length >= 1 ? (
<Popconfirm
title="Sure to delete?"
onConfirm={() => this.handleDelete(record.key)}
>
<Button type="link">删除</Button>
</Popconfirm>
) : null
}
];
this.apiForm = {
api: "",
header: "",
dataField: ""
};
const dataSource =
props.data &&
props.data.map((item: any, i: number) => ({ key: i + "", ...item }));
this.state = {
dataSource: dataSource,
visible: false,
apiVisible: false,
apiResult: ""
};
}
handleDelete = (key: string) => {
const dataSource = [...this.state.dataSource];
const newDataSource = dataSource.filter(item => item.key !== key);
this.setState({ dataSource: newDataSource });
this.props.onChange && this.props.onChange(newDataSource);
};
handleAdd = () => {
const { dataSource } = this.state;
const uid = uuid(8, 10);
const newData = {
key: uid,
name: `dooring ${dataSource.length + 1}`,
value: 32
};
const newDataSource = [...dataSource, newData];
this.setState({
dataSource: newDataSource
});
this.props.onChange && this.props.onChange(newDataSource);
};
handleSave = (row: any) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row
});
this.setState({ dataSource: newData });
this.props.onChange && this.props.onChange(newData);
};
showModal = () => {
this.setState({
visible: true
});
};
handleOk = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setState({
visible: false
});
};
handleCancel = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setState({
visible: false
});
};
showApiModal = () => {
this.setState({
apiVisible: true
});
};
handleAPIOk = () => {
const { dataField } = this.apiForm;
if (dataField) {
let data = this.state.apiResult[dataField];
if (data && data instanceof Array) {
data = data.map((item, i) => ({ key: i + "", ...item }));
this.setState({
dataSource: data
});
this.props.onChange && this.props.onChange(data);
}
this.setState({
apiVisible: false
});
}
};
handleAPICancel = () => {
this.setState({
apiVisible: false
});
};
handleApiField = (type: "api" | "header" | "dataField", v: string) => {
this.apiForm[type] = v;
};
getApiFn = () => {
console.log(this.apiForm);
const { api, header } = this.apiForm;
fetch(api, {
cache: "no-cache",
headers: Object.assign(
{ "content-type": "application/json" },
header ? JSON.parse(header) : {}
),
method: "GET",
mode: "cors"
})
.then(res => res.json())
.then(res => {
this.setState({
apiResult: res
});
});
};
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableRow,
cell: EditableCell
}
};
const columns: ColumnsType<any> = this.columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave
})
};
});
const _this = this;
const props = {
name: "file",
// action: '',
showUploadList: false,
beforeUpload(file: File, fileList: Array<File>) {
// 解析并提取excel数据
let reader = new FileReader();
reader.onload = function(e: any) {
let data = e.target.result;
let workbook = XLSX.read(data, { type: "binary" });
let sheetNames = workbook.SheetNames; // 工作表名称集合
let draftArr: any = {};
sheetNames.forEach(name => {
let worksheet = workbook.Sheets[name]; // 只能通过工作表名称来获取指定工作表
for (let key in worksheet) {
// v是读取单元格的原始值
if (key[0] !== "!") {
if (draftArr[key[0]]) {
draftArr[key[0]].push(worksheet[key].v);
} else {
draftArr[key[0]] = [worksheet[key].v];
}
}
}
});
let sourceData = Object.values(draftArr).map((item: any, i) => ({
key: i + "",
name: item[0],
value: item[1]
}));
_this.setState({
dataSource: sourceData
});
_this.props.onChange && _this.props.onChange(sourceData);
};
reader.readAsBinaryString(file);
}
};
return (
<div>
<Button type="primary" onClick={this.showModal}>
编辑数据源
</Button>
<Modal
title="编辑数据源"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
okText="确定"
cancelText="取消"
>
<Button
onClick={this.handleAdd}
type="primary"
style={{ marginBottom: 16, marginRight: 16 }}
>
添加行
</Button>
<Upload {...props}>
<Button type="primary" ghost style={{ marginRight: 16 }}>
导入Excel
</Button>
</Upload>
<Button type="primary" ghost onClick={this.showApiModal}>
第三方API
</Button>
<Table
components={components}
rowClassName={() => "editable-row"}
bordered
dataSource={dataSource}
columns={columns}
pagination={{ pageSize: 50 }}
scroll={{ y: 240 }}
/>
</Modal>
<Modal
title="配置api"
visible={this.state.apiVisible}
onOk={this.handleAPIOk}
onCancel={this.handleAPICancel}
okText="确定"
cancelText="取消"
>
<div className={styles.apiForm}>
<div className={styles.formItem}>
<Input
placeholder="请输入api地址"
onChange={e => this.handleApiField("api", e.target.value)}
/>
</div>
<div className={styles.formItem}>
<Input.TextArea
placeholder="请输入头信息, 如{token: 123456}, 格式必须为json对象"
rows={4}
onChange={e => this.handleApiField("header", e.target.value)}
/>
</div>
<div className={styles.formItem}>
<Button type="primary" onClick={this.getApiFn}>
发送请求
</Button>
</div>
{this.state.apiResult && (
<>
<div className={styles.formItem}>
<Input.TextArea
rows={6}
value={JSON.stringify(this.state.apiResult, null, 4)}
/>
</div>
<div className={styles.formItem}>
<Input
placeholder="设置数据源字段"
onChange={e =>
this.handleApiField("dataField", e.target.value)
}
/>
<p style={{ color: "red" }}>
数据源字段是接口返回的图表数据对应的字段, 必填,
否则无法正确导入数据
</p>
</div>
</>
)}
</div>
</Modal>
</div>
);
}
}
export default memo(EditableTable);
================================================
FILE: src/components/FormComponents/Upload/index.less
================================================
:global(.ant-upload-select-picture-card i) {
color: #999;
font-size: 14px;
}
:global(.ant-upload-select-picture-card .ant-upload-text) {
margin-top: 8px;
color: #666;
}
.avatarUploader {
display: inline-block;
text-align: left;
}
.wallBtn {
position: absolute;
left: 140px;
bottom: 56px;
display: inline-block;
color: #2f54eb;
cursor: pointer;
border-bottom: 1px solid #2f54eb;
}
.imgBox {
display: flex;
flex-wrap: wrap;
max-height: 520px;
overflow: auto;
.imgItem {
position: relative;
margin-right: 16px;
margin-bottom: 16px;
width: 320px;
max-height: 220px;
overflow: hidden;
cursor: pointer;
img {
width: 100%;
}
&:hover,
&.seleted {
.iconBtn {
visibility: visible;
}
}
.iconBtn {
position: absolute;
visibility: hidden;
top: 6px;
right: 10px;
font-size: 18px;
color: rgb(8, 156, 8);
}
}
}
================================================
FILE: src/components/FormComponents/Upload/index.tsx
================================================
import React from "react";
import { Upload, Modal, message, Tabs, Result } from "antd";
import { PlusOutlined, CheckCircleFilled } from "@ant-design/icons";
import ImgCrop from "antd-img-crop";
import classnames from "classnames";
import {
UploadFile,
UploadChangeParam,
RcFile
} from "antd/lib/upload/interface";
import { isDev, unParams, uuid } from "@/utils/tool";
import req from "@/utils/req";
import styles from "./index.less";
const { TabPane } = Tabs;
// 维护图片分类映射
const wallCateName: any = {
photo: "照片",
bg: "背景",
chahua: "插画"
};
function getBase64(file: File | Blob) {
return new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = error => reject(error);
});
}
interface PicturesWallType {
fileList?: UploadFile<any>[];
action?: string;
headers?: any;
withCredentials?: boolean;
maxLen?: number;
onChange?: (v: any) => void;
cropRate?: number | boolean;
isCrop?: boolean;
}
class PicturesWall extends React.Component<PicturesWallType> {
state = {
previewVisible: false,
previewImage: "",
wallModalVisible: false,
previewTitle: "",
imgBed: {
photo: [],
bg: [],
chahua: []
},
curSelectedImg: "",
fileList: this.props.fileList || []
};
handleCancel = () => this.setState({ previewVisible: false });
handleModalCancel = () => this.setState({ wallModalVisible: false });
handlePreview = async (file: UploadFile<any>) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj!);
}
this.setState({
previewImage: file.url || file.preview,
previewVisible: true,
previewTitle:
file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1)
});
};
handleWallSelect = (url: string) => {
this.setState({
wallModalVisible: true
});
};
handleImgSelected = (url: string) => {
this.setState({
curSelectedImg: url
});
};
handleWallShow = () => {
this.setState({
wallModalVisible: true
});
};
handleModalOk = () => {
const fileList = [
{
uid: uuid(8, 16),
name: "h5-dooring图片库",
status: "done",
url: this.state.curSelectedImg
}
];
this.props.onChange && this.props.onChange(fileList);
this.setState({ fileList, wallModalVisible: false });
};
handleChange = ({ file, fileList }: UploadChangeParam<UploadFile<any>>) => {
this.setState({ fileList });
if (file.status === "done") {
const files = fileList.map(item => {
const { uid, name, status } = item;
const url = item.url || item.response.result.url;
return { uid, name, status, url };
});
this.props.onChange && this.props.onChange(files);
}
};
handleBeforeUpload = (file: RcFile) => {
const isJpgOrPng =
file.type === "image/jpeg" ||
file.type === "image/png" ||
file.type === "image/jpg" ||
file.type === "image/gif";
if (!isJpgOrPng) {
message.error("只能上传格式为jpeg/png/gif的图片");
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("图片必须小于2MB!");
}
return isJpgOrPng && isLt2M;
};
componentDidMount() {
// req.get(`/visible/bed/get?tid=${unParams(location.search)!.tid}`).then(res => {
// res &&
// this.setState({
// imgBed: res,
// });
// });
}
render() {
const {
previewVisible,
previewImage,
fileList,
previewTitle,
wallModalVisible,
imgBed,
curSelectedImg
} = this.state;
const {
action = isDev
? "http://192.168.1.8:3000/api/v0/files/upload/free"
: "你的服务器地址",
headers,
withCredentials = true,
maxLen = 1,
cropRate = 375 / 158,
isCrop
} = this.props;
const uploadButton = (
<div>
<PlusOutlined />
<div className="ant-upload-text">上传</div>
</div>
);
const cates = Object.keys(imgBed);
return (
<>
{isCrop ? (
<ImgCrop
modalTitle="裁剪图片"
modalOk="确定"
modalCancel="取消"
rotate={true}
aspect={cropRate}
>
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={action}
withCredentials={withCredentials}
headers={{
"x-requested-with": localStorage.getItem("user") || "",
authorization: localStorage.getItem("token") || "",
...headers
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
</ImgCrop>
) : (
<Upload
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
name="file"
listType="picture-card"
className={styles.avatarUploader}
action={action}
withCredentials={withCredentials}
headers={{
"x-requested-with": localStorage.getItem("user") || "",
authorization: localStorage.getItem("token") || "",
...headers
}}
beforeUpload={this.handleBeforeUpload}
>
{fileList.length >= maxLen ? null : uploadButton}
</Upload>
)}
<div className={styles.wallBtn} onClick={this.handleWallShow}>
图片库
</div>
<Modal
visible={previewVisible}
title={previewTitle}
footer={null}
onCancel={this.handleCancel}
>
<img alt="预览图片" style={{ width: "100%" }} src={previewImage} />
</Modal>
<Modal
visible={wallModalVisible}
title="图片库"
okText="确定"
cancelText="取消"
width={860}
onCancel={this.handleModalCancel}
onOk={this.handleModalOk}
>
<Tabs
defaultActiveKey={cates[0]}
tabPosition="left"
style={{ height: 520 }}
>
{cates.map((item, i) => {
return (
<TabPane tab={wallCateName[item]} key={item}>
<div className={styles.imgBox}>
{(imgBed as any)[item] &&
(imgBed as any)[item].map((item: string, i: number) => {
return (
<div
className={classnames(
styles.imgItem,
curSelectedImg === item ? styles.seleted : ""
)}
key={i}
onClick={() => this.handleImgSelected(item)}
>
<img src={item} alt="趣谈前端-h5-dooring" />
<span className={styles.iconBtn}>
<CheckCircleFilled />
</span>
</div>
);
})}
</div>
</TabPane>
);
})}
<TabPane tab="更多" key="more">
<Result
status="500"
title="Dooring温馨提示"
subTitle="更多素材, 正在筹备中..."
/>
</TabPane>
</Tabs>
</Modal>
</>
);
}
}
export default PicturesWall;
================================================
FILE: src/components/FormComponents/XEditor/index.less
================================================
.avatarUploader > :global(.ant-upload) {
width: 128px;
height: 128px;
}
================================================
FILE: src/components/FormComponents/XEditor/index.tsx
================================================
import React, { useState, useEffect, memo } from "react";
import req from "@/utils/req";
import BraftEditor from "braft-editor";
import "braft-editor/dist/index.css";
import styles from "./index.less";
const controls = [
{
key: "bold",
text: <b>加粗</b>
},
"undo",
"redo",
"emoji",
"list-ul",
"list-ol",
"blockquote",
"text-align",
"font-size",
"line-height",
"letter-spacing",
"text-color",
"italic",
"underline",
"link",
"media"
];
export default memo(function XEditor(props: any) {
const { value, onChange } = props;
const [editorState, setEditorState] = useState(
BraftEditor.createEditorState(value)
);
const myUploadFn = (param: any) => {
const fd = new FormData();
fd.append("file", param.file);
req
.post("xxxx", fd, {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: function(event) {
// 上传进度发生变化时调用param.progress
console.log((event.loaded / event.total) * 100);
param.progress((event.loaded / event.total) * 100);
}
})
.then((res: any) => {
// 上传成功后调用param.success并传入上传后的文件地址
param.success({
url: res.url,
meta: {
id: Date.now(),
title: res.filename,
alt: "趣谈前端"
}
});
})
.catch(err => {
param.error({
msg: "上传失败."
});
});
};
const submitContent = () => {
const htmlContent = editorState.toHTML();
onChange && onChange(htmlContent);
};
const handleEditorChange = editorState => {
setEditorState(editorState);
if (onChange) {
const htmlContent = editorState.toHTML();
onChange(htmlContent);
}
};
useEffect(() => {
const htmlContent = value || "";
setEditorState(BraftEditor.createEditorState(htmlContent));
}, []);
return (
<BraftEditor
value={editorState}
controls={controls}
onChange={handleEditorChange}
onSave={submitContent}
media={{ uploadFn: myUploadFn }}
/>
);
});
================================================
FILE: src/components/FormComponents/types.ts
================================================
////////////////////
export interface IUploadConfigType {
key: string;
name: string;
type: "Upload";
isCrop?: boolean;
cropRate?: number;
}
export type TUploadDefaultType = Array<{
uid: string;
name: string;
status: string;
url: string;
}>;
/////////////////
export interface ITextConfigType {
key: string;
name: string;
type: "Text";
}
export type TTextDefaultType = string;
////////////////////////
export interface ITextAreaConfigType {
key: string;
name: string;
type: "TextArea";
}
export type TTextAreaDefaultType = string;
////////////////////////////
export interface INumberConfigType {
key: string;
name: string;
type: "Number";
range?: [number, number];
step?: number;
}
export type TNumberDefaultType = number;
///////////////////
export interface IDataListConfigType {
key: string;
name: string;
type: "DataList";
cropRate: number;
}
export type TDataListDefaultTypeItem = {
id: string;
title: string;
desc: string;
link: string;
type?: number;
imgUrl: TUploadDefaultType;
};
export type TDataListDefaultType = Array<TDataListDefaultTypeItem>;
////////////////////
export interface IColorConfigType {
key: string;
name: string;
type: "Color";
}
export type TColorDefaultType = string;
/////////////////
export interface IRichTextConfigType {
key: string;
name: string;
type: "RichText";
}
export type TRichTextDefaultType = string;
export interface IMutiTextConfigType {
key: string;
name: string;
type: "MutiText";
}
export type TMutiTextDefaultType = Array<string>;
/////////////////////////////////
export interface ISelectConfigType<KeyType> {
key: string;
name: string;
type: "Select";
range: Array<{
key: KeyType;
text: string;
}>;
}
export type TSelectDefaultType<KeyType> = KeyType;
/////////////////////////
export interface IRadioConfigType<KeyType> {
key: string;
name: string;
type: "Radio";
range: Array<{
key: KeyType;
text: string;
}>;
}
export type TRadioDefaultType<KeyType> = KeyType;
///////////////
export interface ISwitchConfigType {
key: string;
name: string;
type: "Switch";
}
export type TSwitchDefaultType = boolean;
/////////////////////////////
export interface ICardPickerConfigType<T> {
key: string;
name: string;
type: "CardPicker";
icons: Array<T>;
}
export type TCardPickerDefaultType<T> = T;
/////////////
export interface ITableConfigType {
key: string;
name: string;
type: "Table";
}
export type TTableDefaultType = Array<{
name: string;
value: number;
}>;
// position input control
export interface IPosConfigType {
key: string;
name: string;
type: "Pos";
placeObj: {
text: string;
link: string;
};
}
export type TPosItem = number | undefined;
export type TPosDefaultType = [TPosItem, TPosItem];
//////////////////
export interface IFormItemsConfigType {
key: string;
name: string;
type: "FormItems";
}
//0---------baseform
export type baseFormOptionsType = {
label: string;
value: string;
};
export type baseFormTextTpl = {
id: string;
type: "Text";
label: string;
placeholder: string;
};
export type baseFormTextTipTpl = {
id: string;
type: "MyTextTip";
label: string;
color: string;
fontSize: number;
};
export type baseFormNumberTpl = {
id: string;
type: "Number";
label: string;
placeholder: string;
};
export type baseFormTextAreaTpl = {
id: string;
type: "Textarea";
label: string;
placeholder: string;
};
export type baseFormMyRadioTpl = {
id: string;
type: "MyRadio";
label: string;
options: baseFormOptionsType[];
};
export type baseFormMyCheckboxTpl = {
id: string;
type: "MyCheckbox";
label: string;
options: baseFormOptionsType[];
};
export type baseFormMySelectTpl = {
id: string;
type: "MySelect";
label: string;
options: baseFormOptionsType[];
};
export type baseFormDateTpl = {
id: string;
type: "Date";
label: string;
placeholder: string;
};
export type baseFormUnion =
| baseFormTextTpl
| baseFormTextTipTpl
| baseFormNumberTpl
| baseFormTextAreaTpl
| baseFormMyRadioTpl
| baseFormMyCheckboxTpl
| baseFormMySelectTpl
| baseFormDateTpl;
export type baseFormUnionType =
| baseFormTextTpl["type"]
| baseFormTextTipTpl["type"]
| baseFormNumberTpl["type"]
| baseFormTextAreaTpl["type"]
| baseFormMyRadioTpl["type"]
| baseFormMyCheckboxTpl["type"]
| baseFormMySelectTpl["type"]
| baseFormDateTpl["type"];
export type TFormItemsDefaultType = Array<baseFormUnion>;
================================================
FILE: src/components/LoadingCp/index.tsx
================================================
import React from "react";
export default () => <div className="rotate-animate">Dooring</div>;
================================================
FILE: src/components/Zan/index.less
================================================
.takeCat {
display: inline-block;
}
.imgWrap {
width: 160px;
img {
width: 100%;
}
}
================================================
FILE: src/components/Zan/index.tsx
================================================
import React, { memo } from "react";
import { Button, Popover } from "antd";
import styles from "./index.less";
interface IProps {
text: any;
}
///这组件写的有问题 popover会重定位
const content = (
<div className={styles.imgWrap}>
<img
src={`http://h5.dooring.cn/uploads/WechatIMG2_17969ccfe40.jpeg`}
alt="sponsorship"
/>
</div>
);
export default memo(function ZanPao(props: IProps) {
const {
text = (
<Button
type="primary"
danger
style={{ background: "red !important" }}
size="large"
>
支持开源, 请作者喝茶~
</Button>
)
} = props;
return (
<div className={styles.takeCat}>
<Popover placement="top" title={null} content={content} trigger="hover">
{text}
</Popover>
</div>
);
});
================================================
FILE: src/core/DynamicEngine.tsx
================================================
import { dynamic } from "umi";
import Loading from "../components/LoadingCp";
import { useMemo, memo, FC } from "react";
import React from "react";
export type componentsType = "media" | "base" | "visible" | "shop";
const DynamicFunc = (type: string, componentsType: string) => {
return dynamic({
loader: async function() {
const { default: Graph } = await import(
`@/materials/${componentsType}/${type}`
);
const Component = Graph;
return (props: DynamicType) => {
const { config, isTpl } = props;
return <Component {...config} isTpl={isTpl} />;
};
},
loading: () => (
<div style={{ paddingTop: 10, textAlign: "center" }}>
<Loading />
</div>
)
});
};
type DynamicType = {
isTpl: boolean;
config: { [key: string]: any };
type: string;
componentsType: componentsType;
category: string;
};
const DynamicEngine = memo((props: DynamicType) => {
const { type, config, category } = props;
const Dynamic = useMemo(() => {
return (DynamicFunc(type, category) as unknown) as FC<DynamicType>;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [config]);
return <Dynamic {...props} />;
});
export default DynamicEngine;
================================================
FILE: src/core/index.ts
================================================
import DynamicEngine from "./DynamicEngine";
import ViewRender from "./renderer/ViewRender";
import FormRender from "./renderer/FormRender";
export { DynamicEngine, ViewRender, FormRender };
================================================
FILE: src/core/renderer/FormRender.tsx
================================================
import React, { memo, RefObject, useEffect } from "react";
import { Form, Select, InputNumber, Input, Switch, Radio } from "antd";
import Upload from "../../components/FormComponents/Upload";
import DataList from "../../components/FormComponents/DataList";
import MutiText from "../../components/FormComponents/MutiText";
import Color from "../../components/FormComponents/Color";
import CardPicker from "../../components/FormComponents/CardPicker";
import Table from "../../components/FormComponents/Table";
import Pos from "../../components/FormComponents/Pos";
import { Store } from "antd/lib/form/interface";
import RichText from "../../components/FormComponents/XEditor";
import FormItems from "../../components/FormComponents/FormItems";
const normFile = (e: any) => {
console.log("Upload event:", e);
if (Array.isArray(e)) {
//待修改
return e;
}
return e && e.fileList;
};
const { Option } = Select;
const { TextArea } = Input;
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 }
};
interface FormEditorProps {
uid: string;
onSave: Function;
onDel: Function;
defaultValue: { [key: string]: any };
config: Array<any>;
rightPannelRef: RefObject<HTMLDivElement>;
}
const FormEditor = (props: FormEditorProps) => {
const { config, defaultValue, onSave, uid, rightPannelRef } = props;
const onFinish = (values: Store) => {
onSave && onSave(values);
};
const [form] = Form.useForm();
useEffect(() => {
return () => {
form.resetFields();
};
}, [uid, form]);
const handlechange = () => {
onFinish(form.getFieldsValue());
};
return (
<Form
form={form}
name={`form_editor`}
{...formItemLayout}
onFinish={onFinish}
initialValues={defaultValue}
onValuesChange={handlechange}
>
{config.map((item, i) => {
return (
<React.Fragment key={i}>
{item.type === "Number" && (
<Form.Item label={item.name} name={item.key}>
<InputNumber max={item.range && item.range[1]} />
</Form.Item>
)}
{item.type === "Text" && (
<Form.Item label={item.name} name={item.key}>
<Input />
</Form.Item>
)}
{item.type === "TextArea" && (
<Form.Item label={item.name} name={item.key}>
<TextArea rows={4} />
</Form.Item>
)}
{item.type === "MutiText" && (
<Form.Item label={item.name} name={item.key}>
<MutiText />
</Form.Item>
)}
{item.type === "DataList" && (
<Form.Item label={item.name} name={item.key}>
<DataList cropRate={item.cropRate} />
</Form.Item>
)}
{item.type === "Color" && (
<Form.Item label={item.name} name={item.key}>
<Color />
</Form.Item>
)}
{item.type === "Select" && (
<Form.Item label={item.name} name={item.key}>
<Select placeholder="请选择">
{item.range.map((v: any, i: number) => {
return (
<Option value={v.key} key={i}>
{v.text}
</Option>
);
})}
</Select>
</Form.Item>
)}
{item.type === "Radio" && (
<Form.Item label={item.name} name={item.key}>
<Radio.Group>
{item.range.map((v: any, i: number) => {
return (
<Radio value={v.key} key={i}>
{v.text}
</Radio>
);
})}
</Radio.Group>
</Form.Item>
)}
{item.type === "Switch" && (
<Form.Item
label={item.name}
name={item.key}
valuePropName="checked"
>
<Switch />
</Form.Item>
)}
{item.type === "Upload" && (
<Form.Item
label={item.name}
name={item.key}
valuePropName="fileList"
getValueFromEvent={normFile}
>
<Upload cropRate={item.cropRate} isCrop={item.isCrop} />
</Form.Item>
)}
{item.type === "CardPicker" && (
<Form.Item label={item.name} name={item.key} valuePropName="type">
<CardPicker icons={item.icons} type={defaultValue["type"]} />
</Form.Item>
)}
{item.type === "Table" && (
<Form.Item label={item.name} name={item.key} valuePropName="data">
<Table data={item.data} />
</Form.Item>
)}
{item.type === "Pos" && (
<Form.Item label={item.name} name={item.key}>
<Pos />
</Form.Item>
)}
{item.type === "FormItems" && (
<Form.Item name={item.key} valuePropName="formList">
<FormItems data={item.data} rightPannelRef={rightPannelRef} />
</Form.Item>
)}
{item.type === "RichText" && (
<Form.Item label={item.name} name={item.key} noStyle={true}>
<RichText />
</Form.Item>
)}
</React.Fragment>
);
})}
</Form>
);
};
export default memo(FormEditor);
================================================
FILE: src/core/renderer/ViewRender.tsx
================================================
import React, { memo } from "react";
import GridLayout, { ItemCallback } from "react-grid-layout";
import DynamicEngine from "@/core/DynamicEngine";
import styles from "./viewRender.less";
interface PointDataItem {
id: string;
item: Record<string, any>;
point: Record<string, any>;
}
interface ViewProps {
pointData: Array<PointDataItem>;
pageData?: any;
width?: number;
dragStop?: ItemCallback;
onDragStart?: ItemCallback;
onResizeStop?: ItemCallback;
}
const ViewRender = memo((props: ViewProps) => {
const {
pointData,
pageData,
width,
dragStop,
onDragStart,
onResizeStop
} = props;
return (
<GridLayout
cols={24}
rowHeight={2}
width={width}
margin={[0, 0]}
onDragStop={dragStop}
onDragStart={onDragStart}
onResizeStop={onResizeStop}
style={{
minHeight: "100vh",
backgroundColor: pageData && pageData.bgColor,
backgroundImage:
pageData && pageData.bgImage
? `url(${pageData.bgImage[0].url})`
: "initial",
backgroundSize: "100%",
backgroundRepeat: "no-repeat"
}}
>
{pointData.map((value: PointDataItem) => (
<div
key={value.id}
data-grid={value.point}
gitextract_819mm14l/ ├── .github/ │ ├── FUNDING.yml │ └── ISSUE_TEMPLATE/ │ └── custom.md ├── .gitignore ├── .umirc.ts ├── .vscode/ │ └── settings.json ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── SECURITY.md ├── doc/ │ ├── .vuepress/ │ │ ├── config.js │ │ ├── templates/ │ │ │ └── dev.html │ │ └── theme/ │ │ ├── enhanceApp.js │ │ ├── global-components/ │ │ │ └── Home.vue │ │ ├── index.js │ │ ├── layouts/ │ │ │ └── Layout.vue │ │ └── styles/ │ │ ├── _layout.scss │ │ ├── _reset.scss │ │ ├── main.scss │ │ └── palette.styl │ ├── README.md │ └── zh/ │ └── guide/ │ ├── README.md │ ├── building.md │ ├── componentDev/ │ │ ├── DSLAnalysis.md │ │ ├── componentStructure.md │ │ └── dynamicLoading.md │ ├── deployDev/ │ │ ├── api.md │ │ ├── deploy.md │ │ ├── deploy_v6.md │ │ ├── dir.md │ │ ├── form.md │ │ ├── https.md │ │ ├── log.md │ │ └── oss.md │ ├── directoryStructure.md │ ├── functionRealization/ │ │ ├── download.md │ │ ├── machinePreview.md │ │ ├── pagePreview.md │ │ ├── revocation.md │ │ ├── saveJson.md │ │ ├── screenshot.md │ │ └── templateLibrary.md │ ├── introduced.md │ └── startedQuickly.md ├── package.json ├── readme.md ├── server.js ├── src/ │ ├── app.tsx │ ├── components/ │ │ ├── Calibration/ │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── ErrorBundaries/ │ │ │ └── index.tsx │ │ ├── FormComponents/ │ │ │ ├── CardPicker/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── Color/ │ │ │ │ └── index.tsx │ │ │ ├── DataList/ │ │ │ │ ├── editorModal.tsx │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── FormItems/ │ │ │ │ ├── EditorModal.tsx │ │ │ │ ├── FormItems.tsx │ │ │ │ ├── formItems.less │ │ │ │ └── index.tsx │ │ │ ├── MutiText/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── Pos/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── Table/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── Upload/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── XEditor/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ └── types.ts │ │ ├── LoadingCp/ │ │ │ └── index.tsx │ │ └── Zan/ │ │ ├── index.less │ │ └── index.tsx │ ├── core/ │ │ ├── DynamicEngine.tsx │ │ ├── index.ts │ │ └── renderer/ │ │ ├── FormRender.tsx │ │ ├── ViewRender.tsx │ │ └── viewRender.less │ ├── global.css │ ├── layouts/ │ │ ├── index.less │ │ └── index.tsx │ ├── materials/ │ │ ├── base/ │ │ │ ├── Carousel/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Form/ │ │ │ │ ├── BaseForm.tsx │ │ │ │ ├── BasePopoverForm.tsx │ │ │ │ ├── baseForm.less │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Header/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Icon/ │ │ │ │ ├── icon.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Image/ │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── List/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── LongText/ │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Notice/ │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Qrcode/ │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── RichText/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Tab/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Text/ │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── WhiteTpl/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── common.ts │ │ ├── media/ │ │ │ ├── Audio/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Calendar/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Map/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Video/ │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── schema.ts │ │ ├── shop/ │ │ │ ├── CardLabel/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Coupons/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── List/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── Tab/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── ZhuanLan/ │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ └── template.ts │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ └── visual/ │ │ ├── Area/ │ │ │ ├── index.less │ │ │ ├── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── Chart/ │ │ │ ├── index.less │ │ │ ├── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── Line/ │ │ │ ├── index.less │ │ │ ├── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── Pie/ │ │ │ ├── index.less │ │ │ ├── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── XProgress/ │ │ │ ├── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── schema.ts │ │ └── template.ts │ ├── pages/ │ │ ├── document.ejs │ │ ├── editor/ │ │ │ ├── Container.tsx │ │ │ ├── SourceBox.tsx │ │ │ ├── TargetBox.tsx │ │ │ ├── components/ │ │ │ │ ├── CanvasControl/ │ │ │ │ │ ├── index.less │ │ │ │ │ └── index.tsx │ │ │ │ └── Header/ │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── index.js │ │ │ ├── index.less │ │ │ ├── models/ │ │ │ │ ├── editorModal.js │ │ │ │ └── editorPcModel.ts │ │ │ ├── preview.tsx │ │ │ └── services/ │ │ │ └── editorService.js │ │ ├── help/ │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── home/ │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── ide/ │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── login/ │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── mobileTip.tsx │ │ └── user/ │ │ ├── base.less │ │ ├── index.less │ │ └── index.tsx │ ├── typings.d.ts │ ├── utils/ │ │ ├── req.ts │ │ └── tool.ts │ └── video-react.d.ts ├── tsconfig.json ├── webpack.config.js ├── website/ │ ├── css/ │ │ ├── frameworks.css │ │ └── style.css │ ├── index.html │ └── js/ │ ├── jquery.js │ ├── main.js │ └── plugins.js └── zh.md
SYMBOL INDEX (561 symbols across 81 files)
FILE: src/app.tsx
method onError (line 9) | onError(e: Error) {
FILE: src/components/Calibration/index.tsx
type calibrationTypes (line 5) | interface calibrationTypes {
type CalibrationTypes (line 9) | type CalibrationTypes = {
function Calibration (line 15) | function Calibration(props: CalibrationTypes) {
FILE: src/components/ErrorBundaries/index.tsx
type ErrorBoundaryState (line 3) | interface ErrorBoundaryState {
class ErrorBoundary (line 7) | class ErrorBoundary extends React.Component<
method constructor (line 11) | constructor(props: PropsWithChildren<{}>) {
method componentDidCatch (line 16) | componentDidCatch(_error: Error, _info: ErrorInfo) {
method render (line 23) | render() {
FILE: src/components/FormComponents/CardPicker/index.tsx
type CardPickerType (line 9) | interface CardPickerType
FILE: src/components/FormComponents/Color/index.tsx
type ColorConfigType (line 5) | type ColorConfigType = string;
type ColorProps (line 8) | interface ColorProps {
class colorPicker (line 13) | class colorPicker extends React.Component<ColorProps> {
method render (line 35) | render() {
FILE: src/components/FormComponents/DataList/editorModal.tsx
type EditorModalProps (line 21) | type EditorModalProps = {
FILE: src/components/FormComponents/DataList/index.tsx
type ListItemProps (line 27) | type ListItemProps = DndItemProps & {
function ListItem (line 34) | function ListItem(props: ListItemProps) {
type DndItemProps (line 73) | type DndItemProps = TDataListDefaultTypeItem & {
type DragObject (line 83) | type DragObject = {
method endDrag (line 94) | endDrag(props, monitor) {
method hover (line 115) | hover(props, monitor) {
type DataListMemo (line 136) | type DataListMemo = {
type DataListType (line 142) | type DataListType = DataListMemo & {
FILE: src/components/FormComponents/FormItems/EditorModal.tsx
type EditorModalProps (line 13) | interface EditorModalProps {
FILE: src/components/FormComponents/FormItems/FormItems.tsx
type FormItemsProps (line 72) | interface FormItemsProps {
FILE: src/components/FormComponents/MutiText/index.tsx
type MultiTextProps (line 7) | type MultiTextProps = {
FILE: src/components/FormComponents/Pos/index.tsx
type PosProps (line 6) | type PosProps = {
FILE: src/components/FormComponents/Table/index.tsx
type Item (line 18) | interface Item {
type EditableRowProps (line 25) | interface EditableRowProps {
type EditableCellProps (line 40) | interface EditableCellProps {
class EditableTable (line 117) | class EditableTable extends React.Component<any, any> {
method constructor (line 140) | constructor(props: any) {
method render (line 293) | render() {
FILE: src/components/FormComponents/Upload/index.tsx
function getBase64 (line 24) | function getBase64(file: File | Blob) {
type PicturesWallType (line 33) | interface PicturesWallType {
class PicturesWall (line 44) | class PicturesWall extends React.Component<PicturesWallType> {
method componentDidMount (line 135) | componentDidMount() {
method render (line 144) | render() {
FILE: src/components/FormComponents/types.ts
type IUploadConfigType (line 2) | interface IUploadConfigType {
type TUploadDefaultType (line 10) | type TUploadDefaultType = Array<{
type ITextConfigType (line 17) | interface ITextConfigType {
type TTextDefaultType (line 22) | type TTextDefaultType = string;
type ITextAreaConfigType (line 24) | interface ITextAreaConfigType {
type TTextAreaDefaultType (line 29) | type TTextAreaDefaultType = string;
type INumberConfigType (line 31) | interface INumberConfigType {
type TNumberDefaultType (line 39) | type TNumberDefaultType = number;
type IDataListConfigType (line 42) | interface IDataListConfigType {
type TDataListDefaultTypeItem (line 49) | type TDataListDefaultTypeItem = {
type TDataListDefaultType (line 58) | type TDataListDefaultType = Array<TDataListDefaultTypeItem>;
type IColorConfigType (line 61) | interface IColorConfigType {
type TColorDefaultType (line 67) | type TColorDefaultType = string;
type IRichTextConfigType (line 70) | interface IRichTextConfigType {
type TRichTextDefaultType (line 75) | type TRichTextDefaultType = string;
type IMutiTextConfigType (line 77) | interface IMutiTextConfigType {
type TMutiTextDefaultType (line 83) | type TMutiTextDefaultType = Array<string>;
type ISelectConfigType (line 86) | interface ISelectConfigType<KeyType> {
type TSelectDefaultType (line 95) | type TSelectDefaultType<KeyType> = KeyType;
type IRadioConfigType (line 98) | interface IRadioConfigType<KeyType> {
type TRadioDefaultType (line 107) | type TRadioDefaultType<KeyType> = KeyType;
type ISwitchConfigType (line 111) | interface ISwitchConfigType {
type TSwitchDefaultType (line 116) | type TSwitchDefaultType = boolean;
type ICardPickerConfigType (line 119) | interface ICardPickerConfigType<T> {
type TCardPickerDefaultType (line 125) | type TCardPickerDefaultType<T> = T;
type ITableConfigType (line 129) | interface ITableConfigType {
type TTableDefaultType (line 134) | type TTableDefaultType = Array<{
type IPosConfigType (line 140) | interface IPosConfigType {
type TPosItem (line 150) | type TPosItem = number | undefined;
type TPosDefaultType (line 152) | type TPosDefaultType = [TPosItem, TPosItem];
type IFormItemsConfigType (line 155) | interface IFormItemsConfigType {
type baseFormOptionsType (line 162) | type baseFormOptionsType = {
type baseFormTextTpl (line 167) | type baseFormTextTpl = {
type baseFormTextTipTpl (line 174) | type baseFormTextTipTpl = {
type baseFormNumberTpl (line 182) | type baseFormNumberTpl = {
type baseFormTextAreaTpl (line 189) | type baseFormTextAreaTpl = {
type baseFormMyRadioTpl (line 196) | type baseFormMyRadioTpl = {
type baseFormMyCheckboxTpl (line 203) | type baseFormMyCheckboxTpl = {
type baseFormMySelectTpl (line 210) | type baseFormMySelectTpl = {
type baseFormDateTpl (line 217) | type baseFormDateTpl = {
type baseFormUnion (line 224) | type baseFormUnion =
type baseFormUnionType (line 233) | type baseFormUnionType =
type TFormItemsDefaultType (line 243) | type TFormItemsDefaultType = Array<baseFormUnion>;
FILE: src/components/Zan/index.tsx
type IProps (line 5) | interface IProps {
FILE: src/core/DynamicEngine.tsx
type componentsType (line 6) | type componentsType = "media" | "base" | "visible" | "shop";
type DynamicType (line 28) | type DynamicType = {
FILE: src/core/renderer/FormRender.tsx
type FormEditorProps (line 30) | interface FormEditorProps {
FILE: src/core/renderer/ViewRender.tsx
type PointDataItem (line 6) | interface PointDataItem {
type ViewProps (line 12) | interface ViewProps {
FILE: src/layouts/index.tsx
function Layout (line 32) | function Layout({ children }: IRouteComponentProps) {
FILE: src/materials/base/Carousel/index.tsx
type CarouselTypes (line 7) | interface CarouselTypes extends ICarouselConfig {
FILE: src/materials/base/Carousel/schema.ts
type CarouselDirectionKeyType (line 14) | type CarouselDirectionKeyType = "down" | "left";
type TCarouselEditData (line 16) | type TCarouselEditData = Array<
type ICarouselConfig (line 22) | interface ICarouselConfig {
type ICarouselSchema (line 31) | interface ICarouselSchema {
FILE: src/materials/base/Form/BaseForm.tsx
type TBaseForm (line 18) | type TBaseForm = {
FILE: src/materials/base/Form/BasePopoverForm.tsx
type TBaseForm (line 17) | type TBaseForm = {
FILE: src/materials/base/Form/schema.ts
type TTextWeightSelectKeyType (line 14) | type TTextWeightSelectKeyType = "300" | "400" | "500" | "600";
type TFormEditData (line 16) | type TFormEditData = Array<
type IFormConfig (line 24) | interface IFormConfig {
type IFormSchema (line 36) | interface IFormSchema {
FILE: src/materials/base/Header/schema.ts
type THeaderEditData (line 13) | type THeaderEditData = Array<
type IHeaderConfig (line 16) | interface IHeaderConfig extends ICommonBaseType {
type IHeaderSchema (line 25) | interface IHeaderSchema {
FILE: src/materials/base/Icon/icon.ts
type AntdIconType (line 1) | type AntdIconType =
FILE: src/materials/base/Icon/index.tsx
type IconType (line 8) | interface IconType extends IIconConfig {
FILE: src/materials/base/Icon/schema.ts
type TIconEditData (line 14) | type TIconEditData = Array<
type IIconConfig (line 21) | interface IIconConfig {
type IIconSchema (line 32) | interface IIconSchema {
type IconTypes (line 37) | type IconTypes =
FILE: src/materials/base/Image/schema.ts
type TTextSelectKeyType (line 17) | type TTextSelectKeyType = "left" | "right" | "center";
type TTextWeightSelectKeyType (line 18) | type TTextWeightSelectKeyType = "300" | "400" | "500" | "600";
type TImageEditData (line 20) | type TImageEditData = Array<
type IImageConfig (line 29) | interface IImageConfig extends ICommonBaseType {
type IImageSchema (line 44) | interface IImageSchema {
FILE: src/materials/base/List/schema.ts
type TListSelectKeyType (line 12) | type TListSelectKeyType = "60" | "80" | "100" | "120" | "150";
type TListEditData (line 13) | type TListEditData = Array<
type IListConfig (line 19) | interface IListConfig extends ICommonBaseType {
type IListSchema (line 27) | interface IListSchema {
FILE: src/materials/base/LongText/schema.ts
type TLongTextSelectKeyType (line 11) | type TLongTextSelectKeyType = "left" | "center" | "right";
type TLongTextEditData (line 13) | type TLongTextEditData = Array<
type ILongTextConfig (line 19) | interface ILongTextConfig {
type ILongTextSchema (line 31) | interface ILongTextSchema {
FILE: src/materials/base/Notice/schema.ts
type TNoticeSelectKeyType (line 12) | type TNoticeSelectKeyType =
type TNoticeEditData (line 18) | type TNoticeEditData = Array<
type INoticeConfig (line 24) | interface INoticeConfig {
type INoticeSchema (line 31) | interface INoticeSchema {
FILE: src/materials/base/Qrcode/schema.ts
type TQrcodeEditData (line 12) | type TQrcodeEditData = Array<
type IQrcodeConfig (line 15) | interface IQrcodeConfig {
type IQrcodeSchema (line 22) | interface IQrcodeSchema {
FILE: src/materials/base/RichText/index.tsx
type IProps (line 6) | interface IProps extends IButtonConfig {
FILE: src/materials/base/RichText/schema.ts
type TButtonEditData (line 12) | type TButtonEditData = Array<
type IButtonConfig (line 16) | interface IButtonConfig {
type IButtonSchema (line 24) | interface IButtonSchema {
FILE: src/materials/base/Tab/schema.ts
type TTabEditData (line 12) | type TTabEditData = Array<
type ITabConfig (line 18) | interface ITabConfig {
type ITabSchema (line 27) | interface ITabSchema {
FILE: src/materials/base/Text/schema.ts
type TTextSelectKeyType (line 12) | type TTextSelectKeyType = "left" | "right" | "center";
type TTextEditData (line 13) | type TTextEditData = Array<
type ITextConfig (line 19) | interface ITextConfig {
type ITextSchema (line 27) | interface ITextSchema {
FILE: src/materials/base/WhiteTpl/index.tsx
type IProps (line 6) | interface IProps extends IWhiteTplConfig {
FILE: src/materials/base/WhiteTpl/schema.ts
type TWhiteTplEditData (line 10) | type TWhiteTplEditData = Array<
type IWhiteTplConfig (line 13) | interface IWhiteTplConfig {
type IWhiteTplSchema (line 21) | interface IWhiteTplSchema {
FILE: src/materials/common.ts
type ICommonBaseType (line 9) | interface ICommonBaseType {
FILE: src/materials/media/Audio/schema.ts
type TAudioEditData (line 8) | type TAudioEditData = Array<INumberConfigType | ITextConfigType>;
type IAudioConfig (line 9) | interface IAudioConfig {
type IAudioSchema (line 14) | interface IAudioSchema {
FILE: src/materials/media/Calendar/schema.ts
type TCalendarEditData (line 9) | type TCalendarEditData = Array<
type ICalendarConfig (line 12) | interface ICalendarConfig {
type ICalendarSchema (line 20) | interface ICalendarSchema {
FILE: src/materials/media/Map/schema.ts
type TMapEditData (line 10) | type TMapEditData = Array<
type IMapConfig (line 13) | interface IMapConfig {
type IMapSchema (line 19) | interface IMapSchema {
FILE: src/materials/media/Video/schema.ts
type TVideoEditData (line 8) | type TVideoEditData = Array<IUploadConfigType | ITextConfigType>;
type IVideoConfig (line 9) | interface IVideoConfig {
type IVideoSchema (line 14) | interface IVideoSchema {
FILE: src/materials/shop/CardLabel/index.tsx
type IProps (line 6) | interface IProps extends IZLConfig {
FILE: src/materials/shop/CardLabel/schema.ts
type TZLEditData (line 16) | type TZLEditData = Array<
type IZLConfig (line 24) | interface IZLConfig {
type ICardSchema (line 35) | interface ICardSchema {
FILE: src/materials/shop/Coupons/index.tsx
type IProps (line 6) | interface IProps extends IZLConfig {
FILE: src/materials/shop/Coupons/schema.ts
type TZLEditData (line 18) | type TZLEditData = Array<
type IZLConfig (line 26) | interface IZLConfig {
type ICardSchema (line 35) | interface ICardSchema {
FILE: src/materials/shop/List/index.tsx
type ListType (line 7) | interface ListType extends IListConfig {
FILE: src/materials/shop/List/schema.ts
type TListSelectKeyType (line 16) | type TListSelectKeyType = "60" | "80" | "100" | "120" | "150";
type TListEditData (line 17) | type TListEditData = Array<
type IListConfig (line 24) | interface IListConfig {
type IListSchema (line 34) | interface IListSchema {
FILE: src/materials/shop/Tab/index.tsx
type TabType (line 7) | interface TabType extends ITabConfig {
FILE: src/materials/shop/Tab/schema.ts
type TTabEditData (line 14) | type TTabEditData = Array<
type ITabConfig (line 20) | interface ITabConfig {
type ITabSchema (line 29) | interface ITabSchema {
FILE: src/materials/shop/ZhuanLan/index.tsx
type IProps (line 6) | interface IProps extends IZLConfig {
FILE: src/materials/shop/ZhuanLan/schema.ts
type TZLEditData (line 18) | type TZLEditData = Array<
type IZLConfig (line 26) | interface IZLConfig {
type ICardSchema (line 41) | interface ICardSchema {
FILE: src/materials/visual/Area/index.tsx
type XChartProps (line 9) | interface XChartProps extends IChartConfig {
FILE: src/materials/visual/Area/schema.ts
type TChartEditData (line 12) | type TChartEditData = Array<
type IChartConfig (line 15) | interface IChartConfig {
type IChartSchema (line 23) | interface IChartSchema {
FILE: src/materials/visual/Chart/index.tsx
type XChartProps (line 9) | interface XChartProps extends IChartConfig {
FILE: src/materials/visual/Chart/schema.ts
type TChartEditData (line 12) | type TChartEditData = Array<
type IChartConfig (line 15) | interface IChartConfig {
type IChartSchema (line 23) | interface IChartSchema {
FILE: src/materials/visual/Line/index.tsx
type XChartProps (line 9) | interface XChartProps extends IChartConfig {
FILE: src/materials/visual/Line/schema.ts
type TChartEditData (line 12) | type TChartEditData = Array<
type IChartConfig (line 15) | interface IChartConfig {
type IChartSchema (line 23) | interface IChartSchema {
FILE: src/materials/visual/Pie/index.tsx
type XChartProps (line 9) | interface XChartProps extends IChartConfig {
type DataMap (line 13) | interface DataMap {
FILE: src/materials/visual/Pie/schema.ts
type TChartEditData (line 12) | type TChartEditData = Array<
type IChartConfig (line 15) | interface IChartConfig {
type IChartSchema (line 23) | interface IChartSchema {
FILE: src/materials/visual/XProgress/schema.ts
type TXProgressSelectKeyType (line 9) | type TXProgressSelectKeyType = "success" | "warning" | "danger";
type TXProgressRadiotKeyType (line 10) | type TXProgressRadiotKeyType = "circle" | "line" | "semi-circle";
type TXProgressEditData (line 11) | type TXProgressEditData = Array<
type IXProgressConfig (line 16) | interface IXProgressConfig {
type IXProgressSchema (line 24) | interface IXProgressSchema {
FILE: src/pages/editor/SourceBox.tsx
type TargetBoxProps (line 12) | interface TargetBoxProps {
FILE: src/pages/editor/TargetBox.tsx
type SourceBoxProps (line 13) | interface SourceBoxProps {
FILE: src/pages/editor/components/CanvasControl/index.tsx
type CanvasControlProps (line 7) | interface CanvasControlProps {
FILE: src/pages/editor/components/Header/index.tsx
type HeaderComponentProps (line 29) | interface HeaderComponentProps {
method onOk (line 108) | onOk() {
method onCancel (line 114) | onCancel() {
method onOk (line 148) | onOk() {
method onOk (line 184) | onOk() {
method beforeUpload (line 202) | beforeUpload(file, fileList) {
FILE: src/pages/editor/index.js
function BasicLayout (line 10) | function BasicLayout(props) {
FILE: src/pages/editor/models/editorModal.js
function overSave (line 10) | function overSave(name, data) {
method addPointData (line 21) | addPointData(state, { payload }) {
method modPointData (line 30) | modPointData(state, { payload }) {
method importTplData (line 45) | importTplData(state, { payload }) {
method copyPointData (line 53) | copyPointData(state, { payload }) {
method delPointData (line 69) | delPointData(state, { payload }) {
method keyboardCopyPointData (line 79) | keyboardCopyPointData(state) {
method keyboardDelPointData (line 98) | keyboardDelPointData(state) {
method clearAll (line 111) | clearAll(state) {
method setup (line 122) | setup({ dispatch, history }) {
method keyEvent (line 125) | keyEvent({ dispatch, state }) {
FILE: src/pages/editor/models/editorPcModel.ts
function overSave (line 3) | function overSave(name: string, data: any) {
method addPointData (line 14) | addPointData(state: any, { payload }: any) {
method modPointData (line 23) | modPointData(state: any, { payload }: any) {
method delPointData (line 38) | delPointData(state: any, { payload }: any) {
method clearAll (line 48) | clearAll(state: any) {
FILE: src/pages/editor/preview.tsx
type PreviewPageProps (line 17) | interface PreviewPageProps {
type PointDataItem (line 20) | interface PointDataItem {
FILE: src/pages/editor/services/editorService.js
function getTemplate (line 3) | function getTemplate(data) {
FILE: src/pages/login/index.tsx
type FormValues (line 8) | interface FormValues {
FILE: src/pages/mobileTip.tsx
function MobileTip (line 4) | function MobileTip() {
FILE: src/pages/user/index.tsx
function CodeEditor (line 72) | function CodeEditor() {
type Editor (line 311) | interface Editor {
type EditorChange (line 314) | interface EditorChange {
type Position (line 321) | interface Position {
FILE: src/typings.d.ts
type Window (line 14) | interface Window {
FILE: src/utils/tool.ts
function uuid (line 5) | function uuid(len: number, radix: number) {
function rgba2Obj (line 32) | function rgba2Obj(rgba = "") {
function useGetRect (line 47) | function useGetRect() {
function useGetScrollBarWidth (line 58) | function useGetScrollBarWidth(ref: RefObject<HTMLElement>) {
function useAnimation (line 69) | function useAnimation(state: boolean, delay: number) {
function unParams (line 87) | function unParams(params = "?a=1&b=2&c=3") {
function throttle (line 97) | function throttle(fn: Function, delay: number) {
function formatTime (line 110) | function formatTime(fmt: string, dateObj: any) {
function detectMobileBrowser (line 139) | function detectMobileBrowser(browserNavigatorMetaInfo: string): boolean {
function getBrowserNavigatorMetaInfo (line 151) | function getBrowserNavigatorMetaInfo(): string {
FILE: src/video-react.d.ts
type PreloadType (line 2) | type PreloadType = "auto" | "metadata" | "none";
type PlayerPropsType (line 4) | interface PlayerPropsType {
class Player (line 49) | class Player extends React.Component<PlayerPropsType> {
type VideoPropsType (line 120) | interface VideoPropsType {
class Video (line 160) | class Video extends React.Component<VideoPropsType> {
type BigPlayButtonPropsType (line 220) | interface BigPlayButtonPropsType {
class BigPlayButton (line 227) | class BigPlayButton extends React.Component<BigPlayButtonPropsType> {}
type LoadingSpinnerPropsType (line 229) | interface LoadingSpinnerPropsType {
class LoadingSpinner (line 233) | class LoadingSpinner extends React.Component<LoadingSpinnerPropsType> {}
type PosterImagePropsType (line 235) | interface PosterImagePropsType {
class PosterImage (line 241) | class PosterImage extends React.Component<PosterImagePropsType> {}
type BezelPropsType (line 243) | interface BezelPropsType {
class Bezel (line 247) | class Bezel extends React.Component<BezelPropsType> {}
type ShortcutPropsType (line 249) | interface ShortcutPropsType {
class Shortcut (line 257) | class Shortcut extends React.Component<ShortcutPropsType> {}
type ControlBarPropsType (line 259) | interface ControlBarPropsType {
class ControlBar (line 267) | class ControlBar extends React.Component<ControlBarPropsType> {}
type PlayTogglePropsType (line 269) | interface PlayTogglePropsType {
class PlayToggle (line 274) | class PlayToggle extends React.Component<PlayTogglePropsType> {}
type ForwardSecondsType (line 276) | type ForwardSecondsType = 5 | 10 | 30;
type ForwardControlPropsType (line 277) | interface ForwardControlPropsType {
class ForwardControl (line 282) | class ForwardControl extends React.Component<ForwardControlPropsType> {}
type ReplayControlPropsType (line 284) | interface ReplayControlPropsType {
class ReplayControl (line 289) | class ReplayControl extends React.Component<ReplayControlPropsType> {}
type FullscreenTogglePropsType (line 291) | interface FullscreenTogglePropsType {
class FullscreenToggle (line 296) | class FullscreenToggle extends React.Component<FullscreenTogglePropsType...
type ProgressControlPropsType (line 298) | interface ProgressControlPropsType {
class ProgressControl (line 302) | class ProgressControl extends React.Component<ProgressControlPropsType> {}
type SeekBarPropsType (line 304) | interface SeekBarPropsType {
class SeekBar (line 310) | class SeekBar extends React.Component<SeekBarPropsType> {
type SliderPropsType (line 320) | interface SliderPropsType {
class Slider (line 339) | class Slider extends React.Component<SliderPropsType> {}
type PlayProgressBarPropsType (line 341) | interface PlayProgressBarPropsType {
class PlayProgressBar (line 347) | class PlayProgressBar extends React.Component<PlayProgressBarPropsType> {}
type LoadProgressBarPropsType (line 349) | interface LoadProgressBarPropsType {
type MouseTimeDisplayPropsType (line 356) | interface MouseTimeDisplayPropsType {
type RemainingTimeDisplayPropsType (line 367) | interface RemainingTimeDisplayPropsType {
type CurrentTimeDisplayPropsType (line 376) | interface CurrentTimeDisplayPropsType {
type DurationDisplayPropsType (line 385) | interface DurationDisplayPropsType {
type TimeDividerPropsType (line 393) | interface TimeDividerPropsType {
type VolumeMenuButtonPropsType (line 399) | interface VolumeMenuButtonPropsType {
class VolumeMenuButton (line 409) | class VolumeMenuButton extends React.Component<VolumeMenuButtonPropsType> {
type PlaybackRateMenuButtonPropsType (line 413) | interface PlaybackRateMenuButtonPropsType {
class PlaybackRateMenuButton (line 419) | class PlaybackRateMenuButton extends React.Component<
type ClosedCaptionButtonPropsType (line 423) | interface ClosedCaptionButtonPropsType {
class ClosedCaptionButton (line 431) | class ClosedCaptionButton extends React.Component<
class PlaybackRate (line 435) | class PlaybackRate extends React.Component {}
type MenuButtonPropsType (line 437) | interface MenuButtonPropsType {
class MenuButton (line 445) | class MenuButton extends React.Component<MenuButtonPropsType> {}
type OPERATE (line 448) | type OPERATE = "video-react/OPERATE";
type FULLSCREEN_CHANGE (line 449) | type FULLSCREEN_CHANGE = "video-react/FULLSCREEN_CHANGE";
type PLAYER_ACTIVATE (line 450) | type PLAYER_ACTIVATE = "video-react/PLAYER_ACTIVATE";
type USER_ACTIVATE (line 451) | type USER_ACTIVATE = "video-react/USER_ACTIVATE";
type LOAD_START (line 571) | type LOAD_START = "video-react/LOAD_START";
type CAN_PLAY (line 572) | type CAN_PLAY = "video-react/CAN_PLAY";
type WAITING (line 573) | type WAITING = "video-react/WAITING";
type CAN_PLAY_THROUGH (line 574) | type CAN_PLAY_THROUGH = "video-react/CAN_PLAY_THROUGH";
type PLAYING (line 575) | type PLAYING = "video-react/PLAYING";
type PLAY (line 576) | type PLAY = "video-react/PLAY";
type PAUSE (line 577) | type PAUSE = "video-react/PAUSE";
type END (line 578) | type END = "video-react/END";
type SEEKING (line 579) | type SEEKING = "video-react/SEEKING";
type SEEKED (line 580) | type SEEKED = "video-react/SEEKED";
type SEEKING_TIME (line 581) | type SEEKING_TIME = "video-react/SEEKING_TIME";
type END_SEEKING (line 582) | type END_SEEKING = "video-react/END_SEEKING";
type DURATION_CHANGE (line 583) | type DURATION_CHANGE = "video-react/DURATION_CHANGE";
type TIME_UPDATE (line 584) | type TIME_UPDATE = "video-react/TIME_UPDATE";
type VOLUME_CHANGE (line 585) | type VOLUME_CHANGE = "video-react/VOLUME_CHANGE";
type PROGRESS_CHANGE (line 586) | type PROGRESS_CHANGE = "video-react/PROGRESS_CHANGE";
type RATE_CHANGE (line 587) | type RATE_CHANGE = "video-react/RATE_CHANGE";
type SUSPEND (line 588) | type SUSPEND = "video-react/SUSPEND";
type ABORT (line 589) | type ABORT = "video-react/ABORT";
type EMPTIED (line 590) | type EMPTIED = "video-react/EMPTIED";
type STALLED (line 591) | type STALLED = "video-react/STALLED";
type LOADED_META_DATA (line 592) | type LOADED_META_DATA = "video-react/LOADED_META_DATA";
type LOADED_DATA (line 593) | type LOADED_DATA = "video-react/LOADED_DATA";
type RESIZE (line 594) | type RESIZE = "video-react/RESIZE";
type ERROR (line 595) | type ERROR = "video-react/ERROR";
type ACTIVATE_TEXT_TRACK (line 596) | type ACTIVATE_TEXT_TRACK = "video-react/ACTIVATE_TEXT_TRACK";
FILE: website/js/jquery.js
function b (line 34) | function b(e, t, n) {
function w (line 42) | function w(e) {
function d (line 54) | function d(e) {
function se (line 358) | function se(t, e, n, r) {
function ue (line 404) | function ue() {
function le (line 410) | function le(e) {
function ce (line 413) | function ce(e) {
function fe (line 423) | function fe(e, t) {
function pe (line 428) | function pe(e, t) {
function de (line 435) | function de(t) {
function he (line 440) | function he(n) {
function ge (line 446) | function ge(t) {
function ve (line 459) | function ve(a) {
function ye (line 472) | function ye(e) {
function me (line 1043) | function me() {}
function xe (line 1044) | function xe(e) {
function be (line 1048) | function be(s, e, t) {
function we (line 1081) | function we(i) {
function Te (line 1090) | function Te(e, t, n, r, i) {
function Ce (line 1095) | function Ce(d, h, g, v, y, e) {
function Ee (line 1132) | function Ee(e) {
function A (line 1371) | function A(e, t) {
function j (line 1375) | function j(e, n, r) {
function P (line 1465) | function P(e, t) {
function M (line 1566) | function M(e) {
function I (line 1569) | function I(e) {
function W (line 1572) | function W(e, t, n, r) {
function l (line 1723) | function l(i, o, a, s) {
function B (line 1834) | function B() {
function X (line 1881) | function X(e, t) {
function V (line 1884) | function V(e) {
function Y (line 1890) | function Y() {
function ee (line 1943) | function ee(e, t, n) {
function le (line 2127) | function le(e, t, n, r) {
function fe (line 2156) | function fe(e, t) {
function ve (line 2206) | function ve(e, t) {
function ye (line 2218) | function ye(e, t) {
function we (line 2228) | function we(e, t, n, r, i) {
function ke (line 2265) | function ke() {
function Se (line 2268) | function Se() {
function Ne (line 2271) | function Ne(e, t) {
function Ae (line 2282) | function Ae(e, t, n, r, i, o) {
function De (line 2308) | function De(e, i, o) {
function Oe (line 2693) | function Oe(e, t) {
function Pe (line 2701) | function Pe(e) {
function Re (line 2704) | function Re(e) {
function Me (line 2712) | function Me(e, t) {
function Ie (line 2721) | function Ie(n, r, i, o) {
function We (line 2763) | function We(e, t, n) {
function _e (line 2931) | function _e(e, t, n) {
function ze (line 2954) | function ze(e, t) {
function e (line 2963) | function e() {
function t (line 2982) | function t(e) {
function Ge (line 3017) | function Ge(e) {
function Ze (line 3035) | function Ze(e, t, n) {
function et (line 3039) | function et(e, t, n, r, i, o) {
function tt (line 3062) | function tt(e, t, n) {
function nt (line 3081) | function nt(e, t, n, r, i) {
function lt (line 3305) | function lt() {
function ct (line 3312) | function ct() {
function ft (line 3320) | function ft(e, t) {
function pt (line 3327) | function pt(e, t, n) {
function dt (line 3335) | function dt(o, e, t) {
function mt (line 3726) | function mt(e) {
function xt (line 3729) | function xt(e) {
function bt (line 3732) | function bt(e) {
function qt (line 4094) | function qt(n, e, r, i) {
function Bt (line 4162) | function Bt(o) {
function _t (line 4175) | function _t(t, i, o, a) {
function zt (line 4195) | function zt(e, t) {
function l (line 4370) | function l(e, t, n, r) {
FILE: website/js/main.js
function appinocks_navbarFixed (line 20) | function appinocks_navbarFixed() {
function appinocks_navScrollSpy (line 61) | function appinocks_navScrollSpy() {
function appinocks_counterUp (line 74) | function appinocks_counterUp() {
function appinocks_owl_carousel (line 87) | function appinocks_owl_carousel() {
function appinocks_magnificPopupVideo (line 129) | function appinocks_magnificPopupVideo() {
function appinocks_pricingTab (line 149) | function appinocks_pricingTab() {
function appinocks_copyrightDynamicYear (line 168) | function appinocks_copyrightDynamicYear() {
function appinocks_preloader (line 182) | function appinocks_preloader() {
function appinocks_scrollIt (line 199) | function appinocks_scrollIt() {
function appinocks_scrollUp (line 217) | function appinocks_scrollUp() {
FILE: website/js/plugins.js
function e (line 1001) | function e(e) {
function t (line 1004) | function t(e, t) {
function o (line 1010) | function o(e) {
function n (line 1013) | function n(e) {
function r (line 1028) | function r(e) {
function p (line 1031) | function p(e) {
function s (line 1048) | function s(e) {
function d (line 1052) | function d(e) {
function a (line 1055) | function a(e, t) {
function l (line 1067) | function l(e) {
function f (line 1078) | function f(e, t) {
function m (line 1085) | function m(e, t) {
function h (line 1090) | function h(e, t, o, n) {
function c (line 1104) | function c(e) {
function g (line 1110) | function g(e) {
function u (line 1113) | function u(e) {
function b (line 1135) | function b(e, o) {
function w (line 1164) | function w(e) {
function y (line 1175) | function y(e) {
function E (line 1182) | function E(e) {
function v (line 1187) | function v(e, t, i, r) {
function x (line 1220) | function x(e) {
function O (line 1225) | function O(e, t, o, n, i) {
function L (line 1251) | function L(e, t, o) {
function S (line 1256) | function S(e) {
function T (line 1264) | function T(e) {
function D (line 1270) | function D(e, t, o) {
function C (line 1281) | function C(e, t) {
function N (line 1284) | function N(e, t, o) {
function P (line 1294) | function P(t, o, n) {
function k (line 1309) | function k() {
function W (line 1343) | function W(e, t) {
function H (line 1350) | function H(e) {
function B (line 1362) | function B() {
function A (line 1379) | function A(e) {
function M (line 1383) | function M(e, t, o, i) {
function F (line 1388) | function F(e, t, o, i) {
function I (line 1398) | function I() {
function R (line 1402) | function R(e, t) {
function U (line 1415) | function U() {
function Y (line 1419) | function Y(e) {
function j (line 1422) | function j(e, t) {
function V (line 1431) | function V(e, t) {
function q (line 1437) | function q(e, t) {
function K (line 1458) | function K(e, t, o) {
function z (line 1483) | function z(e) {
function G (line 1486) | function G(e) {
function _ (line 1492) | function _(e, t, o, n) {
function X (line 1523) | function X(e, t, o, n) {
function J (line 1566) | function J(e, t) {
function e (line 1631) | function e(e, t) {
function t (line 1685) | function t(o, n) {
function i (line 2119) | function i(t, e) {
function s (line 2128) | function s(t, e, n) {
function l (line 2131) | function l(o) {
function n (line 2160) | function n(t) {
function i (line 2265) | function i(t) {
function n (line 2348) | function n(t) {
function r (line 2468) | function r(t, e) {
function a (line 2781) | function a(e, t) {
function c (line 3043) | function c(t, e) {
function o (line 3350) | function o(t, e) {
function Se (line 3700) | function Se(t, s, e) {
function i (line 3809) | function i(t, e) {
function i (line 4238) | function i() {
function n (line 4360) | function n(t, e) {
function i (line 4568) | function i(t) {
function i (line 4699) | function i(t, e) {
function r (line 4851) | function r(t) {
function r (line 4909) | function r(t) {
function o (line 5065) | function o(e) {
function l (line 5199) | function l(e) {
function i (line 5261) | function i(e, t) {
function r (line 5286) | function r(e) {
function y (line 5313) | function y(e) {
function w (line 5338) | function w() {
function j (line 5703) | function j(e, t) {
function E (line 5793) | function E(e, t) {
function a (line 5880) | function a() {}
function a (line 5937) | function a() {
function a (line 5960) | function a() {
function e (line 5992) | function e(a) {
function t (line 6269) | function t(o) {
function t (line 6357) | function t(t) {
function e (line 6360) | function e(t) {
function t (line 6389) | function t() {
function t (line 6398) | function t() {
function t (line 6549) | function t(t, e) {
function e (line 6552) | function e(t, e) {
function i (line 6555) | function i(t) {
function t (line 6614) | function t(t) {
function t (line 6646) | function t(t) {
function onYouTubeIframeAPIReady (line 7046) | function onYouTubeIframeAPIReady() {
function iOSversion (line 7069) | function iOSversion() {
function isIframe (line 7152) | function isIframe() {
function hideMouse (line 7765) | function hideMouse() {
function RunPrefixMethod (line 7792) | function RunPrefixMethod(e, r) {
function launchFullscreen (line 7803) | function launchFullscreen(e) {
function cancelFullscreen (line 7806) | function cancelFullscreen() {
function isTouchSupported (line 8630) | function isTouchSupported() {
function uncamel (line 8744) | function uncamel(e) {
function setUnit (line 8749) | function setUnit(e, r) {
function setFilter (line 8752) | function setFilter(e, r, t) {
function e (line 8923) | function e() {
function isTouchSupported (line 9042) | function isTouchSupported() {
function e (line 9348) | function e(b, c) {
function e (line 11156) | function e(b, c) {
function f (line 11166) | function f(a) {
function $ (line 11226) | function $(t) {
function F (line 11229) | function F(t) {
function k (line 11232) | function k(t) {
function M (line 11235) | function M(t) {
function R (line 11238) | function R(t) {
function Z (line 11241) | function Z(t) {
function z (line 11244) | function z(t) {
function q (line 11253) | function q(t) {
function H (line 11258) | function H(t) {
function I (line 11261) | function I(t) {
function V (line 11269) | function V(t) {
function _ (line 11272) | function _(t, e) {
function B (line 11275) | function B(t) {
function U (line 11288) | function U(t) {
function X (line 11295) | function X(t, e) {
function J (line 11301) | function J(t, r, i) {
function W (line 11309) | function W(t, e) {
function Y (line 11312) | function Y(t, e, n, r) {
function G (line 11315) | function G(t, e, n) {
function K (line 11318) | function K(t, n) {
function Q (line 11323) | function Q(t) {
function tt (line 11341) | function tt(t, e) {
function h (line 12179) | function h(t) {
function p (line 12182) | function p(t, e, n, r) {
function d (line 12194) | function d(t) {
function m (line 12204) | function m(t) {
function g (line 12207) | function g(t, e) {
function v (line 12210) | function v(t) {
function y (line 12213) | function y(t, n, i, o, s, u, f) {
function x (line 12242) | function x(t, e, n, r, i) {
function T (line 12251) | function T(t, n) {
function S (line 12271) | function S(t) {
function p (line 12422) | function p(t, n, r) {
function d (line 12426) | function d(t, e, n, i) {
function m (line 12429) | function m(t) {
function g (line 12432) | function g(t) {
function v (line 12435) | function v(t, e) {
function y (line 12441) | function y(t, e, n, r) {
function x (line 12449) | function x(t, e, n, r, i) {
function b (line 12456) | function b(t, e, n) {
function E (line 12460) | function E(t, e, n) {
function j (line 12465) | function j() {}
function w (line 12466) | function w(t) {
function T (line 12473) | function T(t, e) {
function S (line 12476) | function S(t) {
function C (line 12485) | function C(t, n, r, i) {
function O (line 12492) | function O(t, n, r, i) {
function w (line 13050) | function w() {
Condensed preview — 236 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,721K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 130,
"preview": "# These are supported funding model platforms\n# support simple dooring !\n# weclome to try this repo.\n\nopen_collective: h"
},
{
"path": ".github/ISSUE_TEMPLATE/custom.md",
"chars": 126,
"preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n"
},
{
"path": ".gitignore",
"chars": 1659,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
},
{
"path": ".umirc.ts",
"chars": 1420,
"preview": "import path from \"path\";\nimport { defineConfig } from \"umi\";\n\nexport default defineConfig({\n dynamicImport: {\n loadi"
},
{
"path": ".vscode/settings.json",
"chars": 272,
"preview": "{\n \"workbench.colorCustomizations\": {\n \"activityBar.background\": \"#152B60\",\n \"titleBar.activeBackground\": \"#1D3C8"
},
{
"path": "CHANGELOG.md",
"chars": 1951,
"preview": "## 私有化授权版更新日志\n\n[在线地址](http://h5.dooring.cn/h5_plus)\n\n#### 2.50\n\n1. 复制页面添加数量校验\n2. 后台系统模版列表添加“上推荐”按钮\n3. 精选模版支持分类,分页\n4. 编辑器"
},
{
"path": "Dockerfile",
"chars": 71,
"preview": "FROM nginx:latest\r\nCOPY ./default.conf /etc/nginx/conf.d/default.conf\r\n"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "SECURITY.md",
"chars": 619,
"preview": "# Security Policy\n\n## Supported Versions\n\nUse this section to tell people about which versions of your project are\ncurre"
},
{
"path": "doc/.vuepress/config.js",
"chars": 4641,
"preview": "module.exports = {\n base: \"/doc/\",\n title: \"h5-dooring\",\n dest: \"./doc-dist\",\n themeConfig: {\n search: false,\n "
},
{
"path": "doc/.vuepress/templates/dev.html",
"chars": 711,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"X-UA-Compatible\" "
},
{
"path": "doc/.vuepress/theme/enhanceApp.js",
"chars": 453,
"preview": "import \"./styles/main.scss\";\n\n// 使用异步函数也是可以的\nexport default ({\n Vue, // VuePress 正在使用的 Vue 构造函数\n options, // 附加到根实例的一些"
},
{
"path": "doc/.vuepress/theme/global-components/Home.vue",
"chars": 3973,
"preview": "<template>\n <main class=\"home\" :aria-labelledby=\"data.heroText !== null ? 'main-title' : null\">\n <header class=\"hero"
},
{
"path": "doc/.vuepress/theme/index.js",
"chars": 58,
"preview": "module.exports = {\n extend: \"@vuepress/theme-default\"\n};\n"
},
{
"path": "doc/.vuepress/theme/layouts/Layout.vue",
"chars": 3639,
"preview": "<template>\n <div\n class=\"theme-container\"\n :class=\"pageClasses\"\n @touchstart=\"onTouchStart\"\n "
},
{
"path": "doc/.vuepress/theme/styles/_layout.scss",
"chars": 322,
"preview": "$accentColor: #083ac4;\n\n.nav-link.external {\n .outbound {\n display: none;\n }\n}\n\n.nav-link.external:last-chi"
},
{
"path": "doc/.vuepress/theme/styles/_reset.scss",
"chars": 9288,
"preview": "/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ==========================="
},
{
"path": "doc/.vuepress/theme/styles/main.scss",
"chars": 35,
"preview": "@import 'reset';\n@import 'layout';\n"
},
{
"path": "doc/.vuepress/theme/styles/palette.styl",
"chars": 22,
"preview": "$accentColor= #083ac4;"
},
{
"path": "doc/README.md",
"chars": 388,
"preview": "---\ncontent: Home\nhome: true\n#heroImage: ../imgs/common/logo.svg\nheroText: 一款所见即所得的H5编辑器\nfeatures:\n - title: 简洁方便\n d"
},
{
"path": "doc/zh/guide/README.md",
"chars": 611,
"preview": "<img src=\"../../img/common/logo.svg\" alt=\"foo\">\n\nH5-Dooring 是一款功能强大,高可扩展的 H5 可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、无限可能的 H5 落地页最佳"
},
{
"path": "doc/zh/guide/building.md",
"chars": 9,
"preview": "正在建设中...\n"
},
{
"path": "doc/zh/guide/componentDev/DSLAnalysis.md",
"chars": 1167,
"preview": "# DSL 设计\n\nDSL 层主要约定了 Dooring 组件的数据协议,包括组件的可编辑属性、编辑类型、初始值等,之所以定义一致的协议层,主要是方便后期的组件扩展,配置后移,有助于不同后端语言开发和数据存储,接下来我们看看 header "
},
{
"path": "doc/zh/guide/componentDev/componentStructure.md",
"chars": 3546,
"preview": "<!--\n * @Date: 2021-01-17 12:25:33\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 19:42:42\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/componentDev/dynamicLoading.md",
"chars": 602,
"preview": "<!--\n * @Date: 2021-01-17 14:24:40\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 19:42:53\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/deployDev/api.md",
"chars": 9454,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/deploy.md",
"chars": 1100,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/deploy_v6.md",
"chars": 1086,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/dir.md",
"chars": 595,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/form.md",
"chars": 1245,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/https.md",
"chars": 1180,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/log.md",
"chars": 1135,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/deployDev/oss.md",
"chars": 3050,
"preview": "<!--\n * @Date: 2021-01-20 23:25:29\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-01-22 21:48:34\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/directoryStructure.md",
"chars": 6308,
"preview": "```\nsrc\n├─ assets\n│ ├─ header.png\n│ ├─ form.png\n│ ├─ footer.png\n│ ├─ icon.png\n│ ├─ picture.png\n├─ components\n│ ├─ "
},
{
"path": "doc/zh/guide/functionRealization/download.md",
"chars": 674,
"preview": "<!--\n * @Date: 2021-01-17 14:26:00\n * @LastEditors: xuxiaoxi\n * @LastEditTime: 2021-05-17 21:32:58\n * @FilePath: /github"
},
{
"path": "doc/zh/guide/functionRealization/machinePreview.md",
"chars": 332,
"preview": "<!--\n * @Date: 2021-01-17 14:27:13\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:49:26\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/functionRealization/pagePreview.md",
"chars": 368,
"preview": "<!--\n * @Date: 2021-01-17 14:26:41\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:49:18\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/functionRealization/revocation.md",
"chars": 1074,
"preview": "<!--\n * @Date: 2021-01-17 14:27:28\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:50:17\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/functionRealization/saveJson.md",
"chars": 537,
"preview": "<!--\n * @Date: 2021-01-17 14:26:00\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:32:58\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/functionRealization/screenshot.md",
"chars": 320,
"preview": "<!--\n * @Date: 2021-01-17 14:27:49\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:49:46\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/functionRealization/templateLibrary.md",
"chars": 527,
"preview": "<!--\n * @Date: 2021-01-17 14:25:29\n * @LastEditors: chentianshang\n * @LastEditTime: 2021-01-17 21:48:34\n * @FilePath: /g"
},
{
"path": "doc/zh/guide/introduced.md",
"chars": 74,
"preview": "<img src=\"../../img/common/framework.png\" alt=\"foo\">\n\n注:灰色部分还未实现,正在更新中...\n"
},
{
"path": "doc/zh/guide/startedQuickly.md",
"chars": 754,
"preview": "# 快速上手\n\n## 从零搭建一个 H5 表单页面\n\n<iframe src=\"//player.bilibili.com/player.html?aid=715343955&bvid=BV1QQ4y1Z725&cid=332145157&"
},
{
"path": "package.json",
"chars": 4223,
"preview": "{\n \"name\": \"h5-dooring\",\n \"version\": \"1.3.0\",\n \"description\": \"H5-Dooring是一款功能强大,开源免费的H5可视化页面配置解决方案,致力于提供一套简单方便、专业可靠、"
},
{
"path": "readme.md",
"chars": 11289,
"preview": "> > Make H5 as easy as building blocks!\n\n<p align=\"center\">\n <img src=\"http://cdn.dooring.cn/dr/logo.ff7fc6bb.png\" wi"
},
{
"path": "server.js",
"chars": 1155,
"preview": "const Koa = require(\"koa\");\nconst { resolve } = require(\"path\");\nconst staticServer = require(\"koa-static\");\nconst koaBo"
},
{
"path": "src/app.tsx",
"chars": 769,
"preview": "import { createLogger } from \"redux-logger\";\nimport { message } from \"antd\";\nimport undoable, { StateWithHistory } from "
},
{
"path": "src/components/Calibration/index.less",
"chars": 225,
"preview": ".calibration {\n width: calc(200% - 50px);\n height: 200%;\n position: relative;\n white-space: nowrap;\n pointer-events"
},
{
"path": "src/components/Calibration/index.tsx",
"chars": 4271,
"preview": "import React, { useState, useEffect, useRef, useCallback } from \"react\";\n\nimport styles from \"./index.less\";\n\nexport int"
},
{
"path": "src/components/ErrorBundaries/index.tsx",
"chars": 760,
"preview": "import React, { ErrorInfo, PropsWithChildren } from \"react\";\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n}\n\ncla"
},
{
"path": "src/components/FormComponents/CardPicker/index.less",
"chars": 271,
"preview": ".pickerWrap {\n display: flex;\n flex-wrap: wrap;\n .picker {\n display: inline-block;\n padding: 10px;\n border: "
},
{
"path": "src/components/FormComponents/CardPicker/index.tsx",
"chars": 1260,
"preview": "import { useState, useEffect, memo } from \"react\";\nimport classnames from \"classnames\";\nimport Icon from \"@/materials/ba"
},
{
"path": "src/components/FormComponents/Color/index.tsx",
"chars": 2246,
"preview": "import React from \"react\";\nimport { SketchPicker, ColorResult } from \"react-color\";\nimport { rgba2Obj } from \"@/utils/to"
},
{
"path": "src/components/FormComponents/DataList/editorModal.tsx",
"chars": 3182,
"preview": "import React, { memo, useEffect, FC } from \"react\";\nimport { Form, Select, Input, Modal, Button } from \"antd\";\nimport Up"
},
{
"path": "src/components/FormComponents/DataList/index.less",
"chars": 794,
"preview": ".dataList {\n padding: 6px 10px;\n border: 1px solid #f0f0f0;\n text-align: justify;\n padding-left: 10px;\n padding-top"
},
{
"path": "src/components/FormComponents/DataList/index.tsx",
"chars": 6527,
"preview": "import React, { memo, useState, useEffect, useCallback } from \"react\";\nimport {\n EditOutlined,\n MinusCircleOutlined,\n "
},
{
"path": "src/components/FormComponents/FormItems/EditorModal.tsx",
"chars": 3515,
"preview": "import React, { FC, memo, useEffect } from \"react\";\nimport { Form, Select, Input, Modal, Button, InputNumber } from \"ant"
},
{
"path": "src/components/FormComponents/FormItems/FormItems.tsx",
"chars": 5992,
"preview": "import React, {\n memo,\n RefObject,\n useCallback,\n useEffect,\n useState\n} from \"react\";\nimport BaseForm from \"@/mate"
},
{
"path": "src/components/FormComponents/FormItems/formItems.less",
"chars": 1889,
"preview": ".formItemWrap {\n .formTitle {\n width: 56px;\n height: 20px;\n font-size: 14px;\n font-family: PingFangSC-Mediu"
},
{
"path": "src/components/FormComponents/FormItems/index.tsx",
"chars": 63,
"preview": "import FormItems from \"./FormItems\";\nexport default FormItems;\n"
},
{
"path": "src/components/FormComponents/MutiText/index.less",
"chars": 195,
"preview": ".mutiText {\n .iptWrap {\n margin-bottom: 12px;\n display: flex;\n .delBtn {\n // font-size: 14px;\n margi"
},
{
"path": "src/components/FormComponents/MutiText/index.tsx",
"chars": 2002,
"preview": "import React, { memo, useEffect } from \"react\";\nimport { Input, Button, Popconfirm } from \"antd\";\nimport { MinusCircleFi"
},
{
"path": "src/components/FormComponents/Pos/index.less",
"chars": 164,
"preview": ".posIpt {\n display: flex;\n justify-content: flex-end;\n margin-right: -10px;\n .posItem {\n margin-right: 10px;\n "
},
{
"path": "src/components/FormComponents/Pos/index.tsx",
"chars": 1023,
"preview": "import React, { memo, useState, useEffect } from \"react\";\nimport { InputNumber } from \"antd\";\nimport styles from \"./inde"
},
{
"path": "src/components/FormComponents/Table/index.less",
"chars": 515,
"preview": ":global(.editable-cell) {\n position: relative;\n}\n\n:global(.editable-cell-value-wrap) {\n padding: 5px 12px;\n cursor: p"
},
{
"path": "src/components/FormComponents/Table/index.tsx",
"chars": 11109,
"preview": "import React, {\n useContext,\n useState,\n useEffect,\n useRef,\n memo,\n RefObject\n} from \"react\";\nimport { Table, Inp"
},
{
"path": "src/components/FormComponents/Upload/index.less",
"chars": 956,
"preview": ":global(.ant-upload-select-picture-card i) {\n color: #999;\n font-size: 14px;\n}\n\n:global(.ant-upload-select-picture-car"
},
{
"path": "src/components/FormComponents/Upload/index.tsx",
"chars": 7844,
"preview": "import React from \"react\";\nimport { Upload, Modal, message, Tabs, Result } from \"antd\";\nimport { PlusOutlined, CheckCirc"
},
{
"path": "src/components/FormComponents/XEditor/index.less",
"chars": 76,
"preview": ".avatarUploader > :global(.ant-upload) {\n width: 128px;\n height: 128px;\n}\n"
},
{
"path": "src/components/FormComponents/XEditor/index.tsx",
"chars": 2094,
"preview": "import React, { useState, useEffect, memo } from \"react\";\nimport req from \"@/utils/req\";\nimport BraftEditor from \"braft-"
},
{
"path": "src/components/FormComponents/types.ts",
"chars": 4522,
"preview": "////////////////////\nexport interface IUploadConfigType {\n key: string;\n name: string;\n type: \"Upload\";\n isCrop?: bo"
},
{
"path": "src/components/LoadingCp/index.tsx",
"chars": 96,
"preview": "import React from \"react\";\n\nexport default () => <div className=\"rotate-animate\">Dooring</div>;\n"
},
{
"path": "src/components/Zan/index.less",
"chars": 96,
"preview": ".takeCat {\n display: inline-block;\n}\n.imgWrap {\n width: 160px;\n img {\n width: 100%;\n }\n}\n"
},
{
"path": "src/components/Zan/index.tsx",
"chars": 791,
"preview": "import React, { memo } from \"react\";\nimport { Button, Popover } from \"antd\";\nimport styles from \"./index.less\";\n\ninterfa"
},
{
"path": "src/core/DynamicEngine.tsx",
"chars": 1243,
"preview": "import { dynamic } from \"umi\";\nimport Loading from \"../components/LoadingCp\";\nimport { useMemo, memo, FC } from \"react\";"
},
{
"path": "src/core/index.ts",
"chars": 191,
"preview": "import DynamicEngine from \"./DynamicEngine\";\nimport ViewRender from \"./renderer/ViewRender\";\nimport FormRender from \"./r"
},
{
"path": "src/core/renderer/FormRender.tsx",
"chars": 5661,
"preview": "import React, { memo, RefObject, useEffect } from \"react\";\nimport { Form, Select, InputNumber, Input, Switch, Radio } fr"
},
{
"path": "src/core/renderer/ViewRender.tsx",
"chars": 1486,
"preview": "import React, { memo } from \"react\";\nimport GridLayout, { ItemCallback } from \"react-grid-layout\";\nimport DynamicEngine "
},
{
"path": "src/core/renderer/viewRender.less",
"chars": 233,
"preview": ".dragItem {\n display: inline-block;\n position: absolute;\n z-index: 2;\n border: 2px solid transparent;\n cursor: move"
},
{
"path": "src/global.css",
"chars": 3527,
"preview": "html, body, #root {\n height: 100%;\n overflow: hidden;\n}\n\nbody {\n margin: 0;\n}\n\n:root {\n --sk-size: 40px;\n --sk-colo"
},
{
"path": "src/layouts/index.less",
"chars": 647,
"preview": ".dragPay {\n position: absolute;\n z-index: 99999;\n left: 462px;\n bottom: 56px;\n border-radius: 20px;\n background: #"
},
{
"path": "src/layouts/index.tsx",
"chars": 5023,
"preview": "import React, { useCallback, useState, useEffect } from \"react\";\nimport {\n library,\n generateRespones,\n RenderList,\n "
},
{
"path": "src/materials/base/Carousel/index.less",
"chars": 158,
"preview": ".carousel__item__pic {\n display: inline-block;\n width: 100%;\n max-height: 220px;\n overflow: hidden;\n vertical-align"
},
{
"path": "src/materials/base/Carousel/index.tsx",
"chars": 1390,
"preview": "import React, { memo, PropsWithChildren } from \"react\";\nimport { Carousel } from \"zarm\";\nimport styles from \"./index.les"
},
{
"path": "src/materials/base/Carousel/schema.ts",
"chars": 2224,
"preview": "import {\n IDataListConfigType,\n IRadioConfigType,\n ISwitchConfigType,\n INumberConfigType,\n TDataListDefaultType,\n "
},
{
"path": "src/materials/base/Carousel/template.ts",
"chars": 99,
"preview": "const template = {\n type: \"Carousel\",\n h: 82,\n displayName: \"轮播图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Form/BaseForm.tsx",
"chars": 4055,
"preview": "import { Input, Cell, DateSelect, Radio, Select, Checkbox } from \"zarm\";\nimport styles from \"./baseForm.less\";\nimport Re"
},
{
"path": "src/materials/base/Form/BasePopoverForm.tsx",
"chars": 3160,
"preview": "import React, { ReactText } from \"react\";\nimport { Button } from \"antd\";\nimport {\n baseFormDateTpl,\n baseFormMyRadioTp"
},
{
"path": "src/materials/base/Form/baseForm.less",
"chars": 123,
"preview": ".radioWrap {\n margin-bottom: 10px;\n .radioTitle {\n padding: 19px 14px;\n }\n .radioItem {\n margin-top: 10px;\n }"
},
{
"path": "src/materials/base/Form/index.less",
"chars": 250,
"preview": ".formWrap {\n margin: 10px;\n padding: 20px 16px;\n border-radius: 6px;\n background-color: #fff;\n box-shadow: 0 2px 6p"
},
{
"path": "src/materials/base/Form/index.tsx",
"chars": 2487,
"preview": "import React, { memo, useCallback } from \"react\";\nimport { Button } from \"zarm\";\nimport BaseForm from \"./BaseForm\";\nimpo"
},
{
"path": "src/materials/base/Form/schema.ts",
"chars": 2732,
"preview": "import {\n IColorConfigType,\n IFormItemsConfigType,\n INumberConfigType,\n ITextConfigType,\n TColorDefaultType,\n TFor"
},
{
"path": "src/materials/base/Form/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"Form\",\n h: 172,\n displayName: \"表格组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Header/index.less",
"chars": 383,
"preview": ".header {\n box-sizing: content-box;\n padding: 3px 12px;\n display: flex;\n align-items: center;\n height: 50px;\n back"
},
{
"path": "src/materials/base/Header/index.tsx",
"chars": 1206,
"preview": "import { memo } from \"react\";\nimport styles from \"./index.less\";\nimport React from \"react\";\nimport { IHeaderConfig } fro"
},
{
"path": "src/materials/base/Header/schema.ts",
"chars": 1654,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n IUploadConfigType,\n TColorDefaultType,\n TNumber"
},
{
"path": "src/materials/base/Header/template.ts",
"chars": 96,
"preview": "const template = {\n type: \"Header\",\n h: 28,\n displayName: \"页头组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Icon/icon.ts",
"chars": 6646,
"preview": "export type AntdIconType =\n | \"max\"\n | \"required\"\n | \"default\"\n | \"high\"\n | \"low\"\n | \"disabled\"\n | \"start\"\n | \"o"
},
{
"path": "src/materials/base/Icon/index.tsx",
"chars": 1407,
"preview": "import React, { memo } from \"react\";\nimport * as Icon from \"@ant-design/icons\";\nimport { AntdIconProps } from \"@ant-desi"
},
{
"path": "src/materials/base/Icon/schema.ts",
"chars": 3599,
"preview": "import {\n ICardPickerConfigType,\n IColorConfigType,\n ITextConfigType,\n INumberConfigType,\n ISwitchConfigType,\n TCa"
},
{
"path": "src/materials/base/Icon/template.ts",
"chars": 94,
"preview": "const template = {\n type: \"Icon\",\n h: 36,\n displayName: \"图标组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Image/index.tsx",
"chars": 2318,
"preview": "import React, { memo } from \"react\";\nimport { IImageConfig } from \"./schema\";\nimport logo from \"@/assets/img.png\";\nconst"
},
{
"path": "src/materials/base/Image/schema.ts",
"chars": 3684,
"preview": "import {\n INumberConfigType,\n IUploadConfigType,\n TNumberDefaultType,\n TUploadDefaultType,\n IColorConfigType,\n TCo"
},
{
"path": "src/materials/base/Image/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"Image\",\n h: 80,\n displayName: \"图片组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/List/index.less",
"chars": 292,
"preview": ".list {\n margin: 20px auto;\n width: 94%;\n .sourceList {\n .sourceItem {\n display: flex;\n align-items: cen"
},
{
"path": "src/materials/base/List/index.tsx",
"chars": 2280,
"preview": "import React, { memo } from \"react\";\nimport styles from \"./index.less\";\nimport { IListConfig } from \"./schema\";\nimport l"
},
{
"path": "src/materials/base/List/schema.ts",
"chars": 2455,
"preview": "import {\n IColorConfigType,\n IDataListConfigType,\n INumberConfigType,\n ISelectConfigType,\n TColorDefaultType,\n TDa"
},
{
"path": "src/materials/base/List/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"List\",\n h: 110,\n displayName: \"列表组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/LongText/index.tsx",
"chars": 818,
"preview": "import React, { memo } from \"react\";\nimport { ILongTextConfig } from \"./schema\";\nimport logo from \"@/assets/longText.png"
},
{
"path": "src/materials/base/LongText/schema.ts",
"chars": 2166,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ISelectConfigType,\n ITextAreaConfigType,\n TColorDefaultType,\n TNu"
},
{
"path": "src/materials/base/LongText/template.ts",
"chars": 99,
"preview": "const template = {\n type: \"LongText\",\n h: 36,\n displayName: \"长文本组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Notice/index.tsx",
"chars": 672,
"preview": "import { NoticeBar } from \"zarm\";\nimport React, { memo } from \"react\";\nimport { INoticeConfig } from \"./schema\";\nimport "
},
{
"path": "src/materials/base/Notice/schema.ts",
"chars": 1566,
"preview": "import {\n INumberConfigType,\n ISelectConfigType,\n ISwitchConfigType,\n ITextConfigType,\n TNumberDefaultType,\n TSele"
},
{
"path": "src/materials/base/Notice/template.ts",
"chars": 96,
"preview": "const template = {\n type: \"Notice\",\n h: 20,\n displayName: \"通知组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Qrcode/index.tsx",
"chars": 786,
"preview": "import React, { memo } from \"react\";\nimport { IQrcodeConfig } from \"./schema\";\nimport logo from \"@/assets/qrcode.png\";\nc"
},
{
"path": "src/materials/base/Qrcode/schema.ts",
"chars": 1243,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n IUploadConfigType,\n TColorDefaultType,\n TNumber"
},
{
"path": "src/materials/base/Qrcode/template.ts",
"chars": 98,
"preview": "const template = {\n type: \"Qrcode\",\n h: 150,\n displayName: \"二维码组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/RichText/index.less",
"chars": 304,
"preview": ".richTextWrap {\n :global(p) {\n margin-bottom: 0;\n }\n :global(img) {\n max-width: 100%;\n text-align: center;\n "
},
{
"path": "src/materials/base/RichText/index.tsx",
"chars": 770,
"preview": "import React, { memo, useState } from \"react\";\nimport styles from \"./index.less\";\nimport { IButtonConfig } from \"./schem"
},
{
"path": "src/materials/base/RichText/schema.ts",
"chars": 1212,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberDefaultType,\n TTextD"
},
{
"path": "src/materials/base/RichText/template.ts",
"chars": 100,
"preview": "const template = {\n type: \"RichText\",\n h: 120,\n displayName: \"富文本组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Tab/index.less",
"chars": 499,
"preview": ".tabWrap {\n padding-top: 16px;\n padding-bottom: 16px;\n .content {\n display: flex;\n flex-wrap: wrap;\n .item {"
},
{
"path": "src/materials/base/Tab/index.tsx",
"chars": 2445,
"preview": "import React, { memo, useEffect, useRef } from \"react\";\nimport { Tabs } from \"zarm\";\nimport styles from \"./index.less\";\n"
},
{
"path": "src/materials/base/Tab/schema.ts",
"chars": 2343,
"preview": "import {\n IColorConfigType,\n IDataListConfigType,\n IMutiTextConfigType,\n INumberConfigType,\n TColorDefaultType,\n T"
},
{
"path": "src/materials/base/Tab/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"Tab\",\n h: 130,\n displayName: \"切换页组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/Text/index.tsx",
"chars": 516,
"preview": "import React, { memo } from \"react\";\nimport { ITextConfig } from \"./schema\";\nimport logo from \"@/assets/text.png\";\nconst"
},
{
"path": "src/materials/base/Text/schema.ts",
"chars": 1497,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ISelectConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumber"
},
{
"path": "src/materials/base/Text/template.ts",
"chars": 94,
"preview": "const template = {\n type: \"Text\",\n h: 20,\n displayName: \"文本组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/WhiteTpl/index.less",
"chars": 92,
"preview": ".whiteTpl {\n text-align: center;\n .title {\n // font-size: 14px;\n color: #fff;\n }\n}\n"
},
{
"path": "src/materials/base/WhiteTpl/index.tsx",
"chars": 826,
"preview": "import { memo } from \"react\";\nimport styles from \"./index.less\";\nimport React from \"react\";\nimport { IWhiteTplConfig } f"
},
{
"path": "src/materials/base/WhiteTpl/schema.ts",
"chars": 1137,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberDefaultType,\n TTextD"
},
{
"path": "src/materials/base/WhiteTpl/template.ts",
"chars": 98,
"preview": "const template = {\n type: \"WhiteTpl\",\n h: 20,\n displayName: \"空白组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/base/schema.ts",
"chars": 665,
"preview": "import Carousel from \"./Carousel/schema\";\nimport Form from \"./Form/schema\";\nimport Header from \"./Header/schema\";\nimport"
},
{
"path": "src/materials/base/template.ts",
"chars": 784,
"preview": "import Carousel from \"./Carousel/template\";\nimport Form from \"./Form/template\";\nimport Header from \"./Header/template\";\n"
},
{
"path": "src/materials/common.ts",
"chars": 1118,
"preview": "import {\n INumberConfigType,\n TNumberDefaultType\n} from \"../PanelComponents/FormEditor/types\";\n\n///提取所有公用设置,传来时加到这里,约定"
},
{
"path": "src/materials/media/Audio/index.less",
"chars": 71,
"preview": ".audioWrap {\n height: 100%;\n display: flex;\n align-items: center;\n}\n"
},
{
"path": "src/materials/media/Audio/index.tsx",
"chars": 852,
"preview": "import React, { memo } from \"react\";\nimport ReactAudioPlayer from \"react-audio-player\";\nimport styles from \"./index.less"
},
{
"path": "src/materials/media/Audio/schema.ts",
"chars": 678,
"preview": "import {\n ITextConfigType,\n INumberConfigType,\n TTextDefaultType,\n TNumberDefaultType\n} from \"@/components/FormCompo"
},
{
"path": "src/materials/media/Audio/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"Audio\",\n h: 48,\n displayName: \"音频组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/media/Calendar/index.less",
"chars": 1689,
"preview": ".calenderWrap {\n position: relative;\n // height: 100%;\n overflow: hidden;\n background-color: #fff;\n :global(.za-cal"
},
{
"path": "src/materials/media/Calendar/index.tsx",
"chars": 1835,
"preview": "import React, { useState, memo, useEffect, useRef } from \"react\";\nimport { Calendar } from \"zarm\";\nimport styles from \"."
},
{
"path": "src/materials/media/Calendar/schema.ts",
"chars": 1203,
"preview": "import {\n ITextConfigType,\n INumberConfigType,\n TTextDefaultType,\n TNumberDefaultType,\n IColorConfigType\n} from \"@/"
},
{
"path": "src/materials/media/Calendar/template.ts",
"chars": 99,
"preview": "const template = {\n type: \"Calendar\",\n h: 185,\n displayName: \"日历组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/media/Map/index.less",
"chars": 102,
"preview": ".mapWrap {\n height: 100%;\n display: flex;\n align-items: center;\n & > div {\n width: 100%;\n }\n}\n"
},
{
"path": "src/materials/media/Map/index.tsx",
"chars": 1262,
"preview": "import React, { memo } from \"react\";\nimport { Map, Marker, Label, APILoader } from \"@uiw/react-baidu-map\";\nimport styles"
},
{
"path": "src/materials/media/Map/schema.ts",
"chars": 984,
"preview": "import {\n ITextConfigType,\n ITextAreaConfigType,\n IPosConfigType,\n TTextDefaultType,\n TPosDefaultType,\n TTextAreaD"
},
{
"path": "src/materials/media/Map/template.ts",
"chars": 94,
"preview": "const template = {\n type: \"Map\",\n h: 120,\n displayName: \"地图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/media/Video/index.css",
"chars": 51577,
"preview": "@charset \"UTF-8\";\n.video-react .video-react-control:before, .video-react .video-react-big-play-button:before {\n positio"
},
{
"path": "src/materials/media/Video/index.tsx",
"chars": 790,
"preview": "import React, { memo } from \"react\";\nimport { Player, BigPlayButton } from \"video-react\";\nimport \"./index.css\";\nimport {"
},
{
"path": "src/materials/media/Video/schema.ts",
"chars": 801,
"preview": "import {\n ITextConfigType,\n IUploadConfigType,\n TTextDefaultType,\n TUploadDefaultType\n} from \"@/components/FormCompo"
},
{
"path": "src/materials/media/Video/template.ts",
"chars": 96,
"preview": "const template = {\n type: \"Video\",\n h: 107,\n displayName: \"视频组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/media/schema.ts",
"chars": 236,
"preview": "import Video from \"./Video/schema\";\nimport Audio from \"./Audio/schema\";\nimport Calendar from \"./Calendar/schema\";\nimport"
},
{
"path": "src/materials/media/template.ts",
"chars": 329,
"preview": "import Video from \"./Video/template\";\nimport Audio from \"./Audio/template\";\nimport Map from \"./Map/template\";\nimport Cal"
},
{
"path": "src/materials/schema.ts",
"chars": 283,
"preview": "import BasicSchema from \"./base/schema\";\nimport MediaSchema from \"./media/schema\";\nimport VisualSchema from \"./visual/sc"
},
{
"path": "src/materials/shop/CardLabel/index.less",
"chars": 615,
"preview": ".topArea {\n padding: 10px;\n display: flex;\n background: linear-gradient(\n 45deg,\n rgba(212, 176, 11, 1),\n rg"
},
{
"path": "src/materials/shop/CardLabel/index.tsx",
"chars": 1455,
"preview": "import React, { memo } from \"react\";\nimport { IZLConfig } from \"./schema\";\nimport logo from \"@/assets/cardLabel.png\";\nim"
},
{
"path": "src/materials/shop/CardLabel/schema.ts",
"chars": 1861,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n ITextAreaConfigType,\n IUploadConfigType,\n IRich"
},
{
"path": "src/materials/shop/CardLabel/template.ts",
"chars": 99,
"preview": "const template = {\n type: \"CardLabel\",\n h: 40,\n displayName: \"商品标签\"\n};\nexport default template;\n"
},
{
"path": "src/materials/shop/Coupons/index.less",
"chars": 568,
"preview": ".topArea {\n padding: 16px;\n display: flex;\n background-color: rgb(228, 162, 22);\n .leftBox {\n color: #fff;\n sp"
},
{
"path": "src/materials/shop/Coupons/index.tsx",
"chars": 1312,
"preview": "import React, { memo } from \"react\";\nimport { IZLConfig } from \"./schema\";\nimport logo from \"@/assets/cunpos.png\";\nimpor"
},
{
"path": "src/materials/shop/Coupons/schema.ts",
"chars": 1513,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n ITextAreaConfigType,\n IUploadConfigType,\n IRich"
},
{
"path": "src/materials/shop/Coupons/template.ts",
"chars": 98,
"preview": "const template = {\n type: \"Coupons\",\n h: 72,\n displayName: \"优惠券组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/shop/List/index.less",
"chars": 880,
"preview": ".list {\n margin: 20px auto;\n width: 94%;\n .searchWrap {\n margin-bottom: 16px;\n display: flex;\n width: 100%;\n"
},
{
"path": "src/materials/shop/List/index.tsx",
"chars": 2655,
"preview": "import React, { memo, useState, useRef } from \"react\";\nimport { SearchOutlined } from \"@ant-design/icons\";\nimport styles"
},
{
"path": "src/materials/shop/List/schema.ts",
"chars": 2697,
"preview": "import {\n IColorConfigType,\n IDataListConfigType,\n INumberConfigType,\n ISelectConfigType,\n TColorDefaultType,\n ISw"
},
{
"path": "src/materials/shop/List/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"List\",\n h: 110,\n displayName: \"列表组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/shop/Tab/index.less",
"chars": 640,
"preview": ".tabWrap {\n padding-top: 16px;\n padding-bottom: 16px;\n .content {\n display: flex;\n flex-wrap: wrap;\n .item {"
},
{
"path": "src/materials/shop/Tab/index.tsx",
"chars": 2267,
"preview": "import React, { useEffect, useRef } from \"react\";\nimport { Tabs } from \"zarm\";\nimport styles from \"./index.less\";\nimport"
},
{
"path": "src/materials/shop/Tab/schema.ts",
"chars": 2422,
"preview": "import {\n IColorConfigType,\n IDataListConfigType,\n IMutiTextConfigType,\n INumberConfigType,\n TColorDefaultType,\n T"
},
{
"path": "src/materials/shop/Tab/template.ts",
"chars": 95,
"preview": "const template = {\n type: \"Tab\",\n h: 130,\n displayName: \"切换页组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/shop/ZhuanLan/index.less",
"chars": 739,
"preview": ".topArea {\n display: flex;\n .tx {\n flex-shrink: 0;\n width: 72px;\n height: 72px;\n border-radius: 6px;\n b"
},
{
"path": "src/materials/shop/ZhuanLan/index.tsx",
"chars": 1962,
"preview": "import React, { memo } from \"react\";\nimport { IZLConfig } from \"./schema\";\nimport logo from \"@/assets/zhuanlan.png\";\nimp"
},
{
"path": "src/materials/shop/ZhuanLan/schema.ts",
"chars": 2456,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITextConfigType,\n ITextAreaConfigType,\n IUploadConfigType,\n IRich"
},
{
"path": "src/materials/shop/ZhuanLan/template.ts",
"chars": 99,
"preview": "const template = {\n type: \"ZhuanLan\",\n h: 120,\n displayName: \"专栏组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/shop/schema.ts",
"chars": 294,
"preview": "import ZhuanLan from \"./ZhuanLan/schema\";\nimport Tab from \"./Tab/schema\";\nimport List from \"./List/schema\";\nimport Coupo"
},
{
"path": "src/materials/shop/template.ts",
"chars": 385,
"preview": "import ZhuanLan from \"./ZhuanLan/template\";\nimport Tab from \"./Tab/template\";\nimport List from \"./List/template\";\nimport"
},
{
"path": "src/materials/visual/Area/index.less",
"chars": 157,
"preview": ".chartWrap {\n position: relative;\n width: 100%;\n .chartTitle {\n text-align: center;\n }\n img {\n width: 100%;\n "
},
{
"path": "src/materials/visual/Area/index.tsx",
"chars": 1996,
"preview": "import { Chart } from \"@antv/f2\";\nimport React, { memo, useEffect, useRef } from \"react\";\n// import { uuid } from 'utils"
},
{
"path": "src/materials/visual/Area/schema.ts",
"chars": 1366,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITableConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberD"
},
{
"path": "src/materials/visual/Area/template.ts",
"chars": 96,
"preview": "const template = {\n type: \"Area\",\n h: 108,\n displayName: \"面积图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/visual/Chart/index.less",
"chars": 157,
"preview": ".chartWrap {\n position: relative;\n width: 100%;\n .chartTitle {\n text-align: center;\n }\n img {\n width: 100%;\n "
},
{
"path": "src/materials/visual/Chart/index.tsx",
"chars": 1396,
"preview": "import { Chart } from \"@antv/f2\";\nimport React, { memo, useEffect, useRef } from \"react\";\n// import { uuid } from 'utils"
},
{
"path": "src/materials/visual/Chart/schema.ts",
"chars": 1312,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITableConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberD"
},
{
"path": "src/materials/visual/Chart/template.ts",
"chars": 97,
"preview": "const template = {\n type: \"Chart\",\n h: 102,\n displayName: \"柱状图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/visual/Line/index.less",
"chars": 157,
"preview": ".chartWrap {\n position: relative;\n width: 100%;\n .chartTitle {\n text-align: center;\n }\n img {\n width: 100%;\n "
},
{
"path": "src/materials/visual/Line/index.tsx",
"chars": 1880,
"preview": "import { Chart } from \"@antv/f2\";\nimport React, { memo, useEffect, useRef } from \"react\";\n// import { uuid } from 'utils"
},
{
"path": "src/materials/visual/Line/schema.ts",
"chars": 1366,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITableConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberD"
},
{
"path": "src/materials/visual/Line/template.ts",
"chars": 96,
"preview": "const template = {\n type: \"Line\",\n h: 104,\n displayName: \"折线图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/visual/Pie/index.less",
"chars": 157,
"preview": ".chartWrap {\n position: relative;\n width: 100%;\n .chartTitle {\n text-align: center;\n }\n img {\n width: 100%;\n "
},
{
"path": "src/materials/visual/Pie/index.tsx",
"chars": 2581,
"preview": "import { Chart } from \"@antv/f2\";\nimport React, { memo, useEffect, useRef } from \"react\";\n// import { uuid } from 'utils"
},
{
"path": "src/materials/visual/Pie/schema.ts",
"chars": 1365,
"preview": "import {\n IColorConfigType,\n INumberConfigType,\n ITableConfigType,\n ITextConfigType,\n TColorDefaultType,\n TNumberD"
},
{
"path": "src/materials/visual/Pie/template.ts",
"chars": 94,
"preview": "const template = {\n type: \"Pie\",\n h: 106,\n displayName: \"饼图组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/visual/XProgress/index.tsx",
"chars": 708,
"preview": "import React, { memo } from \"react\";\nimport { Progress } from \"zarm\";\nimport { IXProgressConfig } from \"./schema\";\nimpor"
},
{
"path": "src/materials/visual/XProgress/schema.ts",
"chars": 1864,
"preview": "import {\n INumberConfigType,\n IRadioConfigType,\n ISelectConfigType,\n TNumberDefaultType,\n TRadioDefaultType,\n TSel"
},
{
"path": "src/materials/visual/XProgress/template.ts",
"chars": 101,
"preview": "const template = {\n type: \"XProgress\",\n h: 102,\n displayName: \"进度条组件\"\n};\nexport default template;\n"
},
{
"path": "src/materials/visual/schema.ts",
"chars": 281,
"preview": "import Chart from \"./Chart/schema\";\nimport Line from \"./Line/schema\";\nimport Pie from \"./Pie/schema\";\nimport Area from \""
},
{
"path": "src/materials/visual/template.ts",
"chars": 375,
"preview": "import Chart from \"./Chart/template\";\nimport Line from \"./Line/template\";\nimport Pie from \"./Pie/template\";\nimport Area "
},
{
"path": "src/pages/document.ejs",
"chars": 956,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <link href=\"http://49.234.61.19/uploads/logo_1742"
},
{
"path": "src/pages/editor/Container.tsx",
"chars": 13220,
"preview": "import React, {\n useState,\n useEffect,\n useMemo,\n useCallback,\n useRef\n} from \"react\";\nimport { Result, Tabs } from"
},
{
"path": "src/pages/editor/SourceBox.tsx",
"chars": 1761,
"preview": "import React, {\n useMemo,\n memo,\n ReactNode,\n useContext,\n CSSProperties\n} from \"react\";\nimport { useDrag } from \"r"
}
]
// ... and 36 more files (download for full content)
About this extraction
This page contains the full source code of the MrXujiang/h5-Dooring GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 236 files (1.6 MB), approximately 482.3k tokens, and a symbol index with 561 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.