├── src
├── ingredients
│ ├── components.ts
│ ├── export
│ │ ├── cssTemplate.ts
│ │ ├── jsTemplate.ts
│ │ ├── htmlTemplate.ts
│ │ ├── index.ts
│ │ └── resetCssTemplate.ts
│ ├── elements
│ │ ├── index.ts
│ │ ├── Text.ts
│ │ ├── Button.ts
│ │ ├── Img.ts
│ │ ├── Circle.ts
│ │ └── Input.ts
│ ├── components
│ │ └── index.ts
│ ├── layouts
│ │ ├── LeftBox.ts
│ │ ├── RightBox.ts
│ │ ├── FixedBox.ts
│ │ ├── BaseBox.ts
│ │ ├── CeilingBox.ts
│ │ ├── index.ts
│ │ ├── PageTpl2.ts
│ │ └── PageTpl1.ts
│ └── cookEngine.ts
├── assets
│ ├── img
│ │ ├── wg.png
│ │ ├── grid.png
│ │ └── wg.svg
│ ├── global.css
│ └── normalize.css
├── pages
│ ├── Preview
│ │ ├── index.module.less
│ │ └── index.tsx
│ └── Home
│ │ └── index.tsx
├── db
│ ├── connect.ts
│ ├── db_config.ts
│ ├── state.ts
│ └── use.ts
├── routes.ts
├── app.tsx
├── components
│ ├── RulerLine
│ │ ├── index.tsx
│ │ └── index.module.less
│ ├── EditPage
│ │ ├── ActionPanel.tsx
│ │ ├── UploadImg.tsx
│ │ ├── ZoomPlugin.tsx
│ │ ├── index.module.less
│ │ ├── index.tsx
│ │ ├── EditLineBox.tsx
│ │ └── StylePanel.tsx
│ ├── ToolBarBox
│ │ ├── index.tsx
│ │ └── index.module.less
│ └── PageBoard
│ │ ├── index.module.less
│ │ └── index.tsx
├── utils
│ ├── tool.js
│ ├── idb.js
│ ├── drag.js
│ └── styleFormat.js
└── typings.d.ts
├── .stylelintignore
├── public
├── fonts
│ ├── fa-regular-400.ttf
│ └── fa-solid-900.woff2
├── index.html
├── favicon.png
└── libs
│ ├── FileSaver.min.js
│ └── panzoom.min.js
├── .prettierrc.js
├── .stylelintrc.js
├── .prettierignore
├── .eslintignore
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── tsconfig.json
├── README.md
└── package.json
/src/ingredients/components.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/wg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nullno/cook-web/HEAD/src/assets/img/wg.png
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | # 忽略目录
2 | build/
3 | tests/
4 | demo/
5 |
6 | # node 覆盖率文件
7 | coverage/
8 |
--------------------------------------------------------------------------------
/src/assets/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nullno/cook-web/HEAD/src/assets/img/grid.png
--------------------------------------------------------------------------------
/src/pages/Preview/index.module.less:
--------------------------------------------------------------------------------
1 | .cookPreview {
2 | width: 100%;
3 | overflow: hidden;
4 | }
5 |
--------------------------------------------------------------------------------
/public/fonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nullno/cook-web/HEAD/public/fonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/public/fonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nullno/cook-web/HEAD/public/fonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | const { getPrettierConfig } = require('@iceworks/spec');
2 |
3 | module.exports = getPrettierConfig('react');
4 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | const { getStylelintConfig } = require('@iceworks/spec');;
2 |
3 | module.exports = getStylelintConfig('react');
4 |
--------------------------------------------------------------------------------
/src/db/connect.ts:
--------------------------------------------------------------------------------
1 | import Idb from 'idb-js'
2 |
3 | import db_config from './db_config';
4 |
5 | export default Idb(db_config);
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | build/
2 | tests/
3 | demo/
4 | .ice/
5 | coverage/
6 | **/*-min.js
7 | **/*.min.js
8 | package-lock.json
9 | yarn.lock
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # 忽略目录
2 | build/
3 | tests/
4 | demo/
5 | .ice/
6 |
7 | # node 覆盖率文件
8 | coverage/
9 |
10 | # 忽略文件
11 | **/*-min.js
12 | **/*.min.js
13 |
--------------------------------------------------------------------------------
/src/ingredients/export/cssTemplate.ts:
--------------------------------------------------------------------------------
1 | export default (page, cssStr) => {
2 | return `
3 | /*
4 | *@File css ${page.id}
5 | *@Date ${Date()}
6 | */
7 | ${cssStr}
8 | `
9 | }
10 |
--------------------------------------------------------------------------------
/src/ingredients/export/jsTemplate.ts:
--------------------------------------------------------------------------------
1 | export default (page, jsStr) => {
2 | return `
3 | /*
4 | *@File js ${page.id}
5 | *@Date ${Date()}
6 | *@Plugins []
7 | */
8 | ${jsStr}
9 | `
10 | }
11 |
--------------------------------------------------------------------------------
/src/ingredients/elements/index.ts:
--------------------------------------------------------------------------------
1 | const modules = import.meta.globEager("./*.ts");
2 |
3 | const Elements: DomItem[] = [];
4 | for (let m in modules) {
5 | Elements.push(modules[m].default);
6 | }
7 |
8 | export default Elements;
9 |
--------------------------------------------------------------------------------
/src/ingredients/components/index.ts:
--------------------------------------------------------------------------------
1 | const modules = import.meta.globEager("./*.ts");
2 |
3 | const Components: DomItem[] = [];
4 | for (let m in modules) {
5 | Components.push(modules[m].default);
6 | }
7 |
8 | export default Components;
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/src/ingredients/elements/Text.ts:
--------------------------------------------------------------------------------
1 | const Text = {
2 | type: 'Text',
3 | name: '文本',
4 | tag: 'span',
5 | text:'文本',
6 | style: {
7 | color: '#000',
8 | fontSize: '14px',
9 | outline:'none'
10 | },
11 | stopAdd: true,
12 | edit: {},
13 | };
14 | export default Text;
15 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const { getESLintConfig } = require('@iceworks/spec');
2 |
3 | // https://www.npmjs.com/package/@iceworks/spec
4 | module.exports = getESLintConfig('react-ts', {
5 | rules: {
6 | 'react/jsx-filename-extension': 0,
7 | '@typescript-eslint/explicit-function-return-type': 0,
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/LeftBox.ts:
--------------------------------------------------------------------------------
1 | const LeftBox = {
2 | type: 'LeftBox',
3 | name: '居左',
4 | tag: 'div',
5 | dragDots: ['right', 'bottom'],
6 | style: {
7 | width: '100px',
8 | height: '100%',
9 | float: 'left',
10 | border: '1px dashed #EE771F',
11 | },
12 | child: [],
13 | };
14 | export default LeftBox;
15 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/RightBox.ts:
--------------------------------------------------------------------------------
1 | const RightBox = {
2 | type: 'RightBox',
3 | name: '居右',
4 | tag: 'div',
5 | dragDots: ['left', 'bottom'],
6 | style: {
7 | width: '100px',
8 | height: '100%',
9 | float: 'right',
10 | border: '1px dashed #EE771F',
11 | },
12 | child: [],
13 | };
14 | export default RightBox;
15 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/FixedBox.ts:
--------------------------------------------------------------------------------
1 | const FixedBox = {
2 | type: 'FixedBox',
3 | name: '悬浮框',
4 | tag: 'div',
5 | style: {
6 | width: '100px',
7 | height: '100px',
8 | backgroundColor: '#fff',
9 | border: '1px solid #EE771F',
10 | margin: '0 auto',
11 | position: 'fixed'
12 | },
13 | child: [],
14 | };
15 | export default FixedBox;
16 |
--------------------------------------------------------------------------------
/src/ingredients/elements/Button.ts:
--------------------------------------------------------------------------------
1 | const Button = {
2 | type: 'Button',
3 | name: '按钮',
4 | tag: 'button',
5 | text: '按钮',
6 | style: {
7 | minWidth: '100px',
8 | height: '40px',
9 | backgroundColor: '#3388FF',
10 | color: '#fff',
11 | borderRadius: '3px',
12 | padding: '0 5px'
13 | },
14 | edit: {},
15 | };
16 | export default Button;
17 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/BaseBox.ts:
--------------------------------------------------------------------------------
1 | const BaseBox = {
2 | type: 'BaseBox',
3 | name: '容器',
4 | tag: 'div',
5 | dragDots: ['left', 'right', 'bottom'],
6 | style: {
7 | width: '100%',
8 | height: '200px',
9 | backgroundColor: '#EDEDED',
10 | margin: '0 auto',
11 | overflow: 'hidden'
12 | },
13 | child: [],
14 | };
15 | export default BaseBox;
16 |
--------------------------------------------------------------------------------
/src/ingredients/elements/Img.ts:
--------------------------------------------------------------------------------
1 | const Img = {
2 | type: 'Img',
3 | name: '图片',
4 | tag: 'img',
5 | src: 'https://resource.smartisan.com/resource/fdc4370d1ce14a67fadc35d74209ac0f.jpg?x-oss-process=image/resize,w_600/format,webp',
6 | dragDots: ['right', 'bottom'],
7 | stopAdd: true,
8 | style: {
9 | width: '100%',
10 | },
11 | edit: {},
12 | };
13 | export default Img;
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # production
7 | build/
8 | dist/
9 | DIST/
10 | tmp/
11 | lib/
12 |
13 | # misc
14 | .idea/
15 | .happypack
16 | .DS_Store
17 | *.swp
18 | *.dia~
19 | .ice
20 | .vscode
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 | index.module.scss.d.ts
26 | yarn.lock
27 |
--------------------------------------------------------------------------------
/src/ingredients/elements/Circle.ts:
--------------------------------------------------------------------------------
1 | const Circle = {
2 | type: 'Circle',
3 | name: '圆形',
4 | tag: 'div',
5 | stopBefore: true,
6 | stopAfter: true,
7 | equalRatio: true,
8 | dragDots: ['right', 'bottom'],
9 | style: {
10 | width: '50px',
11 | height: '50px',
12 | backgroundColor: '#ccc',
13 | borderRadius: '100%',
14 | },
15 | child: [],
16 | edit: {},
17 | };
18 | export default Circle;
19 |
--------------------------------------------------------------------------------
/src/ingredients/elements/Input.ts:
--------------------------------------------------------------------------------
1 | const Input = {
2 | type: 'Input',
3 | name: '输入框',
4 | tag: 'input',
5 | placeholder: 'please input...',
6 | style: {
7 | minWidth: '100px',
8 | height: '40px',
9 | lineHeight: '40px',
10 | border: '1px solid #3388FF',
11 | borderRadius: '3px',
12 | padding: '0 5px',
13 | fontSize: '14px',
14 | color: '#666'
15 | },
16 | edit: {},
17 | };
18 | export default Input;
19 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/CeilingBox.ts:
--------------------------------------------------------------------------------
1 | const CeilingBox = {
2 | type: 'CeilingBox',
3 | name: '吸顶栏',
4 | tag: 'div',
5 | dragDots: ['bottom'],
6 | style: {
7 | width: '100%',
8 | height: '80px',
9 | backgroundColor: '#fff',
10 | border: '1px solid #EE771F',
11 | margin: '0 auto',
12 | position: 'fixed',
13 | top: '0px',
14 | left: '0px',
15 | zIndex: '9'
16 | },
17 | child: [],
18 | };
19 | export default CeilingBox;
20 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | cook-web
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/routes.ts:
--------------------------------------------------------------------------------
1 | import { IRouterConfig, lazy } from 'ice';
2 |
3 | const Home = lazy(() => import('@/pages/Home'));
4 | const Preview = lazy(() => import('@/pages/Preview'));
5 |
6 | const routerConfig: IRouterConfig[] = [
7 | {
8 | path: '/',
9 | component: Home,
10 | exact: true,
11 | pageConfig: {
12 | title: 'cooking'
13 | }
14 | },
15 | {
16 | path: '/preview/:pid',
17 | component: Preview,
18 | exact: true,
19 | // pageConfig: {
20 | // title: '预览'
21 | // }
22 | },
23 |
24 | ];
25 |
26 | export default routerConfig;
27 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/index.ts:
--------------------------------------------------------------------------------
1 | // const modules = import.meta.globEager("./*.ts");
2 |
3 | // const Layouts: DomItem[] = [];
4 | // for (let m in modules) {
5 | // Layouts.push(modules[m].default);
6 | // }
7 | // export default Layouts;
8 | import BaseBox from "./BaseBox";
9 | import LeftBox from "./LeftBox";
10 | import RightBox from "./RightBox";
11 | import FixedBox from "./FixedBox";
12 | import CeilingBox from "./CeilingBox";
13 | import PageTpl1 from "./PageTpl1";
14 | import PageTpl2 from "./PageTpl2";
15 |
16 | export default [BaseBox,LeftBox,RightBox,FixedBox,CeilingBox,PageTpl1,PageTpl2];
17 |
--------------------------------------------------------------------------------
/src/pages/Preview/index.tsx:
--------------------------------------------------------------------------------
1 | import styles from './index.module.less';
2 | import { useParams } from 'ice';
3 | import { useMount, useTitle } from 'ahooks';
4 | import cookEngine from '@/ingredients/cookEngine'
5 | import STATE from '@/db/state';
6 |
7 | interface pageParam {
8 | pid: string
9 | }
10 | const Preview = (props) => {
11 | const params: pageParam = useParams();
12 | const page = STATE.DOM_DATA.pages[params.pid];
13 | useTitle('预览-' + page.title);
14 | useMount(() => {
15 | cookEngine.preview(page, document.querySelector('#CookPreview')!)
16 | })
17 | return
;
18 | };
19 |
20 | export default Preview;
21 |
--------------------------------------------------------------------------------
/src/ingredients/export/htmlTemplate.ts:
--------------------------------------------------------------------------------
1 | export default (page, htmlStr) => {
2 | return `
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ${page.title}
16 |
17 |
18 |
19 | ${htmlStr}
20 |
21 |
22 |
23 |
24 | `
25 | }
26 |
--------------------------------------------------------------------------------
/src/db/db_config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | dbName: "cookEdit", // *数据库名称
3 | version: 1, // 数据库版本号(默认为当前时间戳)
4 | tables: [ // *数据库的表,即ObjectStore
5 | {
6 | tableName: "dom_sate", // *表名
7 | option: { keyPath: "id" }, // 表配置,即ObjectStore配置,此处指明主键为id
8 | indexs: [ // 数据库索引(建议加上索引)
9 | {
10 | key: "id", // *索引名
11 | option: { // 索引配置,此处表示该字段不允许重复
12 | unique: true
13 | }
14 | },
15 | {
16 | key: "page_use"
17 | },
18 | {
19 | key: "dom_tree"
20 | }
21 | ]
22 | },
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/pages/Home/index.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: weijundong
3 | * @Date: 2022-04-19 15:12:59
4 | * @LastEditors: weijundong
5 | * @LastEditTime: 2022-05-27 09:33:19
6 | * @FilePath: \cook-web\src\pages\Home\index.tsx
7 | */
8 | import { useEventEmitter } from 'ahooks';
9 |
10 | import PageBoard from '@/components/PageBoard';
11 |
12 | import RulerLine from '@/components/RulerLine';
13 |
14 | import ToolBarBox from '@/components/ToolBarBox';
15 |
16 | import EditPage from '@/components/EditPage';
17 |
18 | const Home = () => {
19 | const $switchPage = useEventEmitter();
20 | return
21 | {/* 页面管理 */}
22 |
23 | {/* 页面编辑容器 */}
24 |
25 | {/* 工具栏 */}
26 |
27 | {/* 辅助线 */}
28 |
29 |
30 |
;
31 | };
32 |
33 | export default Home;
34 |
--------------------------------------------------------------------------------
/src/app.tsx:
--------------------------------------------------------------------------------
1 | import { runApp, IAppConfig } from 'ice';
2 | import { Spin } from 'antd';
3 | import connectDB from './db/connect'
4 | import useDB from './db/use'
5 | import STATE from './db/state'
6 |
7 | import 'antd/dist/antd.css';
8 | import '@/assets/global.css';
9 |
10 | const appConfig: IAppConfig = {
11 | app: {
12 | rootId: 'ice-container',
13 | },
14 | router: {
15 | type: 'hash',
16 | basename: '/',
17 | fallback: ,
18 | modifyRoutes: (routes) => {
19 | return routes;
20 | }
21 | }
22 | };
23 |
24 | connectDB.then(async (mydb) => {
25 | STATE.MyDB = mydb;
26 | const Res = await useDB.myStore() as DBStore;
27 | if (!Res) {
28 | await useDB.setStore();
29 | } else {
30 | STATE.PAGE_USE = Res.page_use;
31 | STATE.DOM_DATA = Res.dom_tree;
32 | STATE.CUR_PAGE = STATE.DOM_DATA.pages[STATE.PAGE_USE];
33 | }
34 | runApp(appConfig);
35 | })
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "buildOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": ".",
6 | "outDir": "build",
7 | "module": "esnext",
8 | "target": "es6",
9 | "jsx": "react-jsx",
10 | "moduleResolution": "node",
11 | "allowSyntheticDefaultImports": true,
12 | "lib": ["es6", "dom"],
13 | "sourceMap": true,
14 | "allowJs": true,
15 | "rootDir": "./",
16 | "forceConsistentCasingInFileNames": true,
17 | "noImplicitReturns": true,
18 | "noImplicitThis": true,
19 | "noImplicitAny": false,
20 | "importHelpers": true,
21 | "strictNullChecks": true,
22 | "suppressImplicitAnyIndexErrors": true,
23 | "noUnusedLocals": true,
24 | "skipLibCheck": true,
25 | "types": ["node","vite/client","react-ace"],
26 | "paths": {
27 | "@/*": ["./src/*"],
28 | "ice": [".ice"]
29 | }
30 | },
31 | "include": ["src", ".ice"],
32 | "exclude": ["node_modules", "build", "public"]
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/RulerLine/index.tsx:
--------------------------------------------------------------------------------
1 | import styles from './index.module.less';
2 | import { useSize } from 'ahooks';
3 | // 辅助线
4 | const RulerLine = function () {
5 | const Screen = useSize(document.querySelector('html'));
6 | const Line = function (props) {
7 | const scaleEls: JSX.Element[] = [];
8 | for (var i = 0; i < props.size; i++) {
9 | if (i % 10 == 0 && i != 0) {
10 | let styleObj = props.direction == 'x' ? { left: (i - 1) + 'px', height: i % 50 == 0 ? '100%' : '50%' } : { top: (i - 1) + 'px', width: i % 50 == 0 ? '100%' : '50%' };
11 | scaleEls.push({i % 50 == 0 ? {i} : ''})
12 | }
13 | }
14 | return {scaleEls}
;
15 | }
16 | return
17 | {/* 辅助线-X轴 */}
18 |
19 | {/* 辅助线-Y轴 */}
20 |
21 |
22 | }
23 |
24 | export default RulerLine;
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # cook-web(v1.0)
3 |
4 | > 一个简易的拖拽式网页构建平台
5 |
6 | ## 使用
7 |
8 | ```bash
9 | # 安装依赖
10 | $ yarn
11 |
12 | # 启动服务
13 | $ npm start # visit http://localhost:3333
14 | ```
15 |
16 | [More docs](https://ice.work/docs/guide/about).
17 |
18 | ## 目录
19 |
20 | ```md
21 | ├── build/ # 构建产物
22 | ├── mock/ # 本地模拟数据
23 | │ ├── index.[j,t]s
24 | ├── public/
25 | │ ├── index.html # 应用入口 HTML
26 | │ └── favicon.png # Favicon
27 | ├── src/ # 源码路径
28 | │ ├── components/ # 自定义业务组件
29 | │ │ └── Guide/
30 | │ │ ├── index.[j,t]sx
31 | │ │ └── index.module.scss
32 | │ ├── pages/ # 页面
33 | │ │ └── index.tsx/
34 | │ ├── global.scss # 全局样式
35 | │ └── app.[j,t]s[x] # 应用入口脚本
36 | ├── README.md
37 | ├── package.json
38 | ├── .editorconfig
39 | ├── .eslintignore
40 | ├── .eslintrc.[j,t]s
41 | ├── .gitignore
42 | ├── .stylelintignore
43 | ├── .stylelintrc.[j,t]s
44 | ├── .gitignore
45 | └── [j,t]sconfig.json
46 | ```
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cook-web",
3 | "version": "0.1.0",
4 | "description": "前端页快速搭建工具",
5 | "dependencies": {
6 | "ace-builds": "^1.5.2",
7 | "ahooks": "^3.3.8",
8 | "antd": "^4.20.5",
9 | "idb-js": "^1.3.1",
10 | "jszip": "^3.9.1",
11 | "react": "^17.0.2",
12 | "react-ace": "^10.1.0",
13 | "react-dom": "^17.0.2"
14 | },
15 | "devDependencies": {
16 | "@iceworks/spec": "^1.0.0",
17 | "@types/react": "^17.0.2",
18 | "@types/react-dom": "^17.0.2",
19 | "eslint": "^7.30.0",
20 | "ice.js": "^2.0.0",
21 | "stylelint": "^13.7.2"
22 | },
23 | "scripts": {
24 | "start": "icejs start",
25 | "build": "icejs build",
26 | "lint": "npm run eslint && npm run stylelint",
27 | "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./",
28 | "eslint:fix": "npm run eslint -- --fix",
29 | "stylelint": "stylelint \"**/*.{css,scss,less}\""
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "https://github.com/nullno/cook-web.git"
34 | },
35 | "private": true,
36 | "originTemplate": "@alifd/scaffold-simple"
37 | }
38 |
--------------------------------------------------------------------------------
/src/assets/global.css:
--------------------------------------------------------------------------------
1 | @import './normalize.css';
2 |
3 | .ant-input[type='color'] {
4 | height: 21px;
5 | }
6 | ::-webkit-scrollbar {
7 | width: 3px;
8 | height: 3px;
9 | }
10 | ::-webkit-scrollbar-track {
11 | width: 3px;
12 | background-color: rgba(0, 0, 0, 0);
13 | -webkit-border-radius: 2em;
14 | -moz-border-radius: 5px;
15 | border-radius: 2em;
16 | }
17 | ::-webkit-scrollbar-thumb {
18 | background-color: rgba(0, 0, 0, 0.3);
19 | background-clip: padding-box;
20 | min-height: 20px;
21 | -webkit-border-radius: 2em;
22 | -moz-border-radius: 2em;
23 | border-radius: 2em;
24 | }
25 | ::-webkit-scrollbar-thumb:hover {
26 | background-color: rgba(0, 0, 0, 1);
27 | }
28 | .loading-ready {
29 | width: 50px;
30 | height: 50px;
31 | position: fixed;
32 | top: 50%;
33 | left: 50%;
34 | margin-left: -25px;
35 | margin-top: -25px;
36 | }
37 |
38 | .cookApp {
39 | width: 100%;
40 | height: 100%;
41 | background-color: #fff;
42 | position: fixed;
43 | }
44 |
45 | @keyframes spinner {
46 | from {
47 | transform: rotate(0deg);
48 | }
49 |
50 | to {
51 | transform: rotate(360deg);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/RulerLine/index.module.less:
--------------------------------------------------------------------------------
1 | .ruler-Line {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | z-index: 9;
6 | opacity: 0.5;
7 | user-select: none;
8 | i {
9 | position: absolute;
10 | box-sizing: border-box;
11 | left: 0;
12 | top: 0;
13 | font-style: normal;
14 | font-size: 8px;
15 | background-color: #666;
16 | color: #e94337;
17 | span {
18 | position: absolute;
19 | top: 5px;
20 | left: 0;
21 | transform: scale(0.8);
22 | }
23 | span:hover {
24 | background-color: #e94337;
25 | padding: 0 5px;
26 | border-radius: 20px;
27 | color: #fff;
28 | transform: scale(1.5);
29 | cursor: crosshair;
30 | transition: all 0.1s;
31 | }
32 | }
33 | &.rulerX {
34 | width: 100%;
35 | height: 15px;
36 | i {
37 | width: 1px;
38 | height: 100%;
39 | }
40 | }
41 | &.rulerY {
42 | width: 15px;
43 | height: 100%;
44 | i {
45 | width: 100%;
46 | height: 1px;
47 | span {
48 | transform: scale(0.8) rotate(90deg);
49 | }
50 | span:hover {
51 | transform: scale(1.5) rotate(90deg);
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/db/state.ts:
--------------------------------------------------------------------------------
1 | interface State {
2 | DOM_DATA: DomTree; // 页面数据
3 | DOM_MAP: { [propname: string]: DomItem }; // 扁平树
4 | CUR_EL: HTMLElement | null; // 当前编辑元素
5 | CUR_KEY: string; // 当前编辑key
6 | DRAG_EL: DomItem | null; // 拖动元素
7 | PAGE_USE: number;// 当前编辑页面索引
8 | CUR_PAGE: PageItem | null; // 当前页面数据
9 | MyRenderEngine: any; // 渲染引擎实例
10 | Panzoom: any; // 拖动缩放库实例
11 | MyDB: any; // 数据库
12 | }
13 |
14 |
15 | const State: State = {
16 | DOM_DATA: {
17 | appName: '测试应用',
18 | plugins: [],
19 | pages: [
20 | {
21 | type: 'page',
22 | id: 'page1',
23 | title: '测试标题',
24 | style: {
25 | width: '1200px',
26 | minHeight: '500px',
27 | border: '1px dashed red'
28 | },
29 | child: []
30 | },
31 | {
32 | type: 'page',
33 | id: 'page2',
34 | title: '测试标题',
35 | style: {
36 | width: '1200px',
37 | minHeight: '500px',
38 | border: '1px dashed red'
39 | },
40 | child: []
41 | }
42 | ],
43 | },
44 | DOM_MAP: {},
45 | CUR_EL: null,
46 | CUR_KEY: '',
47 | DRAG_EL: null,
48 | PAGE_USE: 0,
49 | CUR_PAGE: null,
50 | MyRenderEngine: null,
51 | Panzoom: null,
52 | MyDB: null,
53 | }
54 |
55 | State.CUR_PAGE = State.DOM_DATA.pages[State.PAGE_USE];
56 | export default State
57 |
--------------------------------------------------------------------------------
/src/utils/tool.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 对象深拷贝
3 | * @param {*} obj 目标对象
4 | * @returns
5 | */
6 | export const deepCopyObj = function (obj) {
7 | var result = Array.isArray(obj) ? [] : {};
8 | for (var key in obj) {
9 | if (obj.hasOwnProperty(key)) {
10 | if (typeof obj[key] === "object" && obj[key] !== null) {
11 | result[key] = deepCopyObj(obj[key]);
12 | } else {
13 | result[key] = obj[key];
14 | }
15 | }
16 | }
17 | return result;
18 | };
19 | /**
20 | * 获取屏幕尺寸
21 | */
22 | export const screen = {
23 | width: function() {
24 | return (
25 | window.innerWidth ||
26 | document.documentElement.clientWidth ||
27 | document.body.clientWidth
28 | );
29 | },
30 | height: function() {
31 | return (
32 | window.innerHeight ||
33 | document.documentElement.clientHeight ||
34 | document.body.clientHeight
35 | );
36 | },
37 | scrollTop: function() {
38 | return document.documentElement.scrollTop || document.body.scrollTop;
39 | },
40 | scrollHeight: function() {
41 | return document.documentElement.scrollHeight || document.body.scrollHeight;
42 | },
43 | resize: function(callback) {
44 | window.onresize = function() {
45 | callback({
46 | width: screen.width(),
47 | height: screen.height(),
48 | });
49 | };
50 | window.onresize();
51 | },
52 | };
53 |
--------------------------------------------------------------------------------
/src/db/use.ts:
--------------------------------------------------------------------------------
1 | import STATE from '@/db/state';
2 |
3 | export default {
4 | tableName: 'dom_sate',
5 | fixedId: 'cooker',
6 | myStore() {
7 | return new Promise((resolve, reject) => {
8 | STATE.MyDB.query_by_primaryKey({
9 | tableName: this.tableName,
10 | target: this.fixedId,
11 | success: (res) => {
12 | res && (res.dom_tree = JSON.parse(res.dom_tree));
13 | resolve(res)
14 | },
15 | failed(err) {
16 | reject(null)
17 | }
18 | })
19 | })
20 | },
21 | setStore() {
22 | return new Promise((resolve, reject) => {
23 | STATE.MyDB.insert({
24 | tableName: this.tableName,
25 | data: {
26 | id: this.fixedId,
27 | page_use: STATE.PAGE_USE,
28 | dom_tree: JSON.stringify(STATE.DOM_DATA)
29 | },
30 | success: (res) => {
31 | resolve(true)
32 | },
33 | failed(err) {
34 | reject(false)
35 | }
36 | })
37 | });
38 | },
39 | updateStore() {
40 | return new Promise((resolve, reject) => {
41 | STATE.MyDB.update_by_primaryKey({
42 | tableName: this.tableName,
43 | target: this.fixedId,
44 | handle(val) {
45 | val.page_use = STATE.PAGE_USE
46 | val.dom_tree = JSON.stringify(STATE.DOM_DATA);
47 | },
48 | success: res => {
49 | resolve(res);
50 | }
51 | });
52 | });
53 | },
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/ingredients/export/index.ts:
--------------------------------------------------------------------------------
1 | import { message } from 'antd';
2 | import resetCssTemplate from './resetCssTemplate'
3 | import htmlTemplate from './htmlTemplate';
4 | import cssTemplate from './cssTemplate';
5 | import jsTemplate from './jsTemplate';
6 |
7 | export default function (STATE, CoreHandler) {
8 | const loadHide = message.loading('正在导出源码..', 0);
9 | // @ts-ignore
10 | const zip = new JSZip();
11 | const cookerFiles: any = zip.folder(".cooker");
12 | const imgFiles: any = zip.folder("images");
13 | const cssFiles = zip.folder("css");
14 | const jsFiles = zip.folder("js");
15 | cookerFiles.file("cooker." + Date.now() + ".json", JSON.stringify(STATE.DOM_DATA.pages));
16 | cssFiles.file("normalize.css", resetCssTemplate());
17 | STATE.DOM_DATA.pages.forEach(page => {
18 | const exportData = CoreHandler._exportPageHandler(page);
19 | // console.log(exportData);
20 | // img file
21 | for (let key in exportData.img) {
22 | const imgData = exportData.img[key];
23 | imgFiles.file(key, imgData.substring(imgData.indexOf(',') + 1), { base64: true });
24 | }
25 | // js file
26 | jsFiles.file(page.id + '.js', jsTemplate(page, exportData.js));
27 | // css file
28 | cssFiles.file(page.id + '.css', cssTemplate(page, exportData.css));
29 | // html file
30 | zip.file(page.id + '.html', htmlTemplate(page, exportData.html));
31 |
32 | })
33 | zip.generateAsync({ type: "blob" })
34 | .then((content) => {
35 | // @ts-ignore see FileSaver.js
36 | saveAs(content, 'cook-web.zip');
37 | loadHide();
38 | message.success('导出成功!');
39 |
40 | }).catch((err) => {
41 | loadHide();
42 | message.error('导出失败:【' + err + '】');
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/src/utils/idb.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: weijundong wx_dda6ed3e7d4049b18953aab41af2bcd1@git.code.tencent.com
3 | * @Date: 2022-05-20 10:05:32
4 | * @LastEditors: weijundong wx_dda6ed3e7d4049b18953aab41af2bcd1@git.code.tencent.com
5 | * @LastEditTime: 2022-05-26 15:47:31
6 | * @FilePath: \ty-cooking\src\utils\idb.js
7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
8 | */
9 | import { useState, useEffect } from 'react';
10 | const dbp = new Promise((resolve, reject) => {
11 | const openreq = window.indexedDB.open('use-idb', 1);
12 | openreq.onerror = () => reject(openreq.error);
13 | openreq.onsuccess = () => resolve(openreq.result);
14 | openreq.onupgradeneeded = () => openreq.result.createObjectStore('idb');
15 | });
16 |
17 | const call = async (type, method, ...args) => {
18 | const db = await dbp;
19 | const transaction = db.transaction('idb', type);
20 | const store = transaction.objectStore('idb');
21 |
22 | return new Promise((resolve, reject) => {
23 | const req = store[method](...args);
24 | transaction.oncomplete = () => resolve(req);
25 | transaction.onabort = transaction.onerror = () => reject(transaction.error);
26 | });
27 | };
28 |
29 | const get = async (key) => (await call('readonly', 'get', key)).result;
30 | const set = (key, value) =>
31 | value === undefined ? call('readwrite', 'delete', key) : call('readwrite', 'put', value, key);
32 | const useIdb = (key, initialState) => {
33 | const [item, setItem] = useState(initialState);
34 | useEffect(() => {
35 | get(key).then((value) => value === undefined || setItem(value));
36 | }, [key]);
37 |
38 | return [
39 | item,
40 | (value) => {
41 | setItem(value);
42 | return set(key, value);
43 | },
44 | ];
45 | };
46 | export default useIdb;
47 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/PageTpl2.ts:
--------------------------------------------------------------------------------
1 | const PageBox1 = {
2 | type: 'PageBox1',
3 | name: '模板2',
4 | tag: 'div',
5 | stopBefore: true,
6 | stopAfter: true,
7 | style: {
8 | width: '100%',
9 | height: 'auto',
10 | backgroundColor: '#F6F6F6',
11 | overflow: 'hidden'
12 | },
13 | child: [
14 | {
15 | type: 'PageBox1_head',
16 | name: '模板2-头部',
17 | tag: 'div',
18 | stopBefore: true,
19 | style: {
20 | width: '100%',
21 | height: '50px',
22 | backgroundColor: '#fff',
23 | margin: '0 auto',
24 | },
25 | child: [],
26 | },
27 | {
28 | type: 'PageBox1_banner',
29 | name: '模板2-banner',
30 | tag: 'div',
31 | style: {
32 | width: '100%',
33 | minHeight: '300px',
34 | backgroundColor: '#EDEDED',
35 | margin: '10px auto',
36 | overflow: 'hidden'
37 | },
38 | child: [
39 | {
40 | type: 'Img',
41 | name: '图片',
42 | tag: 'img',
43 | src: 'https://static.smartisanos.cn/delta/img/01@2x.4d19c6c.jpg?x-oss-process=image/format,webp',
44 | style: {
45 | width: '100%',
46 | },
47 | }
48 | ],
49 | },
50 | {
51 | type: 'PageBox1_main',
52 | name: '模板2-主体',
53 | tag: 'div',
54 | style: {
55 | width: '1200px',
56 | minHeight: '500px',
57 | backgroundColor: '#fff',
58 | margin: '10px auto',
59 | },
60 | child: [],
61 | },
62 | {
63 | type: 'PageBox1_footer',
64 | name: '模板2-底部',
65 | tag: 'div',
66 | stopAfter: true,
67 | style: {
68 | width: '100%',
69 | height: '200px',
70 | backgroundColor: '#fff',
71 | },
72 | child: [],
73 | },
74 | ],
75 | };
76 | export default PageBox1;
77 |
--------------------------------------------------------------------------------
/src/components/EditPage/ActionPanel.tsx:
--------------------------------------------------------------------------------
1 | import styles from './index.module.less';
2 | import STATE from '@/db/state';
3 | import useDB from '@/db/use';
4 | import { useRef, useEffect } from 'react';
5 |
6 | import { Tag, Button, message } from 'antd';
7 | import { EnvironmentOutlined } from '@ant-design/icons';
8 | // ace代码编辑器
9 | import AceEditor from 'react-ace';
10 |
11 |
12 | const ActionPanel = (props) => {
13 | const aceCodeEditRef = useRef(null);
14 | const curWidget: DomItem = STATE.DOM_MAP[STATE.CUR_KEY];
15 |
16 | // useEffect(()=>{})
17 | let aceEditorValue = '';
18 |
19 | const saveEventCode = () => {
20 | if (curWidget.events === aceEditorValue) return;
21 | curWidget.events = aceEditorValue;
22 | useDB.updateStore();
23 | message.success('保存成功!');
24 | }
25 | return
26 |
27 | } color="#f50">{STATE.CUR_KEY}
28 |
29 |
30 |
31 |
32 |
aceEditorValue = value}
36 | value={curWidget.events || `var ${curWidget.className} = document.querySelector('.${curWidget.className}');`}
37 | name="UNIQUE_ID_JS_CODE"
38 | showGutter={true}
39 | fontSize={16}
40 | editorProps={{ $blockScrolling: true }}
41 | setOptions={{
42 | useWorker: false,
43 | enableBasicAutocompletion: true,
44 | enableLiveAutocompletion: true,
45 | }}
46 | style={{ width: '100%', height: '100%' }}
47 | />
48 |
49 |
50 |
51 | }
52 | export default ActionPanel;
53 |
--------------------------------------------------------------------------------
/src/assets/img/wg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: weijundong wx_dda6ed3e7d4049b18953aab41af2bcd1@git.code.tencent.com
3 | * @Date: 2022-05-20 10:05:32
4 | * @LastEditors: weijundong wx_dda6ed3e7d4049b18953aab41af2bcd1@git.code.tencent.com
5 | * @LastEditTime: 2022-06-01 11:23:37
6 | * @FilePath: \ty-cooking\src\typings.d.ts
7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
8 | */
9 | declare module 'react-ace';
10 |
11 | declare module '*.module.less' {
12 | const classes: { [key: string]: string };
13 | export default classes;
14 | }
15 | declare module '*.jpg' {
16 | const jpg: string;
17 | export default jpg;
18 | }
19 | declare module '*.png' {
20 | const png: string;
21 | export default png;
22 | }
23 |
24 | interface DBStore {
25 | id: string;
26 | page_use: number;
27 | dom_tree: any;
28 | }
29 |
30 | interface Screen {
31 | width: number;
32 | height: number;
33 | }
34 |
35 | interface ToolTab {
36 | name: string;
37 | icon: string;
38 | event: any;
39 | }
40 |
41 | interface DomItem {
42 | type: string;
43 | tag: 'div' | 'ul' | 'li' | 'button' | 'span' | 'image' | 'i';
44 | name: string;
45 | style: Object;
46 | className?: string;
47 | child?: Array;
48 | text?: string;
49 | placeholder?: string;
50 | hide?: boolean;
51 | events?: any;
52 | plugins?: any;
53 | edit?: any;
54 | src?: string;
55 | stopBefore?: boolean;
56 | stopAfter?: boolean;
57 | stopAdd?: boolean;
58 | dragDots?: Array<'left' | 'right' | 'top' | 'bottom'>;
59 | }
60 |
61 | interface PageItem {
62 | type: 'page';
63 | id: string;
64 | title: string;
65 | name?: string;
66 | style?: Object;
67 | child?: Array;
68 | hide?: boolean;
69 | events?: any;
70 | stopBefore?: boolean;
71 | stopAfter?: boolean;
72 | stopAdd?: boolean;
73 | dragDots?: Array<'left' | 'right' | 'top' | 'bottom'>
74 | }
75 | interface DomTree {
76 | appName: string;
77 | plugins: Array;
78 | pages: Array
79 | }
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/components/EditPage/UploadImg.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Upload, message } from 'antd';
3 | import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
4 | import type { UploadChangeParam } from 'antd/es/upload';
5 | import type { RcFile, UploadFile, UploadProps } from 'antd/es/upload/interface';
6 | const getBase64 = (img: RcFile, callback: (url: string) => void) => {
7 | const reader = new FileReader();
8 | reader.addEventListener('load', () => callback(reader.result as string));
9 | reader.readAsDataURL(img);
10 | };
11 |
12 | const beforeUpload = (file: RcFile) => {
13 | const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
14 | if (!isJpgOrPng) {
15 | message.error('只能选择 JPG/PNG 图片!');
16 | }
17 | const isLt2M = file.size / 1024 / 1024 < 2;
18 | if (!isLt2M) {
19 | message.error('选择图片须小于2MB!');
20 | }
21 | return isJpgOrPng && isLt2M;
22 | };
23 |
24 | const UploadImg = (props) => {
25 | const [loading, setLoading] = useState(false);
26 | const [imageUrl, setImageUrl] = useState(props.defaultValue);
27 |
28 | const handleChange: UploadProps['onChange'] = (info: UploadChangeParam) => {
29 | if (info.file.status === 'uploading') {
30 | setLoading(true);
31 | return;
32 | }
33 | if (info.file) {
34 | // Get this url from response in real world.
35 | getBase64(info.file.originFileObj as RcFile, url => {
36 | setLoading(false);
37 | setImageUrl(url);
38 | props.onChange && props.onChange(url)
39 | });
40 | }
41 | };
42 |
43 | const uploadButton = (
44 |
45 | {loading ?
:
}
46 |
本地图片
47 |
48 | );
49 |
50 | return (
51 |
52 |
61 | {imageUrl ?
: uploadButton}
62 |
63 | );
64 | };
65 |
66 | export default UploadImg;
67 |
--------------------------------------------------------------------------------
/src/components/EditPage/ZoomPlugin.tsx:
--------------------------------------------------------------------------------
1 | interface ViewState {
2 | disableMove: boolean;
3 | }
4 |
5 | import styles from './index.module.less';
6 | import { useMount, useSetState, useFullscreen } from 'ahooks'
7 | import STATE from '@/db/state';
8 |
9 | let myPanzoom: any;
10 |
11 | const ZoomPlugin = (props) => {
12 |
13 | const [viewState, setViewState] = useSetState({
14 | disableMove: false,
15 | });
16 | const [isFullscreen, { toggleFullscreen }] = useFullscreen(() => document.body);
17 |
18 | useMount(() => {
19 | const EditPageEl: HTMLDivElement = document.querySelector('#' + props.target)!;
20 | // @ts-ignore
21 | myPanzoom = Panzoom(EditPageEl, { startScale: 0.9, disablePan: viewState.disableMove });
22 | // @ts-ignore
23 | EditPageEl.addEventListener('wheel', (event: any) => {
24 | if (!event.ctrlKey) return;
25 | myPanzoom.zoomWithWheel(event)
26 | })
27 | STATE.Panzoom = myPanzoom;
28 | })
29 | const setZoom = (f: string) => {
30 | if (f === 'add') {
31 | myPanzoom && myPanzoom.zoomIn();
32 | }
33 | if (f === 'minus') {
34 | myPanzoom && myPanzoom.zoomOut();
35 | }
36 | }
37 |
38 | const openMove = () => {
39 | const { disablePan } = myPanzoom.getOptions();
40 | setViewState({
41 | disableMove: !disablePan
42 | })
43 | myPanzoom.setOptions({ disablePan: !disablePan })
44 | }
45 |
46 | return
47 |
48 |
49 | myPanzoom.reset()}>
50 | setZoom('add')}>
51 | setZoom('minus')}>
52 | {
53 | window.open(location.protocol + '//' + location.host +location.pathname+ '#/preview/' + STATE.PAGE_USE);
54 | }}>
55 | {
56 | STATE.MyRenderEngine.download();
57 | }}>
58 |
59 |
60 | }
61 | export default ZoomPlugin;
62 |
--------------------------------------------------------------------------------
/src/ingredients/layouts/PageTpl1.ts:
--------------------------------------------------------------------------------
1 | const MangerBox = {
2 | type: 'MangerBox',
3 | name: '模板1',
4 | tag: 'div',
5 | stopBefore: true,
6 | stopAfter: true,
7 | stopAdd: true,
8 | style: {
9 | width: '100%',
10 | minWidth: '1200px',
11 | minHeight: '500px',
12 | height: '100%',
13 | display: 'flex',
14 | flexDirection: 'column',
15 | position: 'fixed',
16 | backgroundColor: '#fff',
17 |
18 | },
19 | child: [
20 | {
21 | type: 'MangerBox_head',
22 | name: '模板1-头部',
23 | tag: 'div',
24 | stopBefore: true,
25 | stopAfter: true,
26 | dragDots: ['bottom'],
27 | style: {
28 | width: '100%',
29 | height: '60px',
30 | maxHeight: '100px',
31 | overflow: 'hidden',
32 | backgroundColor: '#fff',
33 | padding: '10px'
34 | },
35 | child: [],
36 | },
37 | {
38 | type: 'MangerBox_main',
39 | name: '模板1-主体',
40 | tag: 'div',
41 | stopBefore: true,
42 | stopAfter: true,
43 | stopAdd: true,
44 | style: {
45 | width: '100%',
46 | height: '100%',
47 | flex: 1,
48 | overflow: 'hidden',
49 | display: 'flex',
50 | backgroundColor: '#F6F6F6',
51 | },
52 | child: [
53 | {
54 | type: 'MangerBox_left',
55 | name: '模板1-侧边栏',
56 | tag: 'div',
57 | stopBefore: true,
58 | stopAfter: true,
59 | dragDots: ['right'],
60 | style: {
61 | width: '100px',
62 | height: '99%',
63 | overflow: 'auto',
64 | backgroundColor: '#fff',
65 | marginTop: '10px',
66 | padding: '10px'
67 | },
68 | child: [],
69 | },
70 | {
71 | type: 'MangerBox_right',
72 | name: '模板1-内容',
73 | tag: 'div',
74 | stopBefore: true,
75 | stopAfter: true,
76 | style: {
77 | flex: 1,
78 | width: '98%',
79 | height: '99%',
80 | overflow: 'auto',
81 | marginLeft: '10px',
82 | marginTop: '10px',
83 | backgroundColor: '#fff',
84 | padding: '10px'
85 | },
86 | child: [],
87 | }
88 | ],
89 |
90 | }
91 | ],
92 | };
93 |
94 | export default MangerBox;
95 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
1 | �PNG
2 |
3 |
IHDR � � ��g-
4 | fIDATx��klSe�_E��Cza�ʸ��0�2@�>x ^?(bl;ƌZ���& &��/^h)0.
5 | ��4E�@� �Զnc�v�<>o��ҵ��zN�����
�lky��{zΙ �hLv�-vM���q����q��M��c#?����鲺���8�� ���������=��o⏽�/X*i���� hX�4���67�͢�N��$�*�}������#�)k90N/
�)O����&[��?�g���]9�M�**�v�����d���?�a����t.%��0�*�6�����A�r� �����S$����kO�-<�zT�����Y��/�$�>X��`��2�M��_���߹Zn��
r
6 | ���\�8d��g7�*i� ���K����&���g���D`l�����Cf.�1���@-��N���������M��+Ĺ�Fʷ���$,,]Zl.�W �7��i�\�6!'y�-@��>��7�\������u��4L 5XW�]����^��w�~�<��_C��3C�iMf�+����֒���0�1�4J ����� 0�9-@���/R\��{xfm�"���W�,2gF+
+��jK&�M��y^S�Y*�*@��F�z�%���@@E9����44.GJ6�9Ԥ�K@���4�T�;�� �8O��=ovC�Rj����x`�CE��y����Ʉ&�i嬓W� 3QZ{n������upy����'�|���Ȓ�M�5� �����o���8g�G�z�����0_z���=���=��Ùu�7�\�y����C�������ܦ��F�x*���5��h?�� `�.�[�UT(�B�qZZ�Z�v��A���-G�M��tD.)�?����DD�.�Y�g�N��V�;� M�V�Mɞ�xZI-���~5 7�,��!pe�/�������!�e����(��~.�IfRM'D2�����>���wt�g�vi�i��*l.�-2Ź+4���w�C1)ܕ2��"�N�/�}�����H�zǤd������"Y�C��-J�t�����D
7 | �i��s���
�d�ݴ�~ȝ/(��&�ۂ4}�YZЖ��Hvq��6�
^~��T��<P�7HJj���9 %�p/���4A�S�sG�=�IiT���~Xў�����<�P4�IY��o��0
8 | ���<�*}�S��͎m�je�p'�æ4����"WL��3���� �����B/�;�r?lR����!���;�Y$�
9 | K�w���C���m�a�^���n�F뇾�k�`����3< �,q�C���q���<?��2_�WS�0:���i�,7C@���C�E�Q/�xR������W�f����嵍+
10 | �
����yI�L0�J�c��!ݥ�EW���$[�����f)]6�&ݔnR�n
11 | �t��&I����,J�Ē5��a;�%[��N&�4rl����3�����^ )������y������Od?�i����<�v��91��wm�3�D(���{S�������~BFz5�!��Z���:*�j���I�o��_��"��7�W�H~0�@�<3��>��G��_�U���f�C����x�e��/ꇕOe?�:���~�~��督�^σ�&(��A�+�_��٦�1�����j�O�|bf8��8���@��):�� r�{O@w�h���f��~PY� ���n��� ���l����+����ּHz�A�����!��f��Xncjd�ؐ!
12 | f�I���=�η�H.S<��\@���t��]lD���U���0�������������2������
�����d�0*V���U�$�p�Β7��D�CW����,nv����В��Y�������oӼ��?�%#��Ӧ�こ�hz�4�FsI�L����d�]��'krP�� ]��SW^��)�m}ѹ������f!kskz��:w8-Q��Q� �$�S[I��:��П~�������~����-��S�S�Q�t��x�$O�%^��&Z�@[j�r��������!����"�Ʋ�I�r�M��m}PM�?�3b��)�r������X��jV��a�V��,�W�][� ^�͝3/ivOG����|G�>���25����xI�n&�o�
�T̷����������w�+ ݪ]�X��rz��>�~K-O ��Μ�7dg�}12SX�m��-�t�{Ř[�
13 | l