├── src
├── shim.d.ts
├── app.scss
├── asset
│ └── font
│ │ ├── iconfont.eot
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ ├── iconfont.woff2
│ │ ├── iconfont.css
│ │ └── iconfont.svg
├── main.tsx
├── type.d.ts
├── packages
│ ├── style
│ │ ├── _variable.scss
│ │ ├── VisualEditorBlock.module.scss
│ │ └── VisualEditor.module.scss
│ ├── hook
│ │ ├── useUpdate.ts
│ │ └── useCallbackRef.ts
│ ├── utils
│ │ └── defer.ts
│ ├── plugin
│ │ ├── event.ts
│ │ ├── keyboard-code.ts
│ │ └── command.plugin.ts
│ ├── service
│ │ ├── dropdown
│ │ │ ├── $$dropdown.scss
│ │ │ └── $$dropdown.tsx
│ │ └── dialog
│ │ │ └── $$dialog.tsx
│ ├── visual.config.tsx
│ ├── EditorBlock.tsx
│ ├── editor.utils.tsx
│ ├── editor.command.tsx
│ └── VisualEditor.tsx
├── App.tsx
└── mock.json
├── doc
├── 右键菜单.png
├── 最终效果.png
├── 顶部操作栏.png
├── 容器区域组件基础渲染.png
├── 左侧菜单列表渲染.png
├── 画布容器中组件选中.png
└── 开发文档.md
├── public
└── img
│ └── default-img.jpg
├── index.html
├── .yarnrc
├── .gitignore
├── tsconfig.json
├── README.md
├── package.json
├── vite.config.ts
└── yarn.lock
/src/shim.d.ts:
--------------------------------------------------------------------------------
1 | // enable types for import.meta.* and file types
2 | import 'vite/env'
--------------------------------------------------------------------------------
/doc/右键菜单.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/右键菜单.png
--------------------------------------------------------------------------------
/doc/最终效果.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/最终效果.png
--------------------------------------------------------------------------------
/doc/顶部操作栏.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/顶部操作栏.png
--------------------------------------------------------------------------------
/doc/容器区域组件基础渲染.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/容器区域组件基础渲染.png
--------------------------------------------------------------------------------
/doc/左侧菜单列表渲染.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/左侧菜单列表渲染.png
--------------------------------------------------------------------------------
/doc/画布容器中组件选中.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/doc/画布容器中组件选中.png
--------------------------------------------------------------------------------
/src/app.scss:
--------------------------------------------------------------------------------
1 |
2 | html, body {
3 | height: 100%;
4 | width: 100%;
5 | margin: 0;
6 | padding: 0;
7 | }
--------------------------------------------------------------------------------
/public/img/default-img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/public/img/default-img.jpg
--------------------------------------------------------------------------------
/src/asset/font/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/src/asset/font/iconfont.eot
--------------------------------------------------------------------------------
/src/asset/font/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/src/asset/font/iconfont.ttf
--------------------------------------------------------------------------------
/src/asset/font/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/src/asset/font/iconfont.woff
--------------------------------------------------------------------------------
/src/asset/font/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lyios8859-1/react-app-editor-project/HEAD/src/asset/font/iconfont.woff2
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import App from './App';
3 | import 'antd/dist/antd.css';
4 |
5 | ReactDOM.render(
6 | ,
7 | document.querySelector('#app')
8 | )
9 |
--------------------------------------------------------------------------------
/src/type.d.ts:
--------------------------------------------------------------------------------
1 | declare type React = {}
2 |
3 | declare module '*.module.scss' {
4 | const classes: { readonly [key: string]: string };
5 | export default classes;
6 | }
7 | declare module '*.scss';
--------------------------------------------------------------------------------
/src/packages/style/_variable.scss:
--------------------------------------------------------------------------------
1 | $headSize: 60px;
2 | $menuSize: 275px;
3 | $operatorSize: 275px;
4 |
5 | $ibc: #dcdfe6;
6 | $ibl: #ebeef5;
7 | $itc: #314659;
8 | $icc: rgba(0, 0, 0, 0.45);
9 | $boxShadowColor: #f0f1f2;
10 | $primary: #409EFF;
11 |
12 | $gap: 20px;
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vite React Editor
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/packages/hook/useUpdate.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useMemo, useState } from "react";
2 |
3 | // export function useUpdate() {
4 | // const [count, setCount] = useState(0)
5 | // return useCallback(() => setCount(count + 1), [count])
6 | // }
7 | export function useUpdate () {
8 | const [count, setCount] = useState(0);
9 | return useMemo(() => ({froceUpdate: () => setCount(count + 1)}), [count]);
10 | }
11 |
--------------------------------------------------------------------------------
/src/packages/hook/useCallbackRef.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useRef } from "react";
2 |
3 | /**
4 | * 需要得到一个不变的函数引用,但是这个不变的函数执行的时候,执行的是传递的最新的函数
5 | * @param callback 传递最新的函数
6 | * @returns 返回不变的函数
7 | */
8 | export function useCallbackRef void>(callback: T): T {
9 | const refCallback = useRef(callback);
10 | refCallback.current = callback;
11 | return useCallback(((...args: any[]) => refCallback.current(...args)) as T, []);
12 | }
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | home "https://registry.yarnpkg.com"
2 | registry "https://registry.npm.taobao.org"
3 | sass_binary_site "https://npm.taobao.org/mirrors/node-sass/"
4 | phantomjs_cdnurl "http://cnpmjs.org/downloads"
5 | electron_mirror "https://npm.taobao.org/mirrors/electron/"
6 | sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/"
7 | profiler_binary_host_mirror "https://npm.taobao.org/mirrors/node-inspector/"
8 | chromedriver_cdnurl "https://cdn.npm.taobao.org/dist/chromedriver"
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # Environment Variables
27 | .env
28 | .env.build
29 |
--------------------------------------------------------------------------------
/src/packages/utils/defer.ts:
--------------------------------------------------------------------------------
1 | interface Defer {
2 | (): {
3 | resolve: () => void,
4 | reject: () => void,
5 | promise: Promise
6 | },
7 |
8 | (): {
9 | resolve: (val: T) => void,
10 | reject: () => void,
11 | promise: Promise
12 | },
13 | }
14 |
15 | export const defer: Defer = () => {
16 | const dfd = {} as any
17 | dfd.promise = new Promise((resolve, reject) => {
18 | dfd.resolve = resolve as any
19 | dfd.reject = reject
20 | })
21 | return dfd
22 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "types": ["node"],
6 | "allowJs": false,
7 | "skipLibCheck": false,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/src/packages/plugin/event.ts:
--------------------------------------------------------------------------------
1 | type CustomEventListener = () => void;
2 |
3 | export function createEvent() {
4 | const listeners: CustomEventListener[] = [];
5 | return {
6 | on: (cb: CustomEventListener) => {
7 | listeners.push(cb);
8 | },
9 | off: (cb: CustomEventListener) => {
10 | const index = listeners.indexOf(cb);
11 | if (index > -1) {
12 | listeners.splice(index, 1);
13 | }
14 | },
15 | emit: () => {
16 | listeners.forEach(c => c());
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/packages/service/dropdown/$$dropdown.scss:
--------------------------------------------------------------------------------
1 | .dropdown-service {
2 | position: fixed;
3 | z-index: 999;
4 | box-shadow: 0 1px 10px 2px rgba(0, 0, 0, 0.15);
5 | background-color: #fff;
6 | padding: 12px 0;
7 | transition: opacity 150ms linear;
8 | user-select: none;
9 | }
10 | .dropdown-item {
11 | padding: 2px 12px;
12 | font-weight: 400;
13 | font-size: 14px;
14 | line-height: 22px;
15 | color: #314569;
16 | transition: background-color linear 0.2s;
17 | span {
18 | margin-left: 2px;
19 | }
20 | &:hover {
21 | background-color: #f2f2f2;
22 | cursor: pointer;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/packages/visual.config.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Input } from "antd";
2 | import { createVisualConfig } from "./editor.utils";
3 |
4 | export const visualConfig = createVisualConfig();
5 |
6 | visualConfig.registryComponent('text', {
7 | label: '文本',
8 | preview: () => 预览文本,
9 | render: () => 渲染文本
10 | });
11 |
12 | visualConfig.registryComponent('button', {
13 | label: '按钮',
14 | preview: () => ,
15 | render: () =>
16 | });
17 |
18 | visualConfig.registryComponent('input', {
19 | label: '输入框',
20 | preview: () => ,
21 | render: () =>
22 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React 可视化拖拽编辑器
2 |
3 | **任务清单**
4 |
5 | - [X] 去除 React.StrictMode;(主要是 React Hooks 有影响,产生其奇怪的 BUG, 比如的 useMemo的第二个参数不管是那个一个值变化都要重新执行该函数的第一个回调函数,因为返回了新的对象,没有起到优化作用,还有 useState 等多个函数)
6 | - [X] 主页面结构:左侧菜单栏可选组件列表、中间容器画布、右侧编辑组件定义的属性;
7 | - [X] 从菜单栏拖拽组件到容器;
8 | - [X] 组件(Block)在容器的选中状态;
9 | - [X] 容器内组件可移动位置;
10 | - [X] 命令队列及对应的快捷键;
11 | - [X] 容器内的组件单选、多选、全选;
12 | - [X] 操作栏按钮:
13 | - [X] 撤销、重做 **重难点**;
14 | - [X] 置顶、置底;
15 | - [X] 删除、清空;
16 | - [X] 预览、关闭编辑模式;
17 | - [X] 导入、导出;
18 | - [X] 右键菜单;
19 | - [X] 拖拽粘性贴边;
20 | - [X] 组件可以拖动调整高度和宽度(height,width);
21 | - [X] 组件可以设置预定好的属性(props);
22 | - [X] 组件绑定值(model);
23 | - [X] 设置组件标识(soltName),根据这个标识,定义某个组件的行为(函数触发)和插槽的实现(自定义视图);
24 | - [X] 完善可选组件列表:
25 | - [X] 输入框:双向绑定值,调整宽度;
26 | - [X] 按钮:类型、文字、大小尺寸、拖拽调整宽高;
27 | - [X] 图片:自定义图片地址、拖拽调整图片宽高
28 | - [X] 下拉框:预定义选项值、双向绑定字段;
--------------------------------------------------------------------------------
/src/packages/style/VisualEditorBlock.module.scss:
--------------------------------------------------------------------------------
1 | @import './_variable.scss';
2 |
3 | @mixin pos {
4 | content: '';
5 | position: absolute;
6 | top: -2px;
7 | right: -2px;
8 | bottom: -2px;
9 | left: -2px;
10 | }
11 |
12 | .visual-editor__block {
13 | position: absolute;
14 | width: auto;
15 | height: auto;
16 | box-sizing: border-box;
17 | user-select: none;
18 | &.editor-block__mask {
19 | &::after {
20 | @include pos();
21 | pointer-events: none;
22 | border: 2px solid transparent;
23 | background-color: transparent;
24 | z-index: 1;
25 | }
26 | }
27 | &.editor-block__active {
28 | &::before {
29 | @include pos();
30 | border: 2px dashed $primary;
31 | z-index: 2;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | import data from './mock.json';
4 |
5 | import { VisualEditor } from "./packages/VisualEditor";
6 | import { visualConfig } from "./packages/visual.config";
7 | import { VisualEditorValue } from "./packages/editor.utils";
8 |
9 | export default () => {
10 |
11 | const [editorValue, setEditorValue] = useState(() => {
12 | const val: VisualEditorValue = data;
13 | return val;
14 | });
15 |
16 | return (
17 |
18 | {/*
可视化编辑器
*/}
19 |
24 | {/*
{JSON.stringify(data)}
*/}
25 |
26 | );
27 | };
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-react",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "tsc && vite build"
7 | },
8 | "dependencies": {
9 | "@ant-design/icons": "^4.5.0",
10 | "antd": "^4.15.0",
11 | "classnames": "^2.2.6",
12 | "deepcopy": "^2.1.0",
13 | "react": "^17.0.1",
14 | "react-color": "^2.19.3",
15 | "react-dom": "^17.0.1"
16 | },
17 | "devDependencies": {
18 | "@types/classnames": "^2.2.11",
19 | "@types/node": "^14.14.37",
20 | "@types/react": "^17.0.2",
21 | "@types/react-color": "^3.0.4",
22 | "@types/react-dom": "^17.0.1",
23 | "@vitejs/plugin-react-refresh": "^1.3.1",
24 | "less": "^4.1.1",
25 | "sass": "^1.42.0",
26 | "typescript": "^4.1.5",
27 | "vite": "^2.0.1",
28 | "vite-plugin-babel-import": "^2.0.5",
29 | "vite-plugin-style-import": "^1.2.1"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/packages/plugin/keyboard-code.ts:
--------------------------------------------------------------------------------
1 | export const KeyboardCode = {
2 | 16: 'shift',
3 | 17: 'ctrl',
4 | 18: 'alt',
5 |
6 | 8: 'backspace',
7 | 9: 'tab',
8 | 13: 'enter',
9 | 27: 'esc',
10 | 32: 'space',
11 |
12 | 37: 'left',
13 | 38: 'up',
14 | 39: 'right',
15 | 40: 'down',
16 | 46: 'delete',
17 | 189: '-',
18 | 187: '=',
19 |
20 | 48: '0',
21 | 49: '1',
22 | 50: '2',
23 | 51: '3',
24 | 52: '4',
25 | 53: '5',
26 | 54: '6',
27 | 55: '7',
28 | 56: '8',
29 | 57: '9',
30 |
31 | 65: 'a',
32 | 66: 'b',
33 | 67: 'c',
34 | 68: 'd',
35 | 69: 'e',
36 | 70: 'f',
37 | 71: 'g',
38 | 72: 'h',
39 | 73: 'i',
40 | 74: 'j',
41 | 75: 'k',
42 |
43 | 76: 'l',
44 | 77: 'm',
45 | 78: 'n',
46 | 79: 'o',
47 | 80: 'p',
48 | 81: 'q',
49 | 82: 'r',
50 | 83: 's',
51 | 84: 't',
52 | 85: 'u',
53 | 86: 'v',
54 | 87: 'w',
55 | 88: 'x',
56 | 89: 'y',
57 | 90: 'z',
58 |
59 | 112: 'F1',
60 | 113: 'F2',
61 | 114: 'F3',
62 | 115: 'F4',
63 | 116: 'F5',
64 | 117: 'F6',
65 | 118: 'F7',
66 | 119: 'F8',
67 | 120: 'F9',
68 | 121: 'F10',
69 | 122: 'F11',
70 | 123: 'F12',
71 | } as Record
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | import reactRefresh from '@vitejs/plugin-react-refresh';
3 | import { defineConfig } from 'vite';
4 | import styleImport from 'vite-plugin-style-import';
5 |
6 | export default defineConfig({
7 | plugins: [
8 | reactRefresh(),
9 | // styleImport({
10 | // libs: [
11 | // {
12 | // libraryName: 'antd',
13 | // esModule: true,
14 | // resolveStyle: (name) => {
15 | // return `antd/es/${name}/style`;
16 | // },
17 | // }
18 | // ]
19 | // }),
20 | ],
21 | // css: {
22 | // preprocessorOptions: {
23 | // less: {
24 | // javascriptEnabled: true
25 | // }
26 | // }
27 | // },
28 | esbuild: {
29 | jsxInject: "import React from 'react'", // 为每个 tsx jsx 自动引入 React,不用手动引入了
30 | },
31 | resolve: {
32 | alias: {
33 | "@": path.resolve(__dirname, "src"),
34 | "@assets": path.resolve(__dirname, "src/assets"),
35 | "@components": path.resolve(__dirname, "src/components"),
36 | "@images": path.resolve(__dirname, "src/assets/images"),
37 | "@views": path.resolve(__dirname, "src/views"),
38 | "@store": path.resolve(__dirname, "src/store"),
39 | }
40 | },
41 | server: {
42 | https: false, // 是否开启 https
43 | open: true, // 是否自动在浏览器打开
44 | port: 3000, // 端口号
45 | host: "0.0.0.0",
46 | hmr: {
47 | overlay: true, // 是否开启错误的阴影层
48 | }
49 | },
50 | optimizeDeps: {
51 | include: [] // 第三方库
52 | },
53 | build: {
54 | chunkSizeWarningLimit: 2000,
55 | terserOptions: {
56 | // 生产环境移除console
57 | compress: {
58 | drop_console: true,
59 | drop_debugger: true,
60 | },
61 | },
62 | rollupOptions: {
63 | output:{
64 | // manualChunks(id) {
65 | // if (id.includes('node_modules')) {
66 | // return id.toString().split('node_modules/')[1].split('/')[0].toString();
67 | // }
68 | // }
69 | manualChunks: {
70 | react: ['react', 'react-dom'],
71 | antd: ['antd']
72 | }
73 | }
74 | }
75 | }
76 |
77 | })
78 |
--------------------------------------------------------------------------------
/src/packages/EditorBlock.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useMemo, useRef } from 'react';
2 | import classNames from 'classnames';
3 |
4 | import { VisualEditorBlockData, VisualEditorConfig } from './editor.utils';
5 | import { useUpdate } from './hook/useUpdate';
6 |
7 | import classModule from './style/VisualEditorBlock.module.scss';
8 |
9 | export const VisualEditorBlock: React.FC<{
10 | block: VisualEditorBlockData,
11 | config: VisualEditorConfig,
12 | onMouseDown?: (e: React.MouseEvent) => void,
13 | onContextMenu?: (e: React.MouseEvent) => void,
14 | editing?: boolean,
15 | preview?: boolean,
16 | }> = (props) => {
17 | // 强制更新一次
18 | const { froceUpdate } = useUpdate();
19 | const elRef = useRef({} as HTMLDivElement);
20 |
21 | const style = useMemo(() => {
22 | return {
23 | top: `${props.block.top}px`,
24 | left: `${props.block.left}px`,
25 | zIndex: props.block.zIndex,
26 | opacity: props.block.adjustPosition ? 0 : "", // 解决调整拖拽结束时居中时组件闪动 BUG
27 | }
28 | }, [props.block.top, props.block.left, props.block.zIndex]);
29 |
30 | const classes = useMemo(() => classNames([
31 | classModule['visual-editor__block'],
32 | { [classModule['editor-block__mask']]: props.preview ? false : props.editing },
33 | { [classModule['editor-block__active']]: props.preview ? false : (props.editing ? props.block.focus : false) }])
34 | , [props.block.focus, props.editing, props.preview])
35 |
36 | const component = props.config.componentMap[props.block.componentKey];
37 |
38 | let render: any;
39 | if (!!component) {
40 | render = component.render({} as any);
41 | }
42 |
43 | useEffect(() => {
44 | if (props.block.adjustPosition) {
45 | // 设置是首次拖到容器中是否调整位置居于鼠标点
46 | const { top, left } = props.block;
47 | const blockCom = elRef.current;
48 | const { width, height } = blockCom.getBoundingClientRect();
49 | props.block.adjustPosition = false;
50 | props.block.left = left - width / 2;
51 | props.block.top = top - height / 2;
52 |
53 | // 记录 block 组件的高度和宽度
54 | props.block.width = blockCom.offsetWidth;
55 | props.block.height = blockCom.offsetHeight;
56 |
57 | froceUpdate(); // 需要引用一次才可以更新视图
58 | }
59 | }, []);
60 |
61 | return (() => {
62 |
63 | return (
64 |
71 | {render}
72 |
73 | )
74 | })()
75 | }
--------------------------------------------------------------------------------
/src/packages/editor.utils.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 容器中每个元素的的数据类型
3 | */
4 | export interface VisualEditorBlockData {
5 | componentKey: string, // component 对象的的 key,通过该值找到 visual config 中的对应的 component
6 | top: number, // block 在容器中的 top 位置
7 | left: number, // block 在容器中的 left 位置
8 | width: number, // block 组件自身的宽度
9 | height: number, // block 组件自身的高度
10 | adjustPosition: boolean, // 添加组件到容器中时是否需要调整位置
11 | focus: boolean, // 组件是否是选中状态
12 | zIndex: number, // block 组件元素的 z-index style 属性
13 | hasReasize: boolean, // block 组件元素是否调整大小
14 | props?: Record // block 组件元素右侧属性配置信息
15 | model?: Record // 组件元素右侧自定义配置属性信息(绑定值)
16 | slotName?: string // 组件标识
17 | }
18 | /**
19 | * 编辑器编辑的数据类型
20 | */
21 | export interface VisualEditorValue {
22 | container: { // 画布容器
23 | height: number,
24 | width: number,
25 | },
26 | blocks: VisualEditorBlockData[]
27 | }
28 |
29 | /**
30 | * 编辑器中自定义组件的类型
31 | */
32 | export interface VisualEditorComponent {
33 | key: string, // key 组件唯一标识符
34 | label: string, // label 组件左侧显示名
35 | render: (data: { // render 组件渲染函数,拖拽后在容器区与呈现的函数
36 | block: VisualEditorBlockData,
37 | size: {width?: string, height?: string},
38 | }) => JSX.Element,
39 | preview: () => JSX.Element, // preview 组件左侧预览函数
40 | }
41 |
42 |
43 | /**
44 | * 创建一个 block 的组件数据
45 | */
46 | export function createVisualBlock({
47 | top,
48 | left,
49 | component
50 | }: {
51 | top: number,
52 | left: number,
53 | component: VisualEditorComponent
54 | }): VisualEditorBlockData {
55 | return {
56 | adjustPosition: true,
57 | componentKey: component.key,
58 | top,
59 | left,
60 | width: 0,
61 | height: 0,
62 | focus: false,
63 | zIndex: 0,
64 | hasReasize: false
65 | }
66 | }
67 |
68 |
69 | /**
70 | * 创建编辑器的预设内容
71 | */
72 | export function createVisualConfig() {
73 | // 用于 block 数据,通过 componentKey 找到 component 对象,使用 component 对象的 render 属性渲染内容到 container 容器里
74 | const componentMap: { [k: string]: VisualEditorComponent } = {};
75 | // 用户在 menu 中预定义的组件列表
76 | const componentList: VisualEditorComponent[] = [];
77 |
78 | const registryComponent = (key: string, options: Omit) => {
79 | // key 是唯一的
80 | if (componentMap[key]) {
81 | const index = componentList.indexOf(componentMap[key]);
82 | componentList.splice(index, 1);
83 | }
84 | const newComponent = {
85 | key,
86 | ...options
87 | }
88 |
89 | componentList.push(newComponent);
90 | componentMap[key] = newComponent;
91 | }
92 |
93 | return {
94 | componentList,
95 | componentMap,
96 | registryComponent
97 | }
98 | }
99 |
100 | export type VisualEditorConfig = ReturnType;
--------------------------------------------------------------------------------
/src/packages/service/dropdown/$$dropdown.tsx:
--------------------------------------------------------------------------------
1 | import './$$dropdown.scss';
2 | import ReactDOM from "react-dom";
3 | import { MouseEvent, useContext, useEffect, useMemo, useRef, useState } from 'react';
4 | import { useCallbackRef } from '../../hook/useCallbackRef';
5 |
6 | type Reference = { x: number, y: number } | MouseEvent | HTMLElement;
7 | type Render = JSX.Element | JSX.Element[] | React.ReactFragment;
8 |
9 | interface DropdownOption {
10 | reference: Reference,
11 | render: () => Render,
12 | }
13 |
14 | const DropdownContext = React.createContext<{onClick: () => void}>({} as any);
15 |
16 | const DropdownComponent: React.FC<{
17 | option: DropdownOption,
18 | onRef: (ins: { show: (opt: DropdownOption) => void, close: () => void }) => void
19 | }> = (props) => {
20 |
21 | const elRef = useRef({} as HTMLDivElement);
22 |
23 | const [option, setOption] = useState(props.option);
24 | const [showFlag, setShowFlag] = useState(false);
25 |
26 | const styles = useMemo(() => {
27 | let x = 0, y = 0;
28 | const reference = option.reference
29 | if ('target' in reference) {
30 | x = reference.clientX - 20;
31 | y = reference.clientY - 20;
32 | } else if ('addEventListener' in reference) {
33 | const { top, left, height } = reference.getBoundingClientRect();
34 | x = left;
35 | y = top + height;
36 | } else {
37 | x = reference.x;
38 | y = reference.y;
39 | }
40 | return {
41 | left: `${x + 20}px`,
42 | top: `${y + 20}px`,
43 | display: showFlag ? 'inline-block' : 'none'
44 | }
45 | }, [option?.reference, showFlag]);
46 |
47 | const methods = {
48 | show: (opt: DropdownOption) => {
49 | setOption(opt);
50 | setShowFlag(true);
51 | },
52 | close: () => {
53 | setShowFlag(false);
54 | }
55 | };
56 |
57 | const handler = {
58 | onClickBody: useCallbackRef((ev: MouseEvent) => {
59 | if (elRef.current.contains(ev.target as Node)) {
60 | /*点击了dropdown content*/
61 | return;
62 | } else {
63 | methods.close();
64 | }
65 | }),
66 | onClickDropItem: useCallbackRef(() => {
67 | methods.close();
68 | })
69 | };
70 |
71 | props.onRef!(methods);
72 |
73 | useEffect(() => {
74 | document.body.addEventListener('click', handler.onClickBody as any);
75 | return () => {
76 | document.body.removeEventListener('click', handler.onClickBody as any);
77 | }
78 | }, [])
79 |
80 | return (<>
81 |
82 |
83 | {option?.render()}
84 |
85 |
86 | >);
87 | }
88 |
89 | export const DropdownOption: React.FC<{
90 | onClick?: (ev: React.MouseEvent) => void
91 | icon?: string,
92 | label?: string
93 | }> = (props) => {
94 |
95 | const dropdown = useContext(DropdownContext);
96 | const handler = {
97 | onClick: (e: React.MouseEvent) => {
98 | dropdown.onClick();
99 | props.onClick && props.onClick(e);
100 | }
101 | };
102 |
103 | return (
104 | {props.icon && }
105 | {props.label && {props.label}}
106 | {/* {props.children} 这个时元素默认的标签内的内容*/}
107 |
);
108 | }
109 |
110 | export const $$dropdown = (() => {
111 |
112 | let ins: any;
113 | return (options: DropdownOption) => {
114 | if (!ins) {
115 | const el = document.createElement('div');
116 | document.body.appendChild(el);
117 | ReactDOM.render( ins = val} />, el);
118 | }
119 |
120 | ins.show(options);
121 | }
122 | })();
123 |
124 |
--------------------------------------------------------------------------------
/src/packages/service/dialog/$$dialog.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Button, Input, Modal } from "antd";
3 | import ReactDOM from "react-dom";
4 | import deepcopy from 'deepcopy';
5 | import { defer } from "../../utils/defer";
6 |
7 | export enum DialogEdit {
8 | input = 'input',
9 | textarea = 'textarea',
10 | }
11 |
12 | interface DialogOption {
13 | title?: string,
14 | message?: string | (() => any),
15 | confirmButton?: boolean
16 | cancelButton?: boolean
17 | editType?: DialogEdit,
18 | editValue?: string,
19 | editReadonly?: boolean
20 | width?: string,
21 | onConfirm?: (editValue?: string) => void,
22 | onCancel?: () => void,
23 | }
24 |
25 | interface DialogInstance {
26 | show: (option?: DialogOption) => void,
27 | close: () => void,
28 | }
29 |
30 | const DialogComponent: React.FC<{ option?: DialogOption, onRef?: (ins: DialogInstance) => void }> = (props) => {
31 |
32 | let [option, setOption] = useState(props.option || {});
33 | const [showFlag, setShowFlag] = useState(false);
34 | const [editValue, setEditValue] = useState(option ? option.editValue : '');
35 |
36 | const methods = {
37 | show: (option?: DialogOption) => {
38 | setOption(deepcopy(option || {}));
39 | setEditValue(!option ? '' : (option.editValue || ''));
40 | setShowFlag(true);
41 | },
42 | close: () => setShowFlag(false),
43 | }
44 |
45 | props.onRef && props.onRef(methods);
46 |
47 | const handler = {
48 | onConfirm: () => {
49 | option.onConfirm && option.onConfirm(editValue)
50 | methods.close()
51 | },
52 | onCancel: () => {
53 | option.onCancel && option.onCancel()
54 | methods.close()
55 | },
56 | };
57 |
58 | const inputProps = {
59 | value: editValue,
60 | onChange: (e: React.ChangeEvent) => setEditValue(e.target.value),
61 | readOnly: option.editReadonly === true,
62 | };
63 |
64 | return (
65 |
73 | {option.cancelButton && }
74 | {option.confirmButton && }
75 | >
76 | ) : null}
77 | >
78 | {option.message}
79 | {option.editType === DialogEdit.input && (
80 |
81 | )}
82 | {option.editType === DialogEdit.textarea && (
83 |
84 | )}
85 |
86 | )
87 |
88 | }
89 |
90 | const getInstance = (() => {
91 | let ins: null | DialogInstance = null;
92 | return (option?: DialogOption) => {
93 | if (!ins) {
94 | const el = document.createElement('div');
95 | document.body.appendChild(el);
96 | ReactDOM.render( ins = val} />, el);
97 | }
98 | return ins!;
99 | }
100 | })();
101 |
102 | const DialogService = (option?: DialogOption) => {
103 | const ins = getInstance(option);
104 | ins.show(option);
105 | }
106 |
107 | export const $$dialog = Object.assign(DialogService, {
108 | textarea: (val?: string, option?: DialogOption) => {
109 | const dfd = defer();
110 | option = option || {};
111 | option.editType = DialogEdit.textarea;
112 | option.editValue = val;
113 | if (option.editReadonly !== true) {
114 | option.confirmButton = true;
115 | option.cancelButton = true;
116 | option.onConfirm = dfd.resolve;
117 | }
118 | DialogService(option);
119 | return dfd.promise;
120 | },
121 | input: (val?: string, option?: DialogOption) => {
122 | // TODO
123 | console.log(val, option)
124 | },
125 | })
--------------------------------------------------------------------------------
/src/asset/font/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {font-family: "iconfont";
2 | src: url('iconfont.eot?t=1610438286453'); /* IE9 */
3 | src: url('iconfont.eot?t=1610438286453#iefix') format('embedded-opentype'), /* IE6-IE8 */
4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAfEAAsAAAAADxAAAAd2AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEUgqPDItvATYCJAM0CxwABCAFhG0HgRsbjgwRFaz7IPsiwbaFkDcaa05axJnHSYukNfk8/Db/vgAfrxitD6OPrqJhVcYqdWvUFfCDs0/4WOkaXEWBq/r4S361BkAMMBiDSfPm99uvX7MSCOVp3Pqta0cu8HQ4w6z9//u5OlUIyTZvC4lDLV/83ccZahISSUPSLK4pWSu0Siz4z/Dq1Q6uyX/iSRcDAYBFIuqD6N6z/3AwoCElAk1mTsufBCYSDzrHToCJURIH0nrCBQUY0k7eBfCl8/XE92hKMAAJBQW54+C8HjnoHFF+O4uy1dow1pKBaS8VwHI1gAJQHwC9xIfJ4F4AEftQCiyi66QaUUcElRF1vl6R/MjsyLzIooi9qk3VgG9n1daKaHL7QygBY1Rpf4ACCQYqsODAI2b+SDGuP54SEvEYURZJiCiNWFB76IVYkIBcxIIB5CEWKkA+YsECZiMWHGAe6PZgEWIRA7DDDwRQ1cYPSqBqgB8UwLez2AcaCVZrCUAKQFp5QhjSlINSAhRo2CmQ6QrQiRWI2HSVSsnSNM9Pka5XS1JmTGwywwhCHZ5XZYbum7kgz3t0kyE3x735mCk/13NiqzEv37fluH4ZS3i9rLkR73CDtHkMHDzsQpzg9BFUkXem4POZKI7kZ/GEv/YUgeJo0cpBoFhCO5dCTc8WKH6ZM2Dbw4qOYFEFR1hInRhJUrvM4Y8/TlRsfdJK8Hhs7h5L98TScqAfWcUOPp/TA3LTwzzW6vUajdNsNOVXOLMtAVtWzh6rxWnK9xURrR36HLdh2XC3Izi9z8SFw9bw0+HWyo+WUMjCeeSlFk+pnmW1Q5UjLE+fWvPuwnq9phE+5xzea1T4TALkcxkcbAFnhYnoIlfP7zg7AP25jNMWBSeA9RoJLpcd5isKOvTD3TaDxZOaZhzKZZEXK9d/PpMt3Q6sY48twHG18ndfMCvyxMnDWd9jADvNpue9hELI6aE06NgDFCRnGEsUhX4tPTyBKeEXkte08eWI0PM4T2zps/mHQpbKymaHw9anTyddD6dur+x1LZRS9tS646ztnPNC0fll4ojhtLWqPQbSzYruQSxHtfOCmfKfF+S5LN9jMwxha6dyg91lyfPk5l8mOoxUonp4jL7kZKFYiyjkpFy90i7l3qzsall+k52lct/IcrVdlR31B6LRHdH60J8FJdwbvqSQjXJVvLHuN1z0YEkB9/pgwWp2T+F1Sn7oUxZlrSVVVYVbqlZbD77m+hSg7cyHT4qHDi2o7tgmHG7TsdAXCpAz1ND2pTwJmjU5Yjqf9caQZ3iTdd6Ub3idba2TZzqf/drQ3HQum7ph9jkTPimqbVZGzVhtUlm1KcpJWjuhJuzaaq14+Xps1Bjl6jFWQkmPom13GE1OgjOQkKBtltqnj6aZJilRM751d5pOTUxKiUnt20dr/lcJ8WkU+lzaVlJcaFxXXLx35drFcUsqti/RL9WE8jvM6BLXuxgrvzJsiLtzS9qQszq9XWNdSXF+8zXzU2bucs3VzFXfGtF5XJe4fot+m23X243Xj6Wtnbp25d7i4nUGbEz62aUKqlwtykT/T2WZgtas5dcKS4IB+aLnJ1kdVMs//ZxaSZuSoSlzqcp0KUdrmtrjUuNKp0+3T2WfPk02p8XaLTWixqxBy8Nf3ulZWAxWiut5Z72m0DJdLb8Lv5PV6S2jp6/soXIFXaqeF4dPYV1hF5v8XZ+goOVDQlVBtOpoT4paIdQOgRfgtaht90Tznh8zbsKYD8JjzRPte3HM2MGj3vODWu28fHMzqwgk7dzl1Zx4o3zAAEkycLlT3vbJu2BHtTNaDHVOHDdOlpqZZZfLLjnMvWRzc0keOsziUlWo2ODDrn9SMNkv7hP96JG08WLoN4rqgpM0/H/69NLYpnFyas3FFHOaUJMmR1aV9kybNlWFFoOdQbb51q4RQ4fJUnOzv1dRMlcvNTeT5HHjJv/kFyvEs0W2VZ6EVX9B3otaSEaA2he7qEygNvo51RAAqHzS5qw2nfwVtQfoHdQwFvaugEpZ/62TSgYA6gr5mKWXUal5ihmNAGoJlf63shH+vpu3c4K6/e8M9/Gf8iqt8AvBtVsIBuS7H/An0o2uAUDvWHFSFI7jw5hIDcW22QEC+kIBfAFjwapsoJZna/GmH2wozfxnYUPgIBGDBAcFBulyWl3foQCH5g4lGHRysKiHXodz0GE+KBC0CkBdrGEOAnVwwEFCxAWRotT3RBrq1w4FjKgWlDATlIOFldCdkEMmge9ObwwtcMq8VGdp9nqSnNym3wi3UTPntK3+wbmjWTVlfar6wgxuY5d8D62IV55pUp9sB4wjqcTUw0oZUZmuVeXXu5aWpsVp0Y2hxfJ0yrwCO0uzn1xOic//RriNmnP63Af+g3M3f9WUdQn5F5pL9TmW1vkeWuGpXhmlTJOHP0UqRlVOKtHr9bBSxhql6VrxsXxZVPaPp/t8DgC7kB8rLIuq6YZp2Y7rST8UdqSMYxq1RSGULt4LGBKh6YRZXokmzLcNXCdbRobsuikR22dzGCHYGaZHxsZoO+zwJNfYe+KHZrdYAAA=') format('woff2'),
5 | url('iconfont.woff?t=1610438286453') format('woff'),
6 | url('iconfont.ttf?t=1610438286453') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
7 | url('iconfont.svg?t=1610438286453#iconfont') format('svg'); /* iOS 4.1- */
8 | }
9 |
10 | .iconfont {
11 | font-family: "iconfont" !important;
12 | font-size: 16px;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .icon-close:before {
19 | content: "\e689";
20 | }
21 |
22 | .icon-place-top:before {
23 | content: "\e647";
24 | }
25 |
26 | .icon-place-bottom:before {
27 | content: "\e606";
28 | }
29 |
30 | .icon-entypomenu:before {
31 | content: "\e96d";
32 | }
33 |
34 | .icon-edit:before {
35 | content: "\e74d";
36 | }
37 |
38 | .icon-reset:before {
39 | content: "\e739";
40 | }
41 |
42 | .icon-import:before {
43 | content: "\e675";
44 | }
45 |
46 | .icon-delete:before {
47 | content: "\e665";
48 | }
49 |
50 | .icon-browse:before {
51 | content: "\e666";
52 | }
53 |
54 | .icon-back:before {
55 | content: "\e667";
56 | }
57 |
58 | .icon-export:before {
59 | content: "\e66e";
60 | }
61 |
62 | .icon-forward:before {
63 | content: "\e671";
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/src/packages/style/VisualEditor.module.scss:
--------------------------------------------------------------------------------
1 |
2 | @import "../../asset/font/iconfont.css";
3 | @import './_variable.scss';
4 |
5 | .visual-editor__container {
6 | position: fixed;
7 | top: $gap;
8 | left: $gap;
9 | right: $gap;
10 | bottom: $gap;
11 | &::before {
12 | content: '';
13 | position: fixed;
14 | top: 0;
15 | bottom: 0;
16 | left: 0;
17 | right: 0;
18 | background-color: #ccc;
19 | }
20 | .visual-editor__menu,
21 | .visual-editor__head,
22 | .visual-editor__operator,
23 | .visual-editor__body {
24 | position: absolute;
25 | top: $headSize;
26 | left: $menuSize;
27 | right: $operatorSize;
28 | bottom: 0;
29 | box-sizing: border-box;
30 | padding: 2px;
31 | background-color: #f6f6f6;
32 | overflow: auto;
33 | &.visual-editor__menu {
34 | top: 0;
35 | left: 0;
36 | bottom: 0;
37 | width: $menuSize;
38 | padding: 20px;
39 | border-right: 1px solid $ibc;
40 | background-color:#fff;
41 | box-sizing: border-box;
42 | user-select: none;
43 | .visual-editor__menu__title {
44 | font-size: 22px;
45 | color: $primary;
46 | border-bottom: 1px solid $ibc;
47 | }
48 | .editor-menu__item {
49 | position: relative;
50 | border: 2px solid $ibc;
51 | height: 80px;
52 | margin: 20px 0;
53 | &::after {
54 | content: '';
55 | position: absolute;
56 | top: 0;
57 | left: 0;
58 | bottom: 0;
59 | right: 0;
60 | }
61 | .menu-item__title {
62 | position: absolute;
63 | color: #fff;
64 | background-color: $primary;
65 | padding: 2px 5px;
66 | }
67 | .menu-item__content {
68 | position: absolute;
69 | top: 50%;
70 | left: 50%;
71 | transform: translate(-50%, -50%);
72 | margin: 5px 5px 0 5px;
73 | }
74 | }
75 | }
76 | &.visual-editor__head {
77 | top: 0;
78 | left: $menuSize;
79 | right: $operatorSize;
80 | height: $headSize;;
81 | background-color: #fff;
82 | border-bottom: 1px solid $ibc;
83 | display: flex;
84 | justify-content: center;
85 | align-items: center;
86 | .editor-head__button {
87 | position: relative;
88 | display: flex;
89 | align-items: center;
90 | justify-content: center;
91 | flex-direction: column;
92 | text-align: center;
93 | background-color: rgba(black, 0.3);
94 | color: #fff;
95 | padding: 3px 10px;
96 | transition: all linear 0.1s;
97 | cursor: pointer;
98 | user-select: none;
99 | & > span {
100 | font-size: 12px;
101 | }
102 | &:not(:last-child) {
103 | &::after {
104 | content: '';
105 | position: absolute;
106 | top: 0;
107 | right: 0;
108 | bottom: 0;
109 | width: 1px;
110 | background-color: rgba(white, 0.4);
111 | }
112 | }
113 | &:hover {
114 | background-color: #fff;
115 | color: $primary;
116 | box-shadow: 0px 0px 0px 0.5px $primary;
117 | }
118 | &:active {
119 | background-color: mix($primary, white, 10%);
120 | }
121 | }
122 | }
123 | &.visual-editor__operator {
124 | top: 0;
125 | right: 0;
126 | bottom: 0;
127 | width: $operatorSize;
128 | background-color: #fff;
129 | box-sizing: border-box;
130 | border-left: 1px solid $ibc;
131 | }
132 | }
133 | }
134 |
135 | .editor-body_container {
136 | position: relative;
137 | margin: 0 auto;
138 | background-color: #fff;
139 | box-sizing: border-box;
140 | &.edit-container__border {
141 | border: 1px dashed $primary;
142 | }
143 | .editor-mark-x,
144 | .editor-mark-y {
145 | position: absolute;
146 | &.editor-mark-x {
147 | top: 0;
148 | bottom: 0;
149 | left: 5px;
150 | border-left: 1px dashed $primary;
151 | }
152 | &.editor-mark-y {
153 | left: 0;
154 | right: 0;
155 | top: 5px;
156 | border-top: 1px dashed $primary;
157 | }
158 | }
159 | }
160 |
161 | .visual-editor__preview {
162 | position: relative;
163 | height: inherit;
164 | width: 100%;
165 | background-color: rgba(black, 0.1);
166 | .editor-preview__edit {
167 | position: fixed;
168 | right: 2%;
169 | top: 2%;
170 | z-index: 10;
171 | }
172 | .preview-edit__warpper {
173 | position: absolute;
174 | top: 50%;
175 | left: 50%;
176 | transform: translate(-50%, -50%);
177 | }
178 | }
179 |
180 |
181 | /* 自定义滚动条样式*/
182 | .custom-bar__style {
183 | &::-webkit-scrollbar {
184 | width: 8px;
185 | height: 8px;
186 | }
187 | &::-webkit-scrollbar-thumb {
188 | border-radius: 1em;
189 | background-color: rgba(0, 0, 0, 0.1);
190 | }
191 | &::-webkit-scrollbar-track {
192 | border-radius: 1em;
193 | background-color: transparent;
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "container": {
3 | "height": 500,
4 | "width": 600
5 | },
6 | "blocks": [
7 | {
8 | "adjustPosition": false,
9 | "componentKey": "text",
10 | "top": 69,
11 | "left": 244,
12 | "width": 56,
13 | "height": 22,
14 | "focus": false,
15 | "zIndex": 0,
16 | "hasReasize": false,
17 | "props": {
18 | "text": "登录界面",
19 | "color": {
20 | "hsl": {
21 | "h": 212.3684210526316,
22 | "s": 0.7238095238095237,
23 | "l": 0.5882352941176471,
24 | "a": 1
25 | },
26 | "hex": "#4a90e2",
27 | "rgb": {
28 | "r": 74,
29 | "g": 144,
30 | "b": 226,
31 | "a": 1
32 | },
33 | "hsv": {
34 | "h": 212.3684210526316,
35 | "s": 0.6725663716814159,
36 | "v": 0.8862745098039215,
37 | "a": 1
38 | },
39 | "oldHue": 250,
40 | "source": "hex"
41 | },
42 | "size": "18px"
43 | }
44 | },
45 | {
46 | "adjustPosition": false,
47 | "componentKey": "button",
48 | "top": 360,
49 | "left": 292.5,
50 | "width": 88,
51 | "height": 32,
52 | "focus": false,
53 | "zIndex": 0,
54 | "hasReasize": false,
55 | "slotName": "buttonComponent",
56 | "props": {
57 | "label": "提交"
58 | }
59 | },
60 | {
61 | "adjustPosition": false,
62 | "componentKey": "input",
63 | "top": 143,
64 | "left": 186,
65 | "width": 135,
66 | "height": 32,
67 | "focus": false,
68 | "zIndex": 0,
69 | "hasReasize": false,
70 | "model": {
71 | "default": "username"
72 | }
73 | },
74 | {
75 | "adjustPosition": false,
76 | "componentKey": "select",
77 | "top": 212,
78 | "left": 186,
79 | "width": 172,
80 | "height": 32,
81 | "focus": false,
82 | "zIndex": 0,
83 | "hasReasize": true,
84 | "props": {
85 | "options": [
86 | {
87 | "key": "1617589791638_1",
88 | "label": "喵喵",
89 | "val": "cat",
90 | "comments": "喵喵"
91 | },
92 | {
93 | "key": "1617589791638_0",
94 | "label": "狗狗",
95 | "val": "dog",
96 | "comments": "狗狗"
97 | }
98 | ]
99 | },
100 | "model": {
101 | "default": "aaaa"
102 | }
103 | },
104 | {
105 | "adjustPosition": false,
106 | "componentKey": "number-range",
107 | "top": 287,
108 | "left": 186,
109 | "width": 225,
110 | "height": 26,
111 | "focus": false,
112 | "zIndex": 0,
113 | "hasReasize": false,
114 | "model": {
115 | "start": "minLevel",
116 | "end": "maxLevel"
117 | }
118 | },
119 | {
120 | "adjustPosition": false,
121 | "componentKey": "button",
122 | "top": 360,
123 | "left": 180.5,
124 | "width": 88,
125 | "height": 32,
126 | "focus": false,
127 | "zIndex": 0,
128 | "hasReasize": false,
129 | "props": {
130 | "label": "取消",
131 | "type": "ghost"
132 | }
133 | },
134 | {
135 | "adjustPosition": false,
136 | "componentKey": "text",
137 | "top": 148,
138 | "left": 92,
139 | "width": 56,
140 | "height": 22,
141 | "focus": false,
142 | "zIndex": 0,
143 | "hasReasize": false,
144 | "props": {
145 | "text": "用户名:"
146 | }
147 | },
148 | {
149 | "adjustPosition": false,
150 | "componentKey": "text",
151 | "top": 222,
152 | "left": 92,
153 | "width": 56,
154 | "height": 22,
155 | "focus": false,
156 | "zIndex": 0,
157 | "hasReasize": false,
158 | "props": {
159 | "text": "喜好:"
160 | }
161 | },
162 | {
163 | "adjustPosition": false,
164 | "componentKey": "text",
165 | "top": 289,
166 | "left": 85,
167 | "width": 56,
168 | "height": 22,
169 | "focus": false,
170 | "zIndex": 0,
171 | "hasReasize": false,
172 | "props": {
173 | "text": "幸运数字:"
174 | }
175 | },
176 | {
177 | "adjustPosition": false,
178 | "componentKey": "image",
179 | "top": 164,
180 | "left": 434,
181 | "width": 100,
182 | "height": 100,
183 | "focus": false,
184 | "zIndex": 0,
185 | "hasReasize": false
186 | }
187 | ]
188 | }
--------------------------------------------------------------------------------
/src/packages/plugin/command.plugin.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useRef, useState } from "react";
2 | import { KeyboardCode } from "./keyboard-code";
3 |
4 | // command 的 execute 执行完之后,需要返回 undo、redo。execute 执行后会立即返回 redo,后续撤销的时候会执行 undo,重做的时候会执行 redo
5 | interface CommandExecute {
6 | redo: () => void, // 默认执行,重做会调用
7 | undo?: () => void, // 撤销会调用
8 | }
9 |
10 | interface Command {
11 | name: string, // 命令的唯一标识
12 | execute: (...args: any[]) => CommandExecute, // 命令执行时候,所处理的内容
13 | keyboard?: string | string[], // 命令监听的快捷键
14 | followQueue?: boolean, // 命令执行之后,是否需要将命令执行得到的 undo,redo 存入命令队列(像全选、撤销、重做这中命令不需要存入命令队列的)
15 | init?: () => ((() => void) | undefined), // 命令初始化函数,如果返回的,则是一个销毁命令函数
16 | data?: any // 命令缓存所需的数据信息
17 | }
18 |
19 | export function useCommander() {
20 |
21 | const [state] = useState(() => ({
22 | current: -1, // 当前命令队列中,最后执行的命令返回的 CommandExecute 对象
23 | queue: [] as CommandExecute[], // 命令队列容器
24 | commandList: [] as { current: Command }[], // 预定义命令的容器
25 | commands: {} as Record void>, // 通过 command name 执行 command 动作的一个包装对象
26 | destroyList: [] as ((() => void) | undefined)[] // 所有命令在组件销毁之前,需要执行的消除副作用的函数容器
27 | }));
28 |
29 | /**
30 | * 注册命令
31 | */
32 | const useRegistry = useCallback((command: Command) => {
33 | const commandRef = useRef(command);
34 | commandRef.current = command;
35 | useState(() => {
36 | // 判断命令是否存在
37 | if (state.commands[command.name]) {
38 | const existIndex = state.commandList.findIndex(item => item.current.name === command.name);
39 | state.commandList.splice(existIndex, 1);
40 | }
41 | state.commandList.push(commandRef);
42 | // 对应命令的方法 AAAAAAA
43 | state.commands[command.name] = (...args: any[]) => {
44 | const { redo, undo } = commandRef.current.execute(...args);
45 | // 默认执行重做
46 | redo();
47 | // 如果命令执行后,不需要进入命令队列,就直接结束
48 | if (commandRef.current.followQueue === false) {
49 | return;
50 | }
51 | // 否则,将命令队列中剩余的命令都删除,保留 current 及其之前的命令
52 | let { queue, current } = state;
53 | if (queue.length > 0) {
54 | queue = queue.slice(0, current + 1);
55 | state.queue = queue;
56 | }
57 | // 将命令队列中最后一个命令为i当前执行的命令
58 | queue.push({ undo, redo });
59 | // 索引加 1, 指向队列中的最有一个命令
60 | state.current = current + 1;
61 | }
62 | /**
63 | * commands 结构类型
64 | * {
65 | * undo: () => {},
66 | * redo: () => {},
67 | * delete: () => {},
68 | * clear: () => {},
69 | * placeTop: () => {},
70 | * placeBottom: () => {}
71 | * }
72 | */
73 |
74 | });
75 | }, []);
76 |
77 | // 快捷键
78 | const [keyboardEvent] = useState(() => {
79 | const onKeydown = (ev: KeyboardEvent) => {
80 | // 对于容器是否在空白区域或时操作某个组件的命令区分操作,比如空白区域时全选或全中所有的组件组件,在操作某个输入框组件时,全选就只会选中输入框中的文字
81 | if (document.activeElement !== document.body) {
82 | return;
83 | }
84 | const { keyCode, shiftKey, altKey, ctrlKey, metaKey } = ev;
85 |
86 | let keyString: string[] = [];
87 |
88 | if (ctrlKey || metaKey) {
89 | keyString.push('ctrl');
90 | }
91 | if (shiftKey) {
92 | keyString.push('shift');
93 | }
94 | if (altKey) {
95 | keyString.push('alt');
96 | }
97 | keyString.push(KeyboardCode[keyCode]);
98 |
99 | // 快捷键格式 'ctrl+alt+s'
100 | const keyNames = keyString.join('+');
101 |
102 | state.commandList.forEach(({ current: { keyboard, name } }) => {
103 | if (!keyboard) return;
104 |
105 | const keys = Array.isArray(keyboard) ? keyboard : [keyboard];
106 |
107 | if (keys.indexOf(keyNames) > -1) {
108 | state.commands[name](); // 执行对应的命令的方法 AAAAAAA
109 | ev.stopPropagation();
110 | ev.preventDefault();
111 | }
112 | })
113 | }
114 |
115 | const init = () => {
116 | window.addEventListener('keydown', onKeydown, true);
117 | return () => {
118 | window.removeEventListener('keydown', onKeydown, true)
119 | }
120 | }
121 | return {
122 | init
123 | }
124 | });
125 |
126 | // 初始化注册命令(useRegistry)时的所有的 command 的 init 的方法
127 | const useInit = useCallback(() => {
128 | useState(() => {
129 | state.commandList.forEach(command => {
130 | command.current.init && state.destroyList.push(command.current.init());
131 | });
132 | state.destroyList.push(keyboardEvent.init());
133 | });
134 |
135 | // 注册内置的撤回命令(撤回命令执行的结果是不需要进入命令队列的)
136 | useRegistry({
137 | name: 'undo',
138 | keyboard: 'ctrl+z',
139 | followQueue: false, // 标识不需要进入命令队列
140 | execute: () => {
141 | return {
142 | redo: () => {
143 | if (state.current === -1) return;
144 | const queueItem = state.queue[state.current];
145 | if (queueItem) {
146 | queueItem.undo && queueItem.undo();
147 | state.current--;
148 | }
149 | }
150 | }
151 | }
152 | });
153 |
154 | // 注册内置的重做命令(重做命令执行结果是不需要进入命令队列的)
155 | useRegistry({
156 | name: 'redo',
157 | keyboard: ['ctrl+y', 'ctrl+shift+z'],
158 | followQueue: false,
159 | execute: () => {
160 | return {
161 | redo: () => {
162 | const queueItem = state.queue[state.current + 1];
163 | if (queueItem) {
164 | queueItem.redo();
165 | state.current++;
166 | }
167 | }
168 | }
169 | }
170 | });
171 | }, []);
172 |
173 | return {
174 | state,
175 | useInit,
176 | useRegistry
177 | }
178 | }
--------------------------------------------------------------------------------
/src/packages/editor.command.tsx:
--------------------------------------------------------------------------------
1 | import deepcopy from "deepcopy";
2 | import { useRef } from "react";
3 | import { VisualEditorBlockData, VisualEditorValue } from "./editor.utils";
4 | import { useCallbackRef } from "./hook/useCallbackRef";
5 | import { useCommander } from "./plugin/command.plugin";
6 |
7 |
8 | export function useVisualCommand({
9 | focusData,
10 | value,
11 | updateBlocks,
12 | updateValue,
13 | dragend,
14 | dragstart
15 | }: {
16 | focusData: {
17 | focus: VisualEditorBlockData[],
18 | unFocus: VisualEditorBlockData[]
19 | },
20 | value: VisualEditorValue,
21 | updateBlocks: (blocks: VisualEditorBlockData[]) => void,
22 | updateValue: (value: VisualEditorValue) => void,
23 | dragstart: {
24 | on: (cb: () => void) => void,
25 | off: (cb: () => void) => void
26 | },
27 | dragend: {
28 | on: (cb: () => void) => void,
29 | off: (cb: () => void) => void
30 | }
31 | }) {
32 | const commander = useCommander();
33 |
34 | // 注册一个删除命令操作
35 | commander.useRegistry({
36 | name: 'delete',
37 | keyboard: ['delete', 'ctrl+d', 'backspace'],
38 | execute: () => {
39 | const data = {
40 | before: (() => deepcopy(value.blocks))(),
41 | after: (() => deepcopy(focusData.unFocus))()
42 | }
43 | return {
44 | redo: () => { // 重做
45 | updateBlocks(deepcopy(data.after));
46 | },
47 | undo: () => { // 撤销
48 | updateBlocks(deepcopy(data.before));
49 | }
50 | }
51 | }
52 | });
53 |
54 | // 注册一个清空命令操作
55 | commander.useRegistry({
56 | name: 'clear',
57 | execute: () => {
58 | const data = {
59 | before: deepcopy(value.blocks),
60 | after: deepcopy([]),
61 | }
62 | return {
63 | redo: () => {
64 | updateBlocks(deepcopy(data.after));
65 | },
66 | undo: () => {
67 | updateBlocks(deepcopy(data.before));
68 | },
69 | }
70 | }
71 | });
72 |
73 | {
74 | const dragData = useRef({ before: null as null | VisualEditorBlockData[] });
75 |
76 | const handler = { // 拖拽开始或结束就会通过已经订阅的事件来触发这个 dragstart、dragend 函数,执行对应的函数逻辑
77 | dragstart: useCallbackRef(() => dragData.current.before = deepcopy(value.blocks)),
78 | dragend: useCallbackRef(() => commander.state.commands.drag())
79 | }
80 | /**
81 | * 注册拖拽命令
82 | * 适用于如下三种情况:
83 | * 1. 从左侧菜单拖拽组件到容器画布;
84 | * 2. 在容器中拖拽组件调整位置;
85 | * 3. 拖动调整组件的高度和宽度。
86 | */
87 | commander.useRegistry({
88 | name: 'drag',
89 | init: () => {
90 | dragData.current = { before: null };
91 | dragstart.on(handler.dragstart);
92 | dragend.on(handler.dragend);
93 | return () => {
94 | dragstart.off(handler.dragstart);
95 | dragend.off(handler.dragend);
96 | }
97 | },
98 | execute: () => {
99 | const data = {
100 | before: deepcopy(dragData.current.before),
101 | after: deepcopy(value.blocks)
102 | };
103 | return {
104 | redo: () => {
105 | updateBlocks(deepcopy(data.after));
106 | },
107 | undo: () => {
108 | updateBlocks(deepcopy(data.before) || []);
109 | }
110 | }
111 | }
112 | });
113 | }
114 |
115 | // 注册置顶命令
116 | commander.useRegistry({
117 | name: 'placeTop',
118 | keyboard: 'ctrl+up',
119 | execute: () => {
120 |
121 | const data = {
122 | before: (() => deepcopy(value.blocks))(),
123 | after: (() => deepcopy(() => {
124 | const { focus, unFocus } = focusData;
125 | // 计算出 focus 选中的最大的 zIndex 值,unFocus 未选中的最小的 zIndex 值,计算它们的差值就是当前元素置顶的 zIndex 值
126 | const maxUnFocusIndex = unFocus.reduce((prev, item) => {
127 | return Math.max(prev, item.zIndex);
128 | }, -Infinity);
129 | const minFocusIndex = focus.reduce((prev, item) => {
130 | return Math.min(prev, item.zIndex);
131 | }, Infinity);
132 | let dur = maxUnFocusIndex - minFocusIndex + 1;
133 | if (dur >= 0) {
134 | dur++;
135 | focus.forEach(block => block.zIndex = block.zIndex + dur);
136 | }
137 | return value.blocks;
138 | }))()()
139 | };
140 | return {
141 | redo: () => updateBlocks(deepcopy(data.after) || []),
142 | undo: () => updateBlocks(deepcopy(data.before))
143 | };
144 | }
145 | });
146 |
147 | // 注册全选快捷键命令
148 | commander.useRegistry({
149 | name: 'selectAll',
150 | keyboard: ['ctrl+a'],
151 | followQueue: false,
152 | execute: () => {
153 | return {
154 | redo: () => {
155 | value.blocks.forEach(block => block.focus = true);
156 | updateBlocks(value.blocks);
157 | }
158 | }
159 | }
160 | });
161 |
162 | // 注册置顶命令
163 | commander.useRegistry({
164 | name: 'placeBottom',
165 | keyboard: 'ctrl+down',
166 | execute: () => {
167 |
168 | const data = {
169 | before: (() => deepcopy(value.blocks))(),
170 | after: (() => deepcopy(() => {
171 | const { focus, unFocus } = focusData;
172 | // 计算出 focus 选中的最大的 zIndex 值,unFocus 未选中的最小的 zIndex 值,计算它们的差值就是当前元素置顶的 zIndex 值
173 | const minUnFocusIndex = unFocus.reduce((prev, item) => {
174 | return Math.min(prev, item.zIndex);
175 | }, Infinity);
176 | const maxFocusIndex = focus.reduce((prev, item) => {
177 | return Math.max(prev, item.zIndex);
178 | }, -Infinity);
179 | const minFocusIndex = focus.reduce((prev, item) => {
180 | return Math.min(prev, item.zIndex);
181 | }, Infinity);
182 | let dur = maxFocusIndex - minUnFocusIndex + 1;
183 | if (dur >= 0) {
184 | dur++;
185 | focus.forEach(block => block.zIndex = block.zIndex - dur);
186 | if (minFocusIndex - dur < 0) {
187 | dur = dur - minFocusIndex;
188 | value.blocks.forEach(block => block.zIndex = block.zIndex + dur);
189 | }
190 | }
191 | return value.blocks;
192 | }))()()
193 | };
194 |
195 | return {
196 | redo: () => updateBlocks(deepcopy(data.after)),
197 | undo: () => updateBlocks(deepcopy(data.before))
198 | };
199 | }
200 | });
201 |
202 | // 注册导入数据时,更新数据命令
203 | commander.useRegistry({
204 | name: 'updateValue',
205 | execute: (newModelValue: VisualEditorValue) => {
206 | const data = {
207 | before: deepcopy(value),
208 | after: deepcopy(newModelValue)
209 | };
210 | return {
211 | redo: () => updateValue(data.after),
212 | undo: () => updateValue(data.before)
213 | }
214 | }
215 | });
216 |
217 | // 注册导入节点数据时,更新节点数据命令
218 | commander.useRegistry({
219 | name: 'updateBlock',
220 | execute: (newBlock: VisualEditorBlockData, oldBlock: VisualEditorBlockData) => {
221 | let blocks = deepcopy(value.blocks);
222 | const data = {
223 | before: blocks,
224 | after: (() => {
225 | blocks = [...blocks];
226 | const index = value.blocks.indexOf(oldBlock);
227 | if (index > -1) {
228 | blocks.splice(index, 1, newBlock);
229 | }
230 | return deepcopy(blocks);
231 | })(),
232 | }
233 | return {
234 | redo: () => {
235 | updateBlocks(deepcopy(data.after));
236 | },
237 | undo: () => {
238 | updateBlocks(deepcopy(data.before));
239 | },
240 | }
241 | },
242 | })
243 |
244 | // 初始内置的命令 undo,redo
245 | commander.useInit(); // 在底部调用
246 | return {
247 | delete: () => commander.state.commands.delete(),
248 | clear: () => commander.state.commands.clear(),
249 | undo: () => commander.state.commands.undo(),
250 | redo: () => commander.state.commands.redo(),
251 | placeBottom: () => commander.state.commands.placeBottom(),
252 | placeTop: () => commander.state.commands.placeTop(),
253 | updateValue: (newModelValue: VisualEditorValue) => commander.state.commands.updateValue(newModelValue),
254 | updateBlock: (newModelValue: VisualEditorValue, oldModelValue: VisualEditorBlockData) => commander.state.commands.updateBlock(newModelValue, oldModelValue),
255 | }
256 | }
--------------------------------------------------------------------------------
/src/asset/font/iconfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
63 |
--------------------------------------------------------------------------------
/src/packages/VisualEditor.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useRef, useState } from 'react';
2 | import { Button, notification, Tooltip } from 'antd';
3 | import { MenuUnfoldOutlined } from '@ant-design/icons'
4 |
5 | import '../asset/font/iconfont.css';
6 | import classModule from './style/VisualEditor.module.scss';
7 |
8 | import { createVisualBlock, VisualEditorBlockData, VisualEditorComponent, VisualEditorConfig, VisualEditorValue } from './editor.utils';
9 | import { VisualEditorBlock } from './EditorBlock';
10 | import { useCallbackRef } from './hook/useCallbackRef';
11 | import { useVisualCommand } from './editor.command';
12 | import { createEvent } from './plugin/event';
13 | import { $$dialog } from './service/dialog/$$dialog';
14 | import { $$dropdown, DropdownOption } from './service/dropdown/$$dropdown';
15 |
16 | export const VisualEditor: React.FC<{
17 | value: VisualEditorValue,
18 | config: VisualEditorConfig,
19 | onChange: (val: VisualEditorValue) => void, // 数据有变化出发外部函数
20 | }> = (props) => {
21 | // 当前是否处于编辑状态
22 | const [editing, setEditing] = useState(true); // true 处于编辑状态
23 | const [preview, setPreview] = useState(false); // true 处于预览状态
24 |
25 | const innerMethods = {
26 | // 切换编辑和运行状态
27 | toggleEditing () {
28 | setEditing(!editing);
29 | },
30 | // 切换编辑和预览状态
31 | togglePreview () {
32 | setPreview(!preview);
33 | },
34 | }
35 |
36 | // 拖拽开始和结束的事件监听
37 | const [dragstart] = useState(() => createEvent());
38 | const [dragend] = useState(() => createEvent());
39 |
40 | // 当前选中 block 组件元素的索引
41 | const [selectIndex, setSelectIndex] = useState(-1); // 默认 -1 没选中
42 | const selectBlock = useMemo(() => props.value.blocks[selectIndex], [props.value.blocks, selectIndex]);
43 |
44 | // 对外暴露方法
45 | const methods = {
46 | /**
47 | * 更新 block 数据,触发视图重新渲染
48 | * @param blocks
49 | */
50 | updateBlocks: (blocks: VisualEditorBlockData[]) => {
51 | props.onChange({
52 | ...props.value,
53 | blocks: [...blocks]
54 | })
55 | },
56 | /**
57 | * 更新整个画布容器的数据(所有 block 数据)
58 | * @param value
59 | */
60 | updateValue: (value: VisualEditorValue) => {
61 | props.onChange({...value});
62 | },
63 | /**
64 | * 查看(导出)数据
65 | * @param block
66 | */
67 | showBlockData: (block: VisualEditorBlockData) => {
68 | $$dialog.textarea(JSON.stringify(block), {editReadonly: true, title: '导出的JSON数据'});
69 | },
70 | /**
71 | * 导入数据
72 | * @param block
73 | */
74 | importBlockData: async (block: VisualEditorBlockData) => {
75 | const text = await $$dialog.textarea('', { title: '请输入需要导入的节点数据JSON字符串' });
76 | try {
77 | const data = JSON.parse(text || '');
78 | commander.updateBlock(data, block);
79 | } catch (e) {
80 | console.error(e)
81 | notification.open({
82 | type: 'error',
83 | message: '导入失败!',
84 | description: '导入的数据格式不正常,请检查!'
85 | })
86 | }
87 | },
88 | /**
89 | * 清空选中的数据
90 | */
91 | clearFocus: (external?: VisualEditorBlockData) => {
92 | let blocks = [...props.value.blocks];
93 | if (!blocks.length) return;
94 | if (external) {
95 | blocks = blocks.filter(item => item !== external);
96 | }
97 | blocks.forEach(block => block.focus = false);
98 | methods.updateBlocks(props.value.blocks);
99 | },
100 | }
101 |
102 | // 画布容器 DOM
103 | // 能够确保 containerRef 的存在这样书写
104 | const containerRef = useRef({} as HTMLDivElement);
105 | // 不能够确保 containerRef 的存在这样书写
106 | // const containerRef = useRef(null as null | HTMLDivElement);
107 |
108 | const containerStyles = useMemo(() => {
109 | return {
110 | width: `${props.value.container.width}px`,
111 | height: `${props.value.container.height}px`,
112 | }
113 | }, [props.value.container.height, props.value.container.width]);
114 |
115 | /**
116 | * 存储数据中选中状态和未选中状态的数据
117 | */
118 | const focusData = useMemo(() => {
119 | const focus: VisualEditorBlockData[] = [];
120 | const unFocus: VisualEditorBlockData[] = [];
121 |
122 | props.value.blocks.forEach(block => {
123 | (block.focus ? focus : unFocus).push(block);
124 | });
125 |
126 | return {
127 | focus,
128 | unFocus
129 | }
130 | }, [props.value.blocks]);
131 |
132 | //#region 左侧菜单拖拽到画布容器区域内
133 | const menuDraggier = (() => {
134 |
135 | const dragData = useRef({
136 | dragComponent: null as null | VisualEditorComponent // 左侧组件列表去拖拽的当前组件
137 | });
138 |
139 | const container = {
140 | dragenter: useCallbackRef((e: DragEvent) => {
141 | e.dataTransfer!.dropEffect = 'move';
142 | }),
143 | dragover: useCallbackRef((e: DragEvent) => {
144 | e.preventDefault();
145 | }),
146 | dragleave: useCallbackRef((e: DragEvent) => {
147 | e.dataTransfer!.dropEffect = 'none';
148 | }),
149 | drop: useCallbackRef((e: DragEvent) => {
150 | // 在容器画布添加组件
151 | // console.log('add')
152 |
153 | methods.updateBlocks([
154 | ...props.value.blocks,
155 | createVisualBlock({
156 | top: e.offsetY,
157 | left: e.offsetX,
158 | component: dragData.current.dragComponent!
159 | })
160 | ]);
161 |
162 | const t = setTimeout(() => {
163 | // 拖拽结束后,等页面渲染完毕,才执行,否则拖拽后就不会在页面正常显示
164 | dragend.emit(); // 触发事件
165 | clearTimeout(t);
166 | });
167 | }),
168 | };
169 |
170 | const block = {
171 | dragstart: useCallbackRef((e: React.DragEvent, dragComponent: VisualEditorComponent) => {
172 |
173 | containerRef.current.addEventListener('dragenter', container.dragenter);
174 | containerRef.current.addEventListener('dragover', container.dragover);
175 | containerRef.current.addEventListener('dragleave', container.dragleave);
176 | containerRef.current.addEventListener('drop', container.drop);
177 |
178 | dragData.current.dragComponent = dragComponent;
179 |
180 | dragstart.emit(); // 触发事件
181 |
182 | }),
183 | dragend: useCallbackRef((e: React.DragEvent) => {
184 |
185 | containerRef.current.removeEventListener('dragenter', container.dragenter);
186 | containerRef.current.removeEventListener('dragover', container.dragover);
187 | containerRef.current.removeEventListener('dragleave', container.dragleave);
188 | containerRef.current.removeEventListener('drop', container.drop);
189 | })
190 | };
191 |
192 | return block;
193 | })();
194 | //#endregion
195 |
196 | //#region 画布容器中 block 组件选中
197 | const focusHandler = (() => {
198 | const mousedownBlock = (e: React.MouseEvent, block: VisualEditorBlockData, index: number) => {
199 | e.stopPropagation();
200 | if (preview) return;
201 | e.preventDefault();
202 |
203 | // 右键不作任何处理, 防止右键时还可以拖动
204 | if (e.button === 2) return;
205 |
206 | if (e.shiftKey) {
207 | // 如果摁住了shift键,如果此时没有选中的 block,就选中该 block,否则使该 block 的数据选中状态取反
208 | if (focusData.focus.length <= 1) {
209 | block.focus = true;
210 | } else {
211 | block.focus = !block.focus;
212 | }
213 | methods.updateBlocks(props.value.blocks);
214 | } else {
215 | // 如果点击的这个 block 没有被选中,才清空这个其他选中的 block,否则不做任何事情。放置拖拽多个 block,取消其他 block 的选中状态
216 | if (!block.focus) {
217 | block.focus = true;
218 | methods.clearFocus(block);
219 | }
220 | }
221 |
222 | setSelectIndex(block.focus ? index : -1);
223 | // 使用延时器保证,数据时渲染后的正确数据,否则有 BUG
224 | setTimeout(() => {
225 | blockDraggier.mousedown(e, block);
226 | });
227 | };
228 | const mousedownContainer = (e: React.MouseEvent) => {
229 | e.stopPropagation();
230 | if (preview) return;
231 | e.preventDefault();
232 |
233 | // 判断不是点击了 container 容器就返回
234 | if (e.target !== e.currentTarget) return;
235 |
236 | // console.log('点击了 Contanier');
237 | if (!e.shiftKey) {
238 | // 点击空白出清空所有的选中的 block
239 | methods.clearFocus();
240 | setSelectIndex(-1);
241 | }
242 | };
243 |
244 | return {
245 | block: mousedownBlock,
246 | container: mousedownContainer
247 | }
248 | })();
249 | //#endregion
250 |
251 | //#region 画布容器组件的拖拽
252 | const blockDraggier = (() => {
253 |
254 | const [mark, setMark] = useState({x: null as null | number, y: null as null | number});
255 |
256 | // 存储拖拽时的数据
257 | const dragData = useRef({
258 | startX: 0, // 鼠标拖拽开始的,鼠标的横坐标
259 | startY: 0, // 鼠标拖拽开始的,鼠标的纵坐标
260 | startLeft: 0, // 鼠标拖拽开始的,拖拽的 block 横坐标
261 | startTop: 0, // 鼠标拖拽开始的,拖拽的 block 纵坐标
262 | startPosArray: [] as { top: number, left: number }[], // 鼠标拖拽开始的, 所有选中的 block 元素的横纵坐标值
263 |
264 | shiftKey: false, // 当前是否按住了 shift 键
265 | moveX: 0, // 拖拽过程中的时候, 鼠标的 left 值
266 | moveY: 0, // 拖拽过程中的时候, 鼠标的 top 值
267 | containerBar: {
268 | startScrollTop: 0, // 拖拽开始的时候, scrollTop 值
269 | moveScrollTop: 0, // 拖拽过程中的时候, scrollTop 值
270 | },
271 |
272 | dragging: false, // 当前是否属于拖拽状态
273 | markLines: { // 拖拽元素时,计算当前未选中的数据中,与拖拽元素之间参考辅助线的显示位置
274 | x: [] as {left: number, showLeft: number}[],
275 | y: [] as {top: number, showTop: number}[]
276 | }
277 | });
278 | const moveHandler = useCallbackRef(() => {
279 | if (!dragData.current.dragging) {
280 | dragData.current.dragging = true;
281 | dragstart.emit(); // 拖拽过程中派发事件
282 | }
283 |
284 | let {
285 | startX,
286 | startY,
287 | startPosArray,
288 | moveX,
289 | moveY,
290 | containerBar,
291 | startLeft,
292 | startTop,
293 | markLines,
294 | shiftKey
295 | } = dragData.current;
296 |
297 | moveY = moveY + (containerBar.moveScrollTop - containerBar.startScrollTop);
298 |
299 | // 移动时, 同时按住 shift 键,只在一个方向移动
300 | if (shiftKey) {
301 | const n = 12; // 预定差值
302 | if (Math.abs(moveX - startX) > Math.abs(moveY - startY) + n) {
303 | moveY = startY;
304 | } else {
305 | moveX = startX;
306 | }
307 | }
308 |
309 | //#region 参考线处理
310 | const nowMark = {
311 | mark: {
312 | x: null as null | number,
313 | y: null as null | number
314 | },
315 | top: startTop + moveY - startY,
316 | left: startLeft + moveX - startX
317 | };
318 |
319 | for (let i = 0; i < markLines.y.length; i++) {
320 | const { top, showTop } = markLines.y[i];
321 | if (Math.abs(nowMark.top - top) < 5) {
322 | moveY = top + startY - startTop;
323 | nowMark.mark.y = showTop;
324 | }
325 | }
326 |
327 | for (let i = 0; i < markLines.x.length; i++) {
328 | const { left, showLeft } = markLines.x[i];
329 | if (Math.abs(nowMark.left - left) < 5) {
330 | moveX = left + startX - startLeft;
331 | nowMark.mark.x = showLeft;
332 | }
333 | }
334 | //#endregion
335 |
336 | const durX = moveX - startX;
337 | const durY = moveY - startY;
338 |
339 | focusData.focus.forEach((block, index) => {
340 | const { left, top } = startPosArray[index];
341 | block.left = left + durX;
342 | block.top = top + durY;
343 | });
344 | methods.updateBlocks(props.value.blocks);
345 |
346 | // 修正参考线的位置
347 | setMark(nowMark.mark);
348 | });
349 |
350 | const scrollHandler = useCallbackRef((e: Event) => {
351 | dragData.current.containerBar.moveScrollTop = (e.target as HTMLDivElement).scrollTop;
352 | moveHandler();
353 | });
354 |
355 | const mousemove = useCallbackRef((e: MouseEvent) => {
356 | dragData.current.moveX = e.clientX;
357 | dragData.current.moveY = e.clientY;
358 | moveHandler();
359 | });
360 | const mouseup = useCallbackRef((e: MouseEvent) => {
361 | document.removeEventListener('mousemove', mousemove);
362 | document.removeEventListener('mouseup', mouseup);
363 | // 取消参考辅助线
364 | setMark({x: null, y: null});
365 |
366 | if (dragData.current.dragging) {
367 | dragData.current.dragging = false;
368 | dragend.emit(); // 拖拽过程中,鼠标抬起后才派发事件
369 | }
370 | });
371 | const mousedown = useCallbackRef((e: React.MouseEvent, block: VisualEditorBlockData) => {
372 |
373 | document.addEventListener('mousemove', mousemove);
374 | document.addEventListener('mouseup', mouseup);
375 |
376 | dragData.current = {
377 | startX: e.clientX,
378 | startY: e.clientY,
379 | startLeft: block.left,
380 | startTop: block.top,
381 | startPosArray: focusData.focus.map(({ top, left }) => ({ top, left })),
382 | moveX: e.clientX,
383 | moveY: e.clientY,
384 | shiftKey: e.shiftKey,
385 | containerBar: {
386 | startScrollTop: 0,
387 | moveScrollTop: 0,
388 | },
389 | dragging: false,
390 | markLines: (() => {
391 | const x: {left: number, showLeft: number}[] = [];
392 | const y: {top: number, showTop: number}[] = [];
393 | // 参考线处理 TODO...
394 |
395 | // 拖动的与未选中的对齐
396 | const { unFocus } = focusData;
397 | unFocus.forEach(v => {
398 | // 水平方向,拖动的顶部对未拖动的顶部
399 | y.push({ top: v.top, showTop: v.top });
400 | // 水平方向,拖动的中间对未拖动的中间
401 | y.push({ top: v.top + v.height / 2 - block.height / 2, showTop: v.top + v.height / 2 });
402 | // 水平方向,拖动的底部对未拖动的底部
403 | y.push({ top: v.top + v.height - block.height, showTop: v.top + v.height});
404 | // 水平方向,拖动的底部对未拖动的顶部
405 | y.push({ top: v.top - block.height, showTop: v.top });
406 | // 水平方向,拖动的底部对未拖动的底部
407 | y.push({ top: v.top + v.height, showTop: v.top + v.height });
408 |
409 | // 垂直方向,拖动的左侧对未拖动的左侧
410 | x.push({ left: v.left, showLeft: v.left });
411 | // 垂直方向,拖动的中间对未拖动的中间
412 | x.push({ left: v.left + v.width / 2 - block.width / 2, showLeft: v.left + v.width / 2 });
413 | // 垂直方向,拖动的右侧对未拖动的右侧
414 | x.push({ left: v.left + v.width - block.width, showLeft: v.left + v.width});
415 | // 垂直方向,拖动的右侧对未拖动的左侧
416 | x.push({ left: v.left - block.width, showLeft: v.left });
417 | // 垂直方向,拖动的左侧对未拖动的右侧
418 | x.push({ left: v.left + v.width, showLeft: v.left + v.width });
419 | });
420 | return { x, y }
421 | })()
422 | }
423 | });
424 |
425 | return {
426 | mousedown,
427 | mark
428 | }
429 | })();
430 | //#endregion
431 |
432 | // 命令管理对象
433 | const commander = useVisualCommand({
434 | value: props.value,
435 | focusData,
436 | updateBlocks: methods.updateBlocks,
437 | dragstart,
438 | dragend,
439 | updateValue: methods.updateValue
440 | });
441 |
442 | const handler = {
443 | onContextMenuBlock: (ev: React.MouseEvent, block: VisualEditorBlockData) => {
444 | ev.preventDefault();
445 | ev.stopPropagation();
446 |
447 |
448 | $$dropdown({
449 | reference: ev.nativeEvent,
450 | render: () => {
451 | return (<>
452 |
453 |
454 |
455 | methods.showBlockData(block)}}/>
456 | methods.importBlockData(block)}}/>
457 | >)
458 | }
459 | });
460 | }
461 | };
462 |
463 | //#region 功能操作栏按钮组
464 | const buttons: {
465 | label: string | (() => string),
466 | icon: string | (() => string),
467 | tip?: string | (() => string),
468 | handler: () => void,
469 | }[] = [
470 | {
471 | label: '撤销',
472 | icon: 'icon-back',
473 | handler: () => {
474 | // console.log('撤销')
475 | commander.undo();
476 | },
477 | tip: 'ctrl+z'
478 | },
479 | {
480 | label: '重做',
481 | icon: 'icon-forward',
482 | handler: () => {
483 | // console.log('重做')
484 | commander.redo();
485 | },
486 | tip: 'ctrl+y, ctrl+shift+z'
487 | },
488 | {
489 | label: '清空',
490 | icon: 'icon-reset',
491 | handler: () => {
492 | // console.log('清空')
493 | commander.clear();
494 | }
495 | },
496 | {
497 | label: '删除',
498 | icon: 'icon-delete',
499 | handler: () => {
500 | // console.log('删除')
501 | commander.delete();
502 | },
503 | tip: 'ctrl+d, backspace, delete'
504 | },
505 | {
506 | label: '置顶',
507 | icon: 'icon-place-top',
508 | handler: () => {
509 | // console.log('置顶')
510 | commander.placeTop();
511 | },
512 | tip: 'ctrl+up'
513 | },
514 | {
515 | label: '置底',
516 | icon: 'icon-place-bottom',
517 | handler: () => {
518 | // console.log('置底')
519 | commander.placeBottom();
520 |
521 | },
522 | tip: 'ctrl+down'
523 | },
524 | {
525 | label: '导入',
526 | icon: 'icon-import',
527 | handler: async () => {
528 | console.log('导入');
529 | const text = await $$dialog.textarea('', { title: '请输入导入的JSON字符串' });
530 | try {
531 | const data = JSON.parse(text || '');
532 | commander.updateValue(data);
533 | } catch (e) {
534 | console.error(e)
535 | notification.open({
536 | type: 'error',
537 | message: '导入失败!',
538 | description: '导入的数据格式不正常,请检查!'
539 | })
540 | }
541 | }
542 | },
543 | {
544 | label: '导出',
545 | icon: 'icon-export',
546 | handler: () => {
547 | console.log('导出')
548 | $$dialog.textarea(JSON.stringify(props.value), {editReadonly: true, title: '导出的JSON数据'});
549 | }
550 | },
551 | {
552 | label: () => preview ? '编辑' : '预览',
553 | icon: () => preview ? 'icon-edit' : 'icon-browse',
554 | handler: () => {
555 | if (!preview && !editing) {
556 | methods.clearFocus();
557 | }
558 | innerMethods.togglePreview();
559 | },
560 | },
561 | {
562 | label: '关闭',
563 | icon: 'icon-close',
564 | handler: () => {
565 | if (!editing) {
566 | methods.clearFocus();
567 | }
568 | innerMethods.toggleEditing();
569 | }
570 | }
571 | ]
572 | //#endregion
573 |
574 | return (<>
575 | {
576 | editing ? (
577 |
578 |
579 |
580 | 组件列表
581 |
582 | {
583 | props.config.componentList.map((component, index) => {
584 | return (
585 |
menuDraggier.dragstart(e, component)}
590 | onDragEnd={menuDraggier.dragend}
591 | >
592 |
{component.label}
593 |
594 | {component.preview()}
595 |
596 |
597 | )
598 | })
599 | }
600 |
601 |
602 | {
603 | buttons.map((btn, index) => {
604 | const label = typeof btn.label === "function" ? btn.label() : btn.label
605 | const icon = typeof btn.icon === "function" ? btn.icon() : btn.icon
606 | const content = (
607 |
608 | {label}
609 |
)
610 | return !btn.tip ? content :
611 | {content}
612 |
613 | })
614 | }
615 |
616 |
operator
617 |
618 |
624 | {
625 | props.value.blocks.map((block, index) => {
626 | return
focusHandler.block(e, block, index)}
633 | onContextMenu={e => handler.onContextMenuBlock(e, block)}
634 | />
635 | })
636 | }
637 | {/*参考辅助线 start*/}
638 | {blockDraggier.mark.x !== null && }
639 | {blockDraggier.mark.y !== null && }
640 | {/*参考辅助线 end*/}
641 |
642 |
643 |
644 | ) : (
645 |
646 |
647 |
648 |
649 | {
650 | props.value.blocks.map((block, index) => {
651 | return
656 | })
657 | }
658 |
659 |
660 |
661 | )
662 | }
663 | >);
664 | };
--------------------------------------------------------------------------------
/doc/开发文档.md:
--------------------------------------------------------------------------------
1 | # Vite2 + React17 + Typescript4 + Ant Design 4 可视化拖拽页面编辑器
2 |
3 | > 随着大前端的不断发展,越来越解放开发的双手,感觉要失业啦(^_^),针对一些简单模板处理,可直接通过个拖拉拽放,就可简单实现一些不错的 UI 功能。那么 `可视化拖拽页面编辑器` 的出现也是顺应时代的发展而出现。
4 |
5 |
6 |
7 | ***最终效果***
8 |
9 | 
10 |
11 | # 任务清单
12 |
13 | - [X] 主页面结构:左侧菜单栏可选组件列表、中间容器画布、右侧编辑组件定义的属性;
14 | - [X] 左侧菜单栏可选组件列表渲染;
15 | - [x] 从菜单栏拖拽组件到容器;
16 | - [x] 组件(Block)在容器的选中状态;
17 | - [x] 容器内组件可移动位置;
18 | - [X] 容器内的组件单选、多选、全选;
19 | - [X] 命令队列及对应的快捷键;
20 | - [X] 操作栏按钮:
21 | - [X] 撤销、重做 **重难点**;
22 | - [X] 删除、清空;
23 | - [X] 预览、关闭编辑模式;
24 | - [X] 置顶、置底;
25 | - [X] 导入、导出;
26 | - [X] 右键菜单;
27 | - [X] 拖拽参考线;
28 | - [ ] 组件可以拖动调整高度和宽度(height,width);
29 | - [ ] 组件可以设置预定好的属性(props);
30 | - [ ] 组件绑定值(model);
31 | - [ ] 设置组件标识(soltName),根据这个标识,定义某个组件的行为(函数触发)和插槽的实现(自定义视图);
32 | - [ ] 完善可选组件列表:
33 | - [ ] 输入框:双向绑定值,调整宽度;
34 | - [ ] 按钮:类型、文字、大小尺寸、拖拽调整宽高;
35 | - [ ] 图片:自定义图片地址、拖拽调整图片宽高
36 | - [ ] 下拉框:预定义选项值、双向绑定字段;
37 |
38 |
39 | # 一、项目搭建与页面布局
40 |
41 | 1. 项目所需依赖:
42 |
43 | ```json
44 | "dependencies": {
45 | "@ant-design/icons": "^4.5.0",
46 | "antd": "^4.15.0",
47 | "classnames": "^2.2.6",
48 | "deepcopy": "^2.1.0",
49 | "react": "^17.0.1",
50 | "react-color": "^2.19.3",
51 | "react-dom": "^17.0.1"
52 | },
53 | "devDependencies": {
54 | "@types/classnames": "^2.2.11",
55 | "@types/node": "^14.14.37",
56 | "@types/react": "^17.0.2",
57 | "@types/react-color": "^3.0.4",
58 | "@types/react-dom": "^17.0.1",
59 | "@vitejs/plugin-react-refresh": "^1.3.1",
60 | "less": "^4.1.1",
61 | "sass": "^1.42.0",
62 | "typescript": "^4.1.5",
63 | "vite": "^2.0.1",
64 | "vite-plugin-babel-import": "^2.0.5",
65 | "vite-plugin-style-import": "^1.2.1"
66 | }
67 | ```
68 |
69 | 2. vite 配置
70 |
71 | ```js
72 | const path = require('path');
73 | import reactRefresh from '@vitejs/plugin-react-refresh';
74 | import { defineConfig } from 'vite';
75 | import styleImport from 'vite-plugin-style-import';
76 |
77 | export default defineConfig({
78 | plugins: [
79 | reactRefresh(),
80 | styleImport({
81 | libs: [
82 | {
83 | libraryName: 'antd',
84 | esModule: true,
85 | resolveStyle: (name) => {
86 | return `antd/es/${name}/style`;
87 | },
88 | }
89 | ]
90 | }),
91 | ],
92 | css: {
93 | preprocessorOptions: {
94 | less: {
95 | javascriptEnabled: true
96 | }
97 | }
98 | },
99 | esbuild: {
100 | jsxInject: "import React from 'react'", // 为每个 tsx jsx 自动引入 React,不用手动引入了
101 | },
102 | resolve: {
103 | alias: {
104 | "@": path.resolve(__dirname, "src"),
105 | "@assets": path.resolve(__dirname, "src/assets"),
106 | "@components": path.resolve(__dirname, "src/components")
107 | }
108 | },
109 | server: {
110 | https: false, // 是否开启 https
111 | open: true, // 是否自动在浏览器打开
112 | port: 3000, // 端口号
113 | host: "0.0.0.0",
114 | hmr: {
115 | overlay: true, // 是否开启错误的阴影层
116 | }
117 | },
118 | optimizeDeps: {
119 | include: [] // 第三方库
120 | },
121 | build: {
122 | chunkSizeWarningLimit: 2000,
123 | terserOptions: {
124 | // 生产环境移除 console
125 | compress: {
126 | drop_console: true,
127 | drop_debugger: true,
128 | },
129 | },
130 | rollupOptions: {
131 | output:{
132 | manualChunks: { // 分包
133 | react: ['react', 'react-dom'],
134 | antd: ['antd']
135 | }
136 | }
137 | }
138 | }
139 | })
140 | ```
141 |
142 |
143 |
144 | ### 实现基本的左中右布局
145 |
146 | - 左侧是组件列表的菜单栏
147 | - 中间是画布容器和顶部的工具栏,用来编辑和预览页面
148 | - 右侧是对应画布容器中某个组件,对应显示的该组件的属性配置
149 |
150 | 这布局下相对简单就脑补一下下吧 ^_^
151 |
152 |
153 |
154 | # 二、基本数据结构设计
155 |
156 | > 针对于一个画布容器与组件的关系,对应一个画布大小变化,和一个画布中对应多个组件,一个组件配置相关信息,对于拖拽大家一定想到,**定位**,如果优化,可以使用 CSS3 的属性 `transform` 的 `translateX`,` translateY`优化。这里暂时不做处理。
157 |
158 | - 定义数据结构
159 | - container:画布容器信息
160 | - blocks:画布容器中的组件信息
161 | - blocks中存放每个 block 组件信息,包含了组件的唯一标识、位置、宽高、状态等信息
162 |
163 | ```tsx
164 | /**
165 | * 容器中每个元素的的数据类型
166 | */
167 | export interface VisualEditorBlock {
168 | componentKey: string, // component 对象的的 key 唯一标识
169 | top: number, // block 在容器中的 top 位置
170 | left: number, // block 在容器中的 left 位置
171 | width: number, // block 组件自身的宽度
172 | height: number, // block 组件自身的高度
173 | adjustPosition: boolean, // 添加组件到容器中时是否需要调整位置
174 | focus: boolean, // 组件是否是选中状态
175 | zIndex: number, // block 组件元素的 z-index style 属性
176 | hasReasize: boolean, // block 组件元素是否曾调整国大小
177 | props?: Record // block 组件元素右侧属性配置信息
178 | model?: Record // 组件元素右侧自定义配置属性信息(绑定值)
179 | slotName?: string // 组件标识
180 | }
181 | /**
182 | * 编辑器编辑的数据类型
183 | */
184 | export interface VisualEditorValue {
185 | container: { // 画布容器
186 | height: number,
187 | width: number,
188 | },
189 | blocks: VisualEditorBlock[]
190 | }
191 | ```
192 |
193 | [代码传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/25575720ea2b1c13d6572ae8dad41ea1621fd5c5)
194 |
195 |
196 | # 三、左侧菜单栏可选组件列表渲染
197 |
198 | 1. 开发注册组件
199 |
200 | ```tsx
201 | // visual.config.tsx
202 | import { Button, Input } from "antd";
203 | import { createVisualConfig } from "./editor.utils";
204 |
205 | export const visualConfig = createVisualConfig();
206 |
207 | visualConfig.registryComponent('text', {
208 | label: '文本',
209 | preview: () => 预览文本,
210 | render: () => 渲染文本
211 | });
212 |
213 | visualConfig.registryComponent('button', {
214 | label: '按钮',
215 | preview: () => ,
216 | render: () =>
217 | });
218 |
219 | visualConfig.registryComponent('input', {
220 | label: '输入框',
221 | preview: () => ,
222 | render: () =>
223 | });
224 | ```
225 |
226 | **注册组件的函数**:
227 |
228 | ```tsx
229 | /**
230 | * 创建编辑器的预设内容
231 | */
232 | export function createVisualConfig() {
233 | // 用于 block 数据,通过 componentKey 找到 component 对象,使用 component 对象的 render 属性渲染内容到 container 容器里
234 | const componentMap: { [k: string]: VisualEditorComponent } = {};
235 | // 用户在 menu 中预定义的组件列表
236 | const componentList: VisualEditorComponent[] = [];
237 |
238 | const registryComponent = (key: string, options: Omit) => {
239 | // key 是唯一的
240 | if (componentMap[key]) {
241 | const index = componentList.indexOf(componentMap[key]);
242 | componentList.splice(index, 1);
243 | }
244 | const newComponent = {
245 | key,
246 | ...options
247 | }
248 |
249 | componentList.push(newComponent);
250 | componentMap[key] = newComponent;
251 | }
252 |
253 | return {
254 | componentList,
255 | componentMap,
256 | registryComponent
257 | }
258 | }
259 | ```
260 |
261 | 2. 渲染注册的组件
262 |
263 | ```tsx
264 | // VisualEditor.tsx
265 |
266 | // 代码省略.....
267 |
268 | export const VisualEditor: React.FC<{
269 | value: VisualEditorValue,
270 | config: VisualEditorConfig
271 | }> = (props) => {
272 |
273 | return (<>
274 |
275 |
276 |
277 | 组件列表
278 |
279 | {
280 | props.config.componentList.map((component, index) => {
281 | return (
282 |
283 |
{component.label}
284 |
285 | {component.preview()}
286 |
287 |
288 | )
289 | })
290 | }
291 |
292 |
header
293 |
operator
294 |
body
295 |
296 | >);
297 | };
298 | ```
299 |
300 | ***效果***
301 |
302 | 
303 |
304 | [代码传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/25575720ea2b1c13d6572ae8dad41ea1621fd5c5)
305 |
306 |
307 | # 四、block 组件画布容器区域渲染
308 |
309 | 1. block 组件
310 |
311 | ```tsx
312 | // EditorBlock.tsx
313 | export const VisualEditorBlock: React.FC<{
314 | block: VisualEditorBlockData,
315 | config: VisualEditorConfig,
316 | editing: boolean
317 | }> = (props) => {
318 | const style = useMemo(() => {
319 | return {
320 | top: `${props.block.top}px`,
321 | left: `${props.block.left}px`,
322 | }
323 | }, [props.block.top, props.block.left]);
324 |
325 | const component = props.config.componentMap[props.block.componentKey];
326 |
327 | let render: any;
328 | if (!!component) {
329 | render = component.render({} as any);
330 | }
331 |
332 | return (() => {
333 | const mask = props.editing ? 'mask': '';
334 | return (
335 |
336 | {render}
337 |
338 | )
339 | })()
340 | }
341 | ```
342 |
343 | 2. block 组件的渲染
344 |
345 | ```tsx
346 | // VisualEditor.tsx
347 | export const VisualEditor: React.FC<{
348 | value: VisualEditorValue,
349 | config: VisualEditorConfig
350 | }> = (props) => {
351 | // 代码省略.....
352 |
353 | const containerStyles = useMemo(() => {
354 | return {
355 | width: `${props.value.container.width}px`,
356 | height: `${props.value.container.height}px`,
357 | }
358 | }, [props.value.container.height, props.value.container.width]);
359 | return (<>
360 | {
361 | editing ? (
362 |
363 | {/* // 代码省略..... */}
364 |
header
365 |
operator
366 |
367 |
368 | {
369 | props.value.blocks.map((block, index) => {
370 | return
376 | })
377 | }
378 |
379 |
380 |
381 | ) : (
382 |
383 |
384 |
385 |
386 | {
387 | props.value.blocks.map((block, index) => {
388 | return
394 | })
395 | }
396 |
397 |
398 |
399 | )
400 | }
401 | >);
402 | };
403 | ```
404 |
405 | [代码传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/b3df70f8a0b324ace90e28899c9d8f03c3f9df40)
406 |
407 | ***效果图***
408 |
409 | 
410 |
411 | # 五、左侧菜单组件拖拽到画布容器区域渲染
412 |
413 | > 监听 html5 拖拽函数
414 |
415 |
416 |
417 | ```tsx
418 | // VisualEditor.tsx
419 |
420 | export const VisualEditor: React.FC<{
421 | value: VisualEditorValue,
422 | config: VisualEditorConfig,
423 | onChange: (val: VisualEditorValue) => void, // 数据有变化出发外部函数
424 | }> = (props) => {
425 | // 当前是否处于编辑状态
426 | const [editing, setEditing] = useState(true);
427 | const methods = {
428 | // 切换编辑和运行状态
429 | toggleEditing () {
430 | setEditing(!editing);
431 | },
432 | /**
433 | * 更新 block 数据,触发视图重新渲染
434 | * @param blocks
435 | */
436 | updateBlocks: (blocks: VisualEditorBlockData[]) => {
437 | props.onChange({
438 | ...props.value,
439 | blocks: [...blocks]
440 | })
441 | },
442 | }
443 |
444 | // 画布容器 DOM
445 | const containerRef = useRef({} as HTMLDivElement);
446 |
447 | const containerStyles = useMemo(() => {
448 | return {
449 | width: `${props.value.container.width}px`,
450 | height: `${props.value.container.height}px`,
451 | }
452 | }, [props.value.container.height, props.value.container.width]);
453 |
454 | //#region 左侧菜单拖拽到画布容器区域内
455 | const menuDraggier = (() => {
456 |
457 | const dragData = useRef({
458 | dragComponent: null as null | VisualEditorComponent // 左侧组件列表去拖拽的当前组件
459 | });
460 |
461 | const container = {
462 | dragenter: useCallbackRef((e: DragEvent) => {
463 | e.dataTransfer!.dropEffect = 'move';
464 | }),
465 | dragover: useCallbackRef((e: DragEvent) => {
466 | e.preventDefault();
467 | }),
468 | dragleave: useCallbackRef((e: DragEvent) => {
469 | e.dataTransfer!.dropEffect = 'none';
470 | }),
471 | drop: useCallbackRef((e: DragEvent) => {
472 | // 在容器画布添加组件
473 | console.log('add')
474 |
475 | methods.updateBlocks([
476 | ...props.value.blocks,
477 | createVisualBlock({
478 | top: e.offsetY,
479 | left: e.offsetX,
480 | component: dragData.current.dragComponent!
481 | })
482 | ]);
483 |
484 | }),
485 | };
486 |
487 | const block = {
488 | dragstart: useCallbackRef((e: React.DragEvent, dragComponent: VisualEditorComponent) => {
489 |
490 | containerRef.current.addEventListener('dragenter', container.dragenter);
491 | containerRef.current.addEventListener('dragover', container.dragover);
492 | containerRef.current.addEventListener('dragleave', container.dragleave);
493 | containerRef.current.addEventListener('drop', container.drop);
494 |
495 | dragData.current.dragComponent = dragComponent;
496 |
497 | }),
498 | dragend: useCallbackRef((e: React.DragEvent) => {
499 |
500 | containerRef.current.removeEventListener('dragenter', container.dragenter);
501 | containerRef.current.removeEventListener('dragover', container.dragover);
502 | containerRef.current.removeEventListener('dragleave', container.dragleave);
503 | containerRef.current.removeEventListener('drop', container.drop);
504 | })
505 | };
506 |
507 | return block;
508 | })();
509 | //#endregion
510 |
511 | return (<>
512 | {
513 | editing ? (
514 |
515 | {/* // 代码省略..... */}
516 |
header
517 |
operator
518 |
519 |
524 | {
525 | props.value.blocks.map((block, index) => {
526 | return
532 | })
533 | }
534 |
535 |
536 |
537 | ) : (
538 | // 代码省略.....
539 | )
540 | }
541 | >);
542 | };
543 | ```
544 |
545 | [代码传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/a33d3799e3024b72fa213072a14928ae478dc63c)
546 |
547 |
548 |
549 |
550 |
551 | # 六、画布容器中组件选中
552 |
553 | > 是否选中,为每个元素组件绑定一个字段 focus 做判断处理,可以按住 `Shift` 选中多个,点击画布容器空白处,取消所有的选中状态。
554 |
555 | ```tsx
556 | // VisualEditor.tsx
557 |
558 | const methods = {
559 | /**
560 | * 更新 block 数据,触发视图重新渲染
561 | * @param blocks
562 | */
563 | updateBlocks: (blocks: VisualEditorBlockData[]) => {
564 | props.onChange({
565 | ...props.value,
566 | blocks: [...blocks]
567 | })
568 | },
569 | }
570 | //#region 画布容器中 block 组件选中
571 | const focusHandler = (() => {
572 | const mousedownBlock = (e: React.MouseEvent, block: VisualEditorBlockData, index: number) => {
573 | e.stopPropagation();
574 | if (preview) return;
575 | e.preventDefault();
576 |
577 | if (e.shiftKey) {
578 | // 如果摁住了shift键,如果此时没有选中的 block,就选中该 block,否则使该 block 的数据选中状态取反
579 | if (focusData.focus.length <= 1) {
580 | block.focus = true;
581 | } else {
582 | block.focus = !block.focus;
583 | }
584 | methods.updateBlocks(props.value.blocks);
585 | } else {
586 | // 如果点击的这个 block 没有被选中,才清空这个其他选中的 block,否则不做任何事情。放置拖拽多个 block,取消其他 block 的选中状态
587 | if (!block.focus) {
588 | block.focus = true;
589 | methods.clearFocus(block);
590 | }
591 | }
592 |
593 | setSelectIndex(block.focus ? index : -1);
594 | // 使用延时器保证,数据时渲染后的正确数据,否则有 BUG
595 | setTimeout(() => {
596 | blockDraggier.mousedown(e, block);
597 | });
598 | };
599 | const mousedownContainer = (e: React.MouseEvent) => {
600 | e.stopPropagation();
601 | if (preview) return;
602 | e.preventDefault();
603 |
604 | // 右键不作任何处理
605 | if (e.button === 1) return;
606 | // 判断不是点击了 container 容器就返回
607 | if (e.target !== e.currentTarget) return;
608 |
609 | // console.log('点击了 Contanier');
610 | if (!e.shiftKey) {
611 | // 点击空白出清空所有的选中的 block
612 | methods.clearFocus();
613 | setSelectIndex(-1);
614 | }
615 | };
616 |
617 | return {
618 | block: mousedownBlock,
619 | container: mousedownContainer
620 | }
621 | })();
622 | //#endregion
623 | ```
624 |
625 | ***效果图***
626 | 
627 |
628 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/539a99057735659c73bd34991d28591f5557b814)
629 |
630 | # 七、选中的组件进行拖拽
631 |
632 | > 多个选中也一起移动
633 |
634 | ```tsx
635 | // VisualEditor.tsx
636 |
637 | const methods = {
638 | /**
639 | * 更新 block 数据,触发视图重新渲染
640 | * @param blocks
641 | */
642 | updateBlocks: (blocks: VisualEditorBlockData[]) => {
643 | props.onChange({
644 | ...props.value,
645 | blocks: [...blocks]
646 | })
647 | },
648 | /**
649 | * 清空选中的数据
650 | */
651 | clearFocus: (external?: VisualEditorBlockData) => {
652 | let blocks = [...props.value.blocks];
653 | if (!blocks.length) return;
654 | if (external) {
655 | blocks = blocks.filter(item => item !== external);
656 | }
657 | blocks.forEach(block => block.focus = false);
658 | methods.updateBlocks(props.value.blocks);
659 | },
660 | }
661 |
662 | //#region 画布容器组件的拖拽
663 | const blockDraggier = (() => {
664 |
665 | const [mark, setMark] = useState({x: null as null | number, y: null as null | number});
666 |
667 | // 存储拖拽时的数据
668 | const dragData = useRef({
669 | startX: 0, // 鼠标拖拽开始的,鼠标的横坐标
670 | startY: 0, // 鼠标拖拽开始的,鼠标的纵坐标
671 | startLeft: 0, // 鼠标拖拽开始的,拖拽的 block 横坐标
672 | startTop: 0, // 鼠标拖拽开始的,拖拽的 block 纵坐标
673 | startPosArray: [] as { top: number, left: number }[], // 鼠标拖拽开始的, 所有选中的 block 元素的横纵坐标值
674 |
675 | shiftKey: false, // 当前是否按住了 shift 键
676 | moveX: 0, // 拖拽过程中的时候, 鼠标的 left 值
677 | moveY: 0, // 拖拽过程中的时候, 鼠标的 top 值
678 | containerBar: {
679 | startScrollTop: 0, // 拖拽开始的时候, scrollTop 值
680 | moveScrollTop: 0, // 拖拽过程中的时候, scrollTop 值
681 | },
682 |
683 | dragging: false, // 当前是否属于拖拽状态
684 | markLines: { // 拖拽元素时,计算当前未选中的数据中,与拖拽元素之间参考辅助线的显示位置
685 | x: [] as {left: number, showLeft: number}[],
686 | y: [] as {top: number, showTop: number}[]
687 | }
688 | });
689 | const moveHandler = useCallbackRef(() => {
690 | if (!dragData.current.dragging) {
691 | dragData.current.dragging = true;
692 | }
693 |
694 | let {
695 | startX,
696 | startY,
697 | startPosArray,
698 | moveX,
699 | moveY,
700 | containerBar,
701 | startLeft,
702 | startTop,
703 | markLines,
704 | shiftKey
705 | } = dragData.current;
706 |
707 | moveY = moveY + (containerBar.moveScrollTop - containerBar.startScrollTop);
708 |
709 | // 移动时, 同时按住 shift 键,只在一个方向移动
710 | if (shiftKey) {
711 | const n = 12; // 预定差值
712 | if (Math.abs(moveX - startX) > Math.abs(moveY - startY) + n) {
713 | moveY = startY;
714 | } else {
715 | moveX = startX;
716 | }
717 | }
718 |
719 | const durX = moveX - startX;
720 | const durY = moveY - startY;
721 |
722 | focusData.focus.forEach((block, index) => {
723 | const { left, top } = startPosArray[index];
724 | block.left = left + durX;
725 | block.top = top + durY;
726 | });
727 | methods.updateBlocks(props.value.blocks);
728 |
729 | });
730 |
731 | const scrollHandler = useCallbackRef((e: Event) => {
732 | dragData.current.containerBar.moveScrollTop = (e.target as HTMLDivElement).scrollTop;
733 | moveHandler();
734 | });
735 |
736 | const mousemove = useCallbackRef((e: MouseEvent) => {
737 | dragData.current.moveX = e.clientX;
738 | dragData.current.moveY = e.clientY;
739 | moveHandler();
740 | });
741 | const mouseup = useCallbackRef((e: MouseEvent) => {
742 | document.removeEventListener('mousemove', mousemove);
743 | document.removeEventListener('mouseup', mouseup);
744 |
745 | if (dragData.current.dragging) {
746 | dragData.current.dragging = false;
747 | }
748 | });
749 | const mousedown = useCallbackRef((e: React.MouseEvent, block: VisualEditorBlockData) => {
750 |
751 | document.addEventListener('mousemove', mousemove);
752 | document.addEventListener('mouseup', mouseup);
753 |
754 | dragData.current = {
755 | startX: e.clientX,
756 | startY: e.clientY,
757 | startLeft: block.left,
758 | startTop: block.top,
759 | startPosArray: focusData.focus.map(({ top, left }) => ({ top, left })),
760 | moveX: e.clientX,
761 | moveY: e.clientY,
762 | shiftKey: e.shiftKey,
763 | containerBar: {
764 | startScrollTop: 0,
765 | moveScrollTop: 0,
766 | },
767 | dragging: false,
768 | markLines: (() => {
769 | const x = [{ left: 0, showLeft: 0}];
770 | const y = [{ top: 0, showTop: 0}];
771 | return { x, y }
772 | })()
773 | }
774 | });
775 | return {
776 | mousedown,
777 | mark
778 | }
779 | })();
780 | //#endregion
781 | ```
782 |
783 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/16eaacbbb1e9d682d8bfc972e4f5d45261870a09)
784 |
785 | # 八、顶部操作栏
786 |
787 | > 编辑的操作必要的一些删除,撤销,重做,导入,导出
788 |
789 | ```tsx
790 | VisualEditor.tsx
791 |
792 | //#region 功能操作栏按钮组
793 | const buttons: {
794 | label: string | (() => string),
795 | icon: string | (() => string),
796 | tip?: string | (() => string),
797 | handler: () => void,
798 | }[] = [
799 | {
800 | label: '撤销',
801 | icon: 'icon-back',
802 | handler: () => {
803 | console.log('撤销')
804 | },
805 | tip: 'ctrl+z'
806 | },
807 | {
808 | label: '重做',
809 | icon: 'icon-forward',
810 | handler: () => {
811 | console.log('重做')
812 | },
813 | tip: 'ctrl+y, ctrl+shift+z'
814 | },
815 | {
816 | label: '导入',
817 | icon: 'icon-import',
818 | handler: async () => {
819 | console.log('导入')
820 | }
821 | },
822 | {
823 | label: '导出',
824 | icon: 'icon-export',
825 | handler: () => {
826 | console.log('导出')
827 | }
828 | },
829 | {
830 | label: '置顶',
831 | icon: 'icon-place-top',
832 | handler: () => {
833 | console.log('置顶')
834 | },
835 | tip: 'ctrl+up'
836 | },
837 | {
838 | label: '置底',
839 | icon: 'icon-place-bottom',
840 | handler: () => {
841 | console.log('置底')
842 | },
843 | tip: 'ctrl+down'
844 | },
845 | {
846 | label: '删除',
847 | icon: 'icon-delete',
848 | handler: () => {
849 | console.log('删除')
850 | }, tip: 'ctrl+d, backspace, delete'
851 | },
852 | {
853 | label: '清空',
854 | icon: 'icon-reset',
855 | handler: () => {
856 | console.log('清空')
857 | }
858 | },
859 | {
860 | label: () => preview ? '编辑' : '预览',
861 | icon: () => preview ? 'icon-edit' : 'icon-browse',
862 | handler: () => {
863 | if (!preview && !editing) {
864 | methods.clearFocus();
865 | }
866 | innerMethods.togglePreview();
867 | },
868 | },
869 | {
870 | label: '关闭',
871 | icon: 'icon-close',
872 | handler: () => {
873 | if (!editing) {
874 | methods.clearFocus();
875 | }
876 | innerMethods.toggleEditing();
877 | }
878 | }
879 | ]
880 | //#endregion
881 |
882 | // 省略代码......
883 |
884 |
885 | {
886 | buttons.map((btn, index) => {
887 | const label = typeof btn.label === "function" ? btn.label() : btn.label
888 | const icon = typeof btn.icon === "function" ? btn.icon() : btn.icon
889 | const content = (
890 |
891 | {label}
892 |
)
893 | return !btn.tip ? content :
894 | {content}
895 |
896 | })
897 | }
898 |
899 | ```
900 |
901 | ***效果图***
902 | 
903 |
904 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/8e41eb0d2600331d4ea43231dc0b3a27b080cc22)
905 |
906 | # 九、撤销,重做,删除、清空命令实现
907 |
908 |
909 | - 基础命令初始化
910 |
911 | ```ts
912 | // command.plugin.ts
913 | import { useCallback, useRef, useState } from "react";
914 |
915 | // command 的 execute 执行完之后,需要返回 undo、redo。execute 执行后会立即返回 redo,后续撤销的时候会执行 undo,重做的时候会执行 redo
916 | interface CommandExecute {
917 | redo: () => void, // 默认执行,重做会调用
918 | undo?: () => void, // 撤销会调用
919 | }
920 |
921 | interface Command {
922 | name: string, // 命令的唯一标识
923 | execute: (...args: any[]) => CommandExecute, // 命令执行时候,所处理的内容
924 | keyboard?: string | string[], // 命令监听的快捷键
925 | followQueue?: boolean, // 命令执行之后,是否需要将命令执行得到的 undo,redo 存入命令队列(像全选、撤销、重做这中命令不需要存入命令队列的)
926 | init?: () => ((() => void) | undefined), // 命令初始化函数,如果返回的,则是一个销毁命令函数
927 | data?: any // 命令缓存所需的数据信息
928 | }
929 |
930 | export function useCommander() {
931 |
932 | const [state] = useState(() => ({
933 | current: -1, // 当前命令队列中,最后执行的命令返回的 CommandExecute 对象
934 | queue: [] as CommandExecute[], // 命令队列容器
935 | commandList: [] as { current: Command }[], // 预定义命令的容器
936 | commands: {} as Record void>, // 通过 command name 执行 command 动作的一个包装对象
937 | destroyList: [] as ((() => void) | undefined)[] // 所有命令在组件销毁之前,需要执行的消除副作用的函数容器
938 | }));
939 |
940 | /**
941 | * 注册命令
942 | */
943 | const useRegistry = useCallback((command: Command) => {
944 | const commandRef = useRef(command);
945 | commandRef.current = command;
946 | useState(() => {
947 | // 判断命令是否存在
948 | if (state.commands[command.name]) {
949 | const existIndex = state.commandList.findIndex(item => item.current.name === command.name);
950 | state.commandList.splice(existIndex, 1);
951 | }
952 | state.commandList.push(commandRef);
953 | // 对应命令的方法 AAAAAAA
954 | state.commands[command.name] = (...args: any[]) => {
955 | const { redo, undo } = commandRef.current.execute(...args);
956 | // 默认执行重做
957 | redo();
958 | // 如果命令执行后,不需要进入命令队列,就直接结束
959 | if (commandRef.current.followQueue === false) {
960 | return;
961 | }
962 | // 否则,将命令队列中剩余的命令都删除,保留 current 及其之前的命令
963 | let { queue, current } = state;
964 | if (queue.length > 0) {
965 | queue = queue.slice(0, current + 1);
966 | state.queue = queue;
967 | }
968 | // 将命令队列中最后一个命令为i当前执行的命令
969 | queue.push({ undo, redo });
970 | // 索引加 1, 指向队列中的最有一个命令
971 | state.current = current + 1;
972 | }
973 | /**
974 | * commands 结构类型
975 | * {
976 | * undo: () => {},
977 | * redo: () => {},
978 | * delete: () => {},
979 | * clear: () => {},
980 | * placeTop: () => {},
981 | * placeBottom: () => {}
982 | * }
983 | */
984 |
985 | });
986 | }, []);
987 |
988 | // 初始化注册命令(useRegistry)时的所有的 command 的 init 的方法
989 | const useInit = useCallback(() => {
990 | useState(() => {
991 | state.commandList.forEach(command => {
992 | command.current.init && state.destroyList.push(command.current.init());
993 | });
994 | // state.destroyList.push(keyboardEvent.init());
995 | });
996 |
997 | // 注册内置的撤回命令(撤回命令执行的结果是不需要进入命令队列的)
998 | useRegistry({
999 | name: 'undo',
1000 | keyboard: 'ctrl+z',
1001 | followQueue: false, // 标识不需要进入命令队列
1002 | execute: () => {
1003 | return {
1004 | redo: () => {
1005 | if (state.current === -1) return;
1006 | const queueItem = state.queue[state.current];
1007 | if (queueItem) {
1008 | queueItem.undo && queueItem.undo();
1009 | state.current--;
1010 | }
1011 | }
1012 | }
1013 | }
1014 | });
1015 |
1016 | // 注册内置的重做命令(重做命令执行结果是不需要进入命令队列的)
1017 | useRegistry({
1018 | name: 'redo',
1019 | keyboard: ['ctrl+y', 'ctrl+shift+z'],
1020 | followQueue: false,
1021 | execute: () => {
1022 | return {
1023 | redo: () => {
1024 | const queueItem = state.queue[state.current + 1];
1025 | if (queueItem) {
1026 | queueItem.redo();
1027 | state.current++;
1028 | }
1029 | }
1030 | }
1031 | }
1032 | });
1033 | }, []);
1034 |
1035 | return {
1036 | state,
1037 | useInit,
1038 | useRegistry
1039 | }
1040 | }
1041 | ```
1042 |
1043 | - 扩展命令注册并导出
1044 |
1045 | ```tsx
1046 | // editor.command.tsx
1047 | import deepcopy from "deepcopy";
1048 | import { VisualEditorBlockData, VisualEditorValue } from "./editor.utils";
1049 | import { useCommander } from "./plugin/command.plugin";
1050 |
1051 | export function useVisualCommand({
1052 | focusData,
1053 | value,
1054 | updateBlocks
1055 | }: {
1056 | focusData: {
1057 | focus: VisualEditorBlockData[],
1058 | unFocus: VisualEditorBlockData[]
1059 | },
1060 | value: VisualEditorValue,
1061 | updateBlocks: (blocks: VisualEditorBlockData[]) => void,
1062 | }) {
1063 | const commander = useCommander();
1064 |
1065 | // 注册一个删除命令操作
1066 | commander.useRegistry({
1067 | name: 'delete',
1068 | keyboard: ['delete', 'ctrl+d', 'backspace'],
1069 | execute: () => {
1070 | const data = {
1071 | before: (() => deepcopy(value.blocks))(),
1072 | after: (() => deepcopy(focusData.unFocus))()
1073 | }
1074 | return {
1075 | redo: () => { // 重做
1076 | updateBlocks(deepcopy(data.after));
1077 | },
1078 | undo: () => { // 撤销
1079 | updateBlocks(deepcopy(data.before));
1080 | }
1081 | }
1082 | }
1083 | });
1084 |
1085 | // 注册一个清空命令操作
1086 | commander.useRegistry({
1087 | name: 'clear',
1088 | execute: () => {
1089 | const data = {
1090 | before: deepcopy(value.blocks),
1091 | after: deepcopy([]),
1092 | }
1093 | return {
1094 | redo: () => {
1095 | updateBlocks(deepcopy(data.after));
1096 | },
1097 | undo: () => {
1098 | updateBlocks(deepcopy(data.before));
1099 | },
1100 | }
1101 | }
1102 | })
1103 |
1104 | // 初始内置的命令 undo,redo
1105 | commander.useInit(); // 在底部调用
1106 | return {
1107 | delete: () => commander.state.commands.delete(),
1108 | clear: () => commander.state.commands.clear(),
1109 | undo: () => commander.state.commands.undo(),
1110 | redo: () => commander.state.commands.redo(),
1111 | }
1112 | }
1113 | ```
1114 |
1115 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/c1e7102f40200eeda74fbc457491e7e76b5113fc)
1116 |
1117 | # 十、绑定键盘快捷键事件
1118 |
1119 | > 快捷键的组合处理函数
1120 |
1121 | ```ts
1122 | // command.plugin.ts
1123 |
1124 | // 快捷键
1125 | const [keyboardEvent] = useState(() => {
1126 | const onKeydown = (ev: KeyboardEvent) => {
1127 | // 对于容器是否在空白区域或时操作某个组件的命令区分操作,比如空白区域时全选或全中所有的组件组件,在操作某个输入框组件时,全选就只会选中输入框中的文字
1128 | if (document.activeElement !== document.body) {
1129 | return;
1130 | }
1131 | const { keyCode, shiftKey, altKey, ctrlKey, metaKey } = ev;
1132 |
1133 | let keyString: string[] = [];
1134 |
1135 | if (ctrlKey || metaKey) {
1136 | keyString.push('ctrl');
1137 | }
1138 | if (shiftKey) {
1139 | keyString.push('shift');
1140 | }
1141 | if (altKey) {
1142 | keyString.push('alt');
1143 | }
1144 | keyString.push(KeyboardCode[keyCode]);
1145 |
1146 | // 快捷键格式 'ctrl+alt+s'
1147 | const keyNames = keyString.join('+');
1148 |
1149 | state.commandList.forEach(({ current: { keyboard, name } }) => {
1150 | if (!keyboard) return;
1151 |
1152 | const keys = Array.isArray(keyboard) ? keyboard : [keyboard];
1153 |
1154 | if (keys.indexOf(keyNames) > -1) {
1155 | state.commands[name](); // 执行对应的命令的方法 AAAAAAA
1156 | ev.stopPropagation();
1157 | ev.preventDefault();
1158 | }
1159 | })
1160 | }
1161 | ```
1162 |
1163 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/2e4d65ac7e696daadfb0cc0c7d0055b9e7d31d90)
1164 |
1165 | # 十一、注册拖拽事件命令到命令队列中去
1166 |
1167 | > 拖拽开始和结束都需要派发出事件(发布订阅的模式),在画布容器移动的过程中也需要派发事件,拖拽结束鼠标抬起也要派发结束事件。
1168 |
1169 | ```ts
1170 | {
1171 | const dragData = useRef({ before: null as null | VisualEditorBlockData[] });
1172 |
1173 | const handler = {
1174 | // 拖拽开始或结束就会通过已经订阅的事件来触发这个 dragstart、dragend 函数,执行对应的函数逻辑
1175 | dragstart: useCallbackRef(() => dragData.current.before = deepcopy(value.blocks)),
1176 | dragend: useCallbackRef(() => commander.state.commands.drag())
1177 | }
1178 | /**
1179 | * 注册拖拽命令
1180 | * 适用于如下三种情况:
1181 | * 1. 从左侧菜单拖拽组件到容器画布;
1182 | * 2. 在容器中拖拽组件调整位置;
1183 | * 3. 拖动调整组件的高度和宽度。
1184 | */
1185 | commander.useRegistry({
1186 | name: 'drag',
1187 | init: () => {
1188 | dragData.current = { before: null };
1189 | dragstart.on(handler.dragstart);
1190 | dragend.on(handler.dragend);
1191 | return () => {
1192 | dragstart.off(handler.dragstart);
1193 | dragend.off(handler.dragend);
1194 | }
1195 | },
1196 | execute: () => {
1197 | const data = {
1198 | before: deepcopy(dragData.current.before),
1199 | after: deepcopy(value.blocks)
1200 | };
1201 | return {
1202 | redo: () => {
1203 | updateBlocks(deepcopy(data.after));
1204 | },
1205 | undo: () => {
1206 | updateBlocks(deepcopy(data.before) || []);
1207 | }
1208 | }
1209 | }
1210 | });
1211 | }
1212 | ```
1213 |
1214 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/5e28b64f19b8fd11ca1ad442eaef876258624500)
1215 |
1216 |
1217 |
1218 | # 十二、顶部操作栏的置顶、置底
1219 |
1220 |
1221 |
1222 | ```ts
1223 | // VisualEditor.command.tsx
1224 |
1225 | // 注册置顶命令
1226 | commander.useRegistry({
1227 | name: 'placeTop',
1228 | keyboard: 'ctrl+up',
1229 | execute: () => {
1230 | const data = {
1231 | before: (() => deepcopy(value.blocks))(),
1232 | after: (() => deepcopy(() => {
1233 | const { focus, unFocus } = focusData;
1234 | // 计算出 focus 选中的最大的 zIndex 值,unFocus 未选中的最小的 zIndex 值,计算它们的差值就是当前元素置顶的 zIndex 值
1235 | const maxUnFocusIndex = unFocus.reduce((prev, item) => {
1236 | return Math.max(prev, item.zIndex);
1237 | }, -Infinity);
1238 | const minFocusIndex = focus.reduce((prev, item) => {
1239 | return Math.min(prev, item.zIndex);
1240 | }, Infinity);
1241 | let dur = maxUnFocusIndex - minFocusIndex + 1;
1242 | if (dur >= 0) {
1243 | dur++;
1244 | focus.forEach(block => block.zIndex = block.zIndex + dur);
1245 | }
1246 | return value.blocks;
1247 | }))()()
1248 | };
1249 | return {
1250 | redo: () => updateBlocks(deepcopy(data.after) || []),
1251 | undo: () => updateBlocks(deepcopy(data.before))
1252 | };
1253 | }
1254 | });
1255 |
1256 | // 注册置顶命令
1257 | commander.useRegistry({
1258 | name: 'placeBottom',
1259 | keyboard: 'ctrl+down',
1260 | execute: () => {
1261 |
1262 | const data = {
1263 | before: (() => deepcopy(value.blocks))(),
1264 | after: (() => deepcopy(() => {
1265 | // 这的置顶算法需要优化一下
1266 | const { focus, unFocus } = focusData;
1267 | // 计算出 focus 选中的最大的 zIndex 值,unFocus 未选中的最小的 zIndex 值,计算它们的差值就是当前元素置顶的 zIndex 值
1268 | const minUnFocusIndex = unFocus.reduce((prev, item) => {
1269 | return Math.min(prev, item.zIndex);
1270 | }, Infinity);
1271 | const maxFocusIndex = focus.reduce((prev, item) => {
1272 | return Math.max(prev, item.zIndex);
1273 | }, -Infinity);
1274 | const minFocusIndex = focus.reduce((prev, item) => {
1275 | return Math.min(prev, item.zIndex);
1276 | }, Infinity);
1277 | let dur = maxFocusIndex - minUnFocusIndex + 1;
1278 | if (dur >= 0) {
1279 | dur++;
1280 | focus.forEach(block => block.zIndex = block.zIndex - dur);
1281 | if (minFocusIndex - dur < 0) {
1282 | dur = dur - minFocusIndex;
1283 | value.blocks.forEach(block => block.zIndex = block.zIndex + dur);
1284 | }
1285 | }
1286 | return value.blocks;
1287 | }))()()
1288 | };
1289 |
1290 | return {
1291 | redo: () => updateBlocks(deepcopy(data.after)),
1292 | undo: () => updateBlocks(deepcopy(data.before))
1293 | };
1294 | }
1295 | });
1296 | ```
1297 |
1298 |
1299 |
1300 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/e33501344a0ffc7d3fc5971823dfbbdd25c1a280)
1301 |
1302 |
1303 |
1304 | # 十三、注册全选快捷键命令
1305 |
1306 | ```ts
1307 | // VisualEditor.command.tsx
1308 |
1309 | // 注册全选快捷键命令
1310 | commander.useRegistry({
1311 | name: 'selectAll',
1312 | keyboard: ['ctrl+a'],
1313 | followQueue: false,
1314 | execute: () => {
1315 | return {
1316 | redo: () => {
1317 | value.blocks.forEach(block => block.focus = true);
1318 | updateBlocks(value.blocks);
1319 | }
1320 | }
1321 | }
1322 | });
1323 | ```
1324 |
1325 |
1326 |
1327 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/363abf243b27cd27be17a1a9986dcf2d80e3f828)
1328 |
1329 |
1330 |
1331 | # 十四、操作栏导入、导出
1332 |
1333 |
1334 |
1335 | - 注册导入更新命令
1336 |
1337 | ```ts
1338 | // VisualEditor.command.tsx
1339 |
1340 | // 注册导入数据时,更新数据命令
1341 | commander.useRegistry({
1342 | name: 'updateValue',
1343 | execute: (newModelValue: VisualEditorValue) => {
1344 | const data = {
1345 | before: deepcopy(value),
1346 | after: deepcopy(newModelValue)
1347 | };
1348 | return {
1349 | redo: () => updateValue(data.after),
1350 | undo: () => updateValue(data.before)
1351 | }
1352 | }
1353 | });
1354 | ```
1355 |
1356 | - 导入、导出弹框
1357 |
1358 | ```tsx
1359 | // $$dialog.tsx
1360 |
1361 | export enum DialogEdit {
1362 | input = 'input',
1363 | textarea = 'textarea',
1364 | }
1365 |
1366 | interface DialogOption {
1367 | title?: string,
1368 | message?: string | (() => any),
1369 | confirmButton?: boolean
1370 | cancelButton?: boolean
1371 | editType?: DialogEdit,
1372 | editValue?: string,
1373 | editReadonly?: boolean
1374 | width?: string,
1375 | onConfirm?: (editValue?: string) => void,
1376 | onCancel?: () => void,
1377 | }
1378 |
1379 | interface DialogInstance {
1380 | show: (option?: DialogOption) => void,
1381 | close: () => void,
1382 | }
1383 |
1384 | const DialogComponent: React.FC<{ option?: DialogOption, onRef?: (ins: DialogInstance) => void }> = (props) => {
1385 |
1386 | let [option, setOption] = useState(props.option || {});
1387 | const [showFlag, setShowFlag] = useState(false);
1388 | const [editValue, setEditValue] = useState(option ? option.editValue : '');
1389 |
1390 | const methods = {
1391 | show: (option?: DialogOption) => {
1392 | setOption(deepcopy(option || {}));
1393 | setEditValue(!option ? '' : (option.editValue || ''));
1394 | setShowFlag(true);
1395 | },
1396 | close: () => setShowFlag(false),
1397 | }
1398 |
1399 | props.onRef && props.onRef(methods);
1400 |
1401 | const handler = {
1402 | onConfirm: () => {
1403 | option.onConfirm && option.onConfirm(editValue)
1404 | methods.close()
1405 | },
1406 | onCancel: () => {
1407 | option.onCancel && option.onCancel()
1408 | methods.close()
1409 | },
1410 | };
1411 |
1412 | const inputProps = {
1413 | value: editValue,
1414 | onChange: (e: React.ChangeEvent) => setEditValue(e.target.value),
1415 | readOnly: option.editReadonly === true,
1416 | };
1417 |
1418 | return (
1419 |
1427 | {option.cancelButton && }
1428 | {option.confirmButton && }
1429 | >
1430 | ) : null}
1431 | >
1432 | {option.message}
1433 | {option.editType === DialogEdit.input && (
1434 |
1435 | )}
1436 | {option.editType === DialogEdit.textarea && (
1437 |
1438 | )}
1439 |
1440 | )
1441 |
1442 | }
1443 |
1444 | const getInstance = (() => {
1445 | let ins: null | DialogInstance = null;
1446 | return (option?: DialogOption) => {
1447 | if (!ins) {
1448 | const el = document.createElement('div');
1449 | document.body.appendChild(el);
1450 | ReactDOM.render( ins = val} />, el);
1451 | }
1452 | return ins!;
1453 | }
1454 | })();
1455 |
1456 | const DialogService = (option?: DialogOption) => {
1457 | const ins = getInstance(option);
1458 | ins.show(option);
1459 | }
1460 |
1461 | export const $$dialog = Object.assign(DialogService, {
1462 | textarea: (val?: string, option?: DialogOption) => {
1463 | const dfd = defer();
1464 | option = option || {};
1465 | option.editType = DialogEdit.textarea;
1466 | option.editValue = val;
1467 | if (option.editReadonly !== true) {
1468 | option.confirmButton = true;
1469 | option.cancelButton = true;
1470 | option.onConfirm = dfd.resolve;
1471 | }
1472 | DialogService(option);
1473 | return dfd.promise;
1474 | },
1475 | input: (val?: string, option?: DialogOption) => {
1476 | // TODO
1477 | console.log(val, option)
1478 | },
1479 | })
1480 | ```
1481 |
1482 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/411da79dbbfa918fc79708c210c3fa5cc696f29a)
1483 |
1484 |
1485 |
1486 | # 十五、右键菜单
1487 |
1488 | - 菜单实现
1489 |
1490 | ```tsx
1491 | // src/packages/service/dropdown/$$dropdown.tsx
1492 |
1493 | import ReactDOM from "react-dom";
1494 | import { MouseEvent, useContext, useEffect, useMemo, useRef, useState } from 'react';
1495 | import './$$dropdown.scss';
1496 | import { useCallbackRef } from '../../hook/useCallbackRef';
1497 |
1498 | type Reference = { x: number, y: number } | MouseEvent | HTMLElement;
1499 | type Render = JSX.Element | JSX.Element[] | React.ReactFragment;
1500 |
1501 | interface DropdownOption {
1502 | reference: Reference,
1503 | render: () => Render,
1504 | }
1505 |
1506 | const DropdownContext = React.createContext<{onClick: () => void}>({} as any);
1507 |
1508 | const DropdownComponent: React.FC<{
1509 | option: DropdownOption,
1510 | onRef: (ins: { show: (opt: DropdownOption) => void, close: () => void }) => void
1511 | }> = (props) => {
1512 |
1513 | const elRef = useRef({} as HTMLDivElement);
1514 |
1515 | const [option, setOption] = useState(props.option);
1516 | const [showFlag, setShowFlag] = useState(false);
1517 |
1518 | const styles = useMemo(() => {
1519 | let x = 0, y = 0;
1520 | const reference = option.reference
1521 | if ('target' in reference) {
1522 | x = reference.clientX - 20;
1523 | y = reference.clientY - 20;
1524 | } else if ('addEventListener' in reference) {
1525 | const { top, left, height } = reference.getBoundingClientRect();
1526 | x = left;
1527 | y = top + height;
1528 | } else {
1529 | x = reference.x;
1530 | y = reference.y;
1531 | }
1532 | return {
1533 | left: `${x + 20}px`,
1534 | top: `${y + 20}px`,
1535 | display: showFlag ? 'inline-block' : 'none'
1536 | }
1537 | }, [option?.reference, showFlag]);
1538 |
1539 | const methods = {
1540 | show: (opt: DropdownOption) => {
1541 | setOption(opt);
1542 | setShowFlag(true);
1543 | },
1544 | close: () => {
1545 | setShowFlag(false);
1546 | }
1547 | };
1548 |
1549 | const handler = {
1550 | onClickBody: useCallbackRef((ev: MouseEvent) => {
1551 | if (elRef.current.contains(ev.target as Node)) {
1552 | /*点击了dropdown content*/
1553 | return;
1554 | } else {
1555 | methods.close();
1556 | }
1557 | }),
1558 | onClickDropItem: useCallbackRef(() => {
1559 | methods.close();
1560 | })
1561 | };
1562 |
1563 | props.onRef!(methods);
1564 |
1565 | useEffect(() => {
1566 | document.body.addEventListener('click', handler.onClickBody as any);
1567 | return () => {
1568 | document.body.removeEventListener('click', handler.onClickBody as any);
1569 | }
1570 | }, [])
1571 |
1572 | return (<>
1573 |
1574 |
1575 | {option?.render()}
1576 |
1577 |
1578 | >);
1579 | }
1580 |
1581 | export const DropdownOption: React.FC<{
1582 | onClick?: (ev: React.MouseEvent) => void
1583 | icon?: string,
1584 | label?: string
1585 | }> = (props) => {
1586 |
1587 | const dropdown = useContext(DropdownContext);
1588 | const handler = {
1589 | onClick: (e: React.MouseEvent) => {
1590 | dropdown.onClick();
1591 | props.onClick && props.onClick(e);
1592 | }
1593 | };
1594 |
1595 | return (
1596 | {props.icon && }
1597 | {props.label && {props.label}}
1598 | {/* {props.children} 这个时元素默认的标签内的内容*/}
1599 |
);
1600 | }
1601 |
1602 | export const $$dropdown = (() => {
1603 |
1604 | let ins: any;
1605 | return (options: DropdownOption) => {
1606 | if (!ins) {
1607 | const el = document.createElement('div');
1608 | document.body.appendChild(el);
1609 | ReactDOM.render( ins = val} />, el);
1610 | }
1611 |
1612 | ins.show(options);
1613 | }
1614 | })();
1615 |
1616 |
1617 | ```
1618 |
1619 |
1620 |
1621 | - 注册事件监听并显示
1622 |
1623 | ```tsx
1624 | // src/packages/VisualEditor.tsx
1625 |
1626 | const handler = {
1627 | onContextMenuBlock: (ev: React.MouseEvent, block: VisualEditorBlockData) => {
1628 | ev.preventDefault();
1629 | ev.stopPropagation();
1630 |
1631 |
1632 | $$dropdown({
1633 | reference: ev.nativeEvent,
1634 | render: () => {
1635 | return (<>
1636 |
1637 |
1638 |
1639 | methods.showBlockData(block)}}/>
1640 | methods.importBlockData(block)}}/>
1641 | >)
1642 | }
1643 | });
1644 | }
1645 | };
1646 | ```
1647 |
1648 | [传送门](https://github.com/lyios8859-1/react-app-editor-project/commit/6ef3c7eb6a2c65ce7d3661cd592b12c6bddbcc03)
1649 |
1650 | ***效果图***
1651 |
1652 | 
1653 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@ant-design/colors@^6.0.0":
6 | version "6.0.0"
7 | resolved "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298"
8 | integrity sha1-m5NmJXz/zEfbQrnQIDu1ksE8Apg=
9 | dependencies:
10 | "@ctrl/tinycolor" "^3.4.0"
11 |
12 | "@ant-design/icons-svg@^4.0.0":
13 | version "4.1.0"
14 | resolved "https://registry.nlark.com/@ant-design/icons-svg/download/@ant-design/icons-svg-4.1.0.tgz#480b025f4b20ef7fe8f47d4a4846e4fee84ea06c"
15 | integrity sha1-SAsCX0sg73/o9H1KSEbk/uhOoGw=
16 |
17 | "@ant-design/icons@^4.5.0", "@ant-design/icons@^4.6.3":
18 | version "4.6.4"
19 | resolved "https://registry.nlark.com/@ant-design/icons/download/@ant-design/icons-4.6.4.tgz#21b037dbb90ee1bb7c632cca057006e57d992fd9"
20 | integrity sha1-IbA327kO4bt8YyzKBXAG5X2ZL9k=
21 | dependencies:
22 | "@ant-design/colors" "^6.0.0"
23 | "@ant-design/icons-svg" "^4.0.0"
24 | "@babel/runtime" "^7.11.2"
25 | classnames "^2.2.6"
26 | rc-util "^5.9.4"
27 |
28 | "@ant-design/react-slick@~0.28.1":
29 | version "0.28.4"
30 | resolved "https://registry.nlark.com/@ant-design/react-slick/download/@ant-design/react-slick-0.28.4.tgz?cache=0&sync_timestamp=1629256314194&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40ant-design%2Freact-slick%2Fdownload%2F%40ant-design%2Freact-slick-0.28.4.tgz#8b296b87ad7c7ae877f2a527b81b7eebd9dd29a9"
31 | integrity sha1-iylrh618euh38qUnuBt+69ndKak=
32 | dependencies:
33 | "@babel/runtime" "^7.10.4"
34 | classnames "^2.2.5"
35 | json2mq "^0.2.0"
36 | lodash "^4.17.21"
37 | resize-observer-polyfill "^1.5.0"
38 |
39 | "@babel/code-frame@^7.14.5":
40 | version "7.14.5"
41 | resolved "https://registry.nlark.com/@babel/code-frame/download/@babel/code-frame-7.14.5.tgz?cache=0&sync_timestamp=1623280394200&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
42 | integrity sha1-I7CNdA6D9JxeWZRfvxtD6Au/Tts=
43 | dependencies:
44 | "@babel/highlight" "^7.14.5"
45 |
46 | "@babel/compat-data@^7.15.0":
47 | version "7.15.0"
48 | resolved "https://registry.nlark.com/@babel/compat-data/download/@babel/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
49 | integrity sha1-Lbr4uFM0eWyvuw9Xk6kKL8AQsXY=
50 |
51 | "@babel/core@^7.14.8":
52 | version "7.15.5"
53 | resolved "https://registry.nlark.com/@babel/core/download/@babel/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9"
54 | integrity sha1-+O2aznMHIlRGCfkMm7SRYtw79bk=
55 | dependencies:
56 | "@babel/code-frame" "^7.14.5"
57 | "@babel/generator" "^7.15.4"
58 | "@babel/helper-compilation-targets" "^7.15.4"
59 | "@babel/helper-module-transforms" "^7.15.4"
60 | "@babel/helpers" "^7.15.4"
61 | "@babel/parser" "^7.15.5"
62 | "@babel/template" "^7.15.4"
63 | "@babel/traverse" "^7.15.4"
64 | "@babel/types" "^7.15.4"
65 | convert-source-map "^1.7.0"
66 | debug "^4.1.0"
67 | gensync "^1.0.0-beta.2"
68 | json5 "^2.1.2"
69 | semver "^6.3.0"
70 | source-map "^0.5.0"
71 |
72 | "@babel/generator@^7.12.11", "@babel/generator@^7.15.4":
73 | version "7.15.4"
74 | resolved "https://registry.nlark.com/@babel/generator/download/@babel/generator-7.15.4.tgz?cache=0&sync_timestamp=1630618918440&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fgenerator%2Fdownload%2F%40babel%2Fgenerator-7.15.4.tgz#85acb159a267ca6324f9793986991ee2022a05b0"
75 | integrity sha1-hayxWaJnymMk+Xk5hpke4gIqBbA=
76 | dependencies:
77 | "@babel/types" "^7.15.4"
78 | jsesc "^2.5.1"
79 | source-map "^0.5.0"
80 |
81 | "@babel/helper-compilation-targets@^7.15.4":
82 | version "7.15.4"
83 | resolved "https://registry.nlark.com/@babel/helper-compilation-targets/download/@babel/helper-compilation-targets-7.15.4.tgz?cache=0&sync_timestamp=1630618788550&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-compilation-targets%2Fdownload%2F%40babel%2Fhelper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9"
84 | integrity sha1-z22U8w++/BORI+J91rAvZa7tt7k=
85 | dependencies:
86 | "@babel/compat-data" "^7.15.0"
87 | "@babel/helper-validator-option" "^7.14.5"
88 | browserslist "^4.16.6"
89 | semver "^6.3.0"
90 |
91 | "@babel/helper-function-name@^7.15.4":
92 | version "7.15.4"
93 | resolved "https://registry.nlark.com/@babel/helper-function-name/download/@babel/helper-function-name-7.15.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-function-name%2Fdownload%2F%40babel%2Fhelper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc"
94 | integrity sha1-hFdE2vxDgaSl+2r6bD02+Yp4frw=
95 | dependencies:
96 | "@babel/helper-get-function-arity" "^7.15.4"
97 | "@babel/template" "^7.15.4"
98 | "@babel/types" "^7.15.4"
99 |
100 | "@babel/helper-get-function-arity@^7.15.4":
101 | version "7.15.4"
102 | resolved "https://registry.nlark.com/@babel/helper-get-function-arity/download/@babel/helper-get-function-arity-7.15.4.tgz?cache=0&sync_timestamp=1630618916983&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-get-function-arity%2Fdownload%2F%40babel%2Fhelper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b"
103 | integrity sha1-CYgYk0oTf854tTaj4BWGS+Hih5s=
104 | dependencies:
105 | "@babel/types" "^7.15.4"
106 |
107 | "@babel/helper-hoist-variables@^7.15.4":
108 | version "7.15.4"
109 | resolved "https://registry.nlark.com/@babel/helper-hoist-variables/download/@babel/helper-hoist-variables-7.15.4.tgz?cache=0&sync_timestamp=1630618919536&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-hoist-variables%2Fdownload%2F%40babel%2Fhelper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df"
110 | integrity sha1-CZk6MlnA6Rj5nRBCYd/fwDPxeN8=
111 | dependencies:
112 | "@babel/types" "^7.15.4"
113 |
114 | "@babel/helper-member-expression-to-functions@^7.15.4":
115 | version "7.15.4"
116 | resolved "https://registry.nlark.com/@babel/helper-member-expression-to-functions/download/@babel/helper-member-expression-to-functions-7.15.4.tgz?cache=0&sync_timestamp=1630618921004&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-member-expression-to-functions%2Fdownload%2F%40babel%2Fhelper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef"
117 | integrity sha1-v9NNybupgkpGWLAxfsL9VxpR5u8=
118 | dependencies:
119 | "@babel/types" "^7.15.4"
120 |
121 | "@babel/helper-module-imports@^7.12.5", "@babel/helper-module-imports@^7.15.4":
122 | version "7.15.4"
123 | resolved "https://registry.nlark.com/@babel/helper-module-imports/download/@babel/helper-module-imports-7.15.4.tgz?cache=0&sync_timestamp=1630619202866&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-module-imports%2Fdownload%2F%40babel%2Fhelper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f"
124 | integrity sha1-4YAH0jBjLeoZtHhTuYRHbntOED8=
125 | dependencies:
126 | "@babel/types" "^7.15.4"
127 |
128 | "@babel/helper-module-transforms@^7.15.4":
129 | version "7.15.7"
130 | resolved "https://registry.nlark.com/@babel/helper-module-transforms/download/@babel/helper-module-transforms-7.15.7.tgz?cache=0&sync_timestamp=1631920857520&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-module-transforms%2Fdownload%2F%40babel%2Fhelper-module-transforms-7.15.7.tgz#7da80c8cbc1f02655d83f8b79d25866afe50d226"
131 | integrity sha1-fagMjLwfAmVdg/i3nSWGav5Q0iY=
132 | dependencies:
133 | "@babel/helper-module-imports" "^7.15.4"
134 | "@babel/helper-replace-supers" "^7.15.4"
135 | "@babel/helper-simple-access" "^7.15.4"
136 | "@babel/helper-split-export-declaration" "^7.15.4"
137 | "@babel/helper-validator-identifier" "^7.15.7"
138 | "@babel/template" "^7.15.4"
139 | "@babel/traverse" "^7.15.4"
140 | "@babel/types" "^7.15.6"
141 |
142 | "@babel/helper-optimise-call-expression@^7.15.4":
143 | version "7.15.4"
144 | resolved "https://registry.nlark.com/@babel/helper-optimise-call-expression/download/@babel/helper-optimise-call-expression-7.15.4.tgz?cache=0&sync_timestamp=1630619283276&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-optimise-call-expression%2Fdownload%2F%40babel%2Fhelper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171"
145 | integrity sha1-8xClEho7nMUtmrGRIr1ymCLe4XE=
146 | dependencies:
147 | "@babel/types" "^7.15.4"
148 |
149 | "@babel/helper-plugin-utils@^7.14.5":
150 | version "7.14.5"
151 | resolved "https://registry.nlark.com/@babel/helper-plugin-utils/download/@babel/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
152 | integrity sha1-WsgizpfuxGdBq3ClF5ceRDpwxak=
153 |
154 | "@babel/helper-replace-supers@^7.15.4":
155 | version "7.15.4"
156 | resolved "https://registry.nlark.com/@babel/helper-replace-supers/download/@babel/helper-replace-supers-7.15.4.tgz?cache=0&sync_timestamp=1630618924259&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-replace-supers%2Fdownload%2F%40babel%2Fhelper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a"
157 | integrity sha1-UqirJrqRjH9t7ihiiwcHGse3NHo=
158 | dependencies:
159 | "@babel/helper-member-expression-to-functions" "^7.15.4"
160 | "@babel/helper-optimise-call-expression" "^7.15.4"
161 | "@babel/traverse" "^7.15.4"
162 | "@babel/types" "^7.15.4"
163 |
164 | "@babel/helper-simple-access@^7.15.4":
165 | version "7.15.4"
166 | resolved "https://registry.nlark.com/@babel/helper-simple-access/download/@babel/helper-simple-access-7.15.4.tgz?cache=0&sync_timestamp=1630619204668&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-simple-access%2Fdownload%2F%40babel%2Fhelper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b"
167 | integrity sha1-rDaJBavx3o6XgUNLY12PhnS8wTs=
168 | dependencies:
169 | "@babel/types" "^7.15.4"
170 |
171 | "@babel/helper-split-export-declaration@^7.15.4":
172 | version "7.15.4"
173 | resolved "https://registry.nlark.com/@babel/helper-split-export-declaration/download/@babel/helper-split-export-declaration-7.15.4.tgz?cache=0&sync_timestamp=1630618922438&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-split-export-declaration%2Fdownload%2F%40babel%2Fhelper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257"
174 | integrity sha1-rsq5Lc2+9qEKo7YqsgSwhfd24lc=
175 | dependencies:
176 | "@babel/types" "^7.15.4"
177 |
178 | "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7":
179 | version "7.15.7"
180 | resolved "https://registry.nlark.com/@babel/helper-validator-identifier/download/@babel/helper-validator-identifier-7.15.7.tgz?cache=0&sync_timestamp=1631920857390&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhelper-validator-identifier%2Fdownload%2F%40babel%2Fhelper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
181 | integrity sha1-Ig35k7/pBKSmsCq08zhaXr9uI4k=
182 |
183 | "@babel/helper-validator-option@^7.14.5":
184 | version "7.14.5"
185 | resolved "https://registry.nlark.com/@babel/helper-validator-option/download/@babel/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
186 | integrity sha1-bnKh//GNXfy4eOHmLxoCHEty1aM=
187 |
188 | "@babel/helpers@^7.15.4":
189 | version "7.15.4"
190 | resolved "https://registry.nlark.com/@babel/helpers/download/@babel/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43"
191 | integrity sha1-X0DwIFCjAnEho89I1JfAXFVer0M=
192 | dependencies:
193 | "@babel/template" "^7.15.4"
194 | "@babel/traverse" "^7.15.4"
195 | "@babel/types" "^7.15.4"
196 |
197 | "@babel/highlight@^7.14.5":
198 | version "7.14.5"
199 | resolved "https://registry.nlark.com/@babel/highlight/download/@babel/highlight-7.14.5.tgz?cache=0&sync_timestamp=1623280393681&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fhighlight%2Fdownload%2F%40babel%2Fhighlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
200 | integrity sha1-aGGlLwOWZAUAH2qlNKAaJNmejNk=
201 | dependencies:
202 | "@babel/helper-validator-identifier" "^7.14.5"
203 | chalk "^2.0.0"
204 | js-tokens "^4.0.0"
205 |
206 | "@babel/parser@^7.12.11", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5":
207 | version "7.15.7"
208 | resolved "https://registry.nlark.com/@babel/parser/download/@babel/parser-7.15.7.tgz?cache=0&sync_timestamp=1631920857646&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fparser%2Fdownload%2F%40babel%2Fparser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae"
209 | integrity sha1-DD7UousHsWXfqFs8xFxyczTE7a4=
210 |
211 | "@babel/plugin-transform-react-jsx-self@^7.14.5":
212 | version "7.14.9"
213 | resolved "https://registry.nlark.com/@babel/plugin-transform-react-jsx-self/download/@babel/plugin-transform-react-jsx-self-7.14.9.tgz?cache=0&sync_timestamp=1627804694133&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fplugin-transform-react-jsx-self%2Fdownload%2F%40babel%2Fplugin-transform-react-jsx-self-7.14.9.tgz#33041e665453391eb6ee54a2ecf3ba1d46bd30f4"
214 | integrity sha1-MwQeZlRTOR627lSi7PO6HUa9MPQ=
215 | dependencies:
216 | "@babel/helper-plugin-utils" "^7.14.5"
217 |
218 | "@babel/plugin-transform-react-jsx-source@^7.14.5":
219 | version "7.14.5"
220 | resolved "https://registry.nlark.com/@babel/plugin-transform-react-jsx-source/download/@babel/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d"
221 | integrity sha1-efco5g5tvTGiuGCwv2yXZZGKzx0=
222 | dependencies:
223 | "@babel/helper-plugin-utils" "^7.14.5"
224 |
225 | "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
226 | version "7.15.4"
227 | resolved "https://registry.nlark.com/@babel/runtime/download/@babel/runtime-7.15.4.tgz?cache=0&sync_timestamp=1630618785994&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
228 | integrity sha1-/RfRa/34eObdAtGXU6OfqKjZyEo=
229 | dependencies:
230 | regenerator-runtime "^0.13.4"
231 |
232 | "@babel/template@^7.15.4":
233 | version "7.15.4"
234 | resolved "https://registry.nlark.com/@babel/template/download/@babel/template-7.15.4.tgz?cache=0&sync_timestamp=1630618922172&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Ftemplate%2Fdownload%2F%40babel%2Ftemplate-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194"
235 | integrity sha1-UYmNNdzz+qZwxO5q/P1RfuE58ZQ=
236 | dependencies:
237 | "@babel/code-frame" "^7.14.5"
238 | "@babel/parser" "^7.15.4"
239 | "@babel/types" "^7.15.4"
240 |
241 | "@babel/traverse@^7.12.12", "@babel/traverse@^7.15.4":
242 | version "7.15.4"
243 | resolved "https://registry.nlark.com/@babel/traverse/download/@babel/traverse-7.15.4.tgz?cache=0&sync_timestamp=1630618923983&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Ftraverse%2Fdownload%2F%40babel%2Ftraverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d"
244 | integrity sha1-/4UQNnoUS/v/VS2eGOKPPiiJwi0=
245 | dependencies:
246 | "@babel/code-frame" "^7.14.5"
247 | "@babel/generator" "^7.15.4"
248 | "@babel/helper-function-name" "^7.15.4"
249 | "@babel/helper-hoist-variables" "^7.15.4"
250 | "@babel/helper-split-export-declaration" "^7.15.4"
251 | "@babel/parser" "^7.15.4"
252 | "@babel/types" "^7.15.4"
253 | debug "^4.1.0"
254 | globals "^11.1.0"
255 |
256 | "@babel/types@^7.12.12", "@babel/types@^7.15.4", "@babel/types@^7.15.6":
257 | version "7.15.6"
258 | resolved "https://registry.nlark.com/@babel/types/download/@babel/types-7.15.6.tgz?cache=0&sync_timestamp=1631216248664&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Ftypes%2Fdownload%2F%40babel%2Ftypes-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f"
259 | integrity sha1-mavcSCGLKIHAWN0KerBbmcm+dY8=
260 | dependencies:
261 | "@babel/helper-validator-identifier" "^7.14.9"
262 | to-fast-properties "^2.0.0"
263 |
264 | "@ctrl/tinycolor@^3.4.0":
265 | version "3.4.0"
266 | resolved "https://registry.npm.taobao.org/@ctrl/tinycolor/download/@ctrl/tinycolor-3.4.0.tgz?cache=0&sync_timestamp=1612895880147&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ctrl%2Ftinycolor%2Fdownload%2F%40ctrl%2Ftinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
267 | integrity sha1-w8WuVDyJfKqcKmhjC+01W+X5mQ8=
268 |
269 | "@icons/material@^0.2.4":
270 | version "0.2.4"
271 | resolved "https://registry.npm.taobao.org/@icons/material/download/@icons/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
272 | integrity sha1-6QyfcXaLNzbnbX3WeD/Gwq+oi8g=
273 |
274 | "@rollup/pluginutils@^4.1.1":
275 | version "4.1.1"
276 | resolved "https://registry.nlark.com/@rollup/pluginutils/download/@rollup/pluginutils-4.1.1.tgz?cache=0&sync_timestamp=1626393703548&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40rollup%2Fpluginutils%2Fdownload%2F%40rollup%2Fpluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec"
277 | integrity sha1-HU2obdTt7RVlalfZM/2iuaCNR+w=
278 | dependencies:
279 | estree-walker "^2.0.1"
280 | picomatch "^2.2.2"
281 |
282 | "@types/classnames@^2.2.11":
283 | version "2.3.1"
284 | resolved "https://registry.nlark.com/@types/classnames/download/@types/classnames-2.3.1.tgz?cache=0&sync_timestamp=1618929797687&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fclassnames%2Fdownload%2F%40types%2Fclassnames-2.3.1.tgz#3c2467aa0f1a93f1f021e3b9bcf938bd5dfdc0dd"
285 | integrity sha1-PCRnqg8ak/HwIeO5vPk4vV39wN0=
286 | dependencies:
287 | classnames "*"
288 |
289 | "@types/node@^14.14.37":
290 | version "14.17.18"
291 | resolved "https://registry.nlark.com/@types/node/download/@types/node-14.17.18.tgz?cache=0&sync_timestamp=1632251307629&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.17.18.tgz#0198489a751005f71217744aa966cd1f29447c81"
292 | integrity sha1-AZhImnUQBfcSF3RKqWbNHylEfIE=
293 |
294 | "@types/prop-types@*":
295 | version "15.7.4"
296 | resolved "https://registry.nlark.com/@types/prop-types/download/@types/prop-types-15.7.4.tgz?cache=0&sync_timestamp=1629708737049&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fprop-types%2Fdownload%2F%40types%2Fprop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
297 | integrity sha1-/PcgXCXf95Xuea8eMNosl5CAjxE=
298 |
299 | "@types/react-color@^3.0.4":
300 | version "3.0.5"
301 | resolved "https://registry.nlark.com/@types/react-color/download/@types/react-color-3.0.5.tgz?cache=0&sync_timestamp=1629708935609&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Freact-color%2Fdownload%2F%40types%2Freact-color-3.0.5.tgz#b8bdf8df7085bd1577658fb37d9a18d7ba3963bb"
302 | integrity sha1-uL3433CFvRV3ZY+zfZoY17o5Y7s=
303 | dependencies:
304 | "@types/react" "*"
305 | "@types/reactcss" "*"
306 |
307 | "@types/react-dom@^17.0.1":
308 | version "17.0.9"
309 | resolved "https://registry.nlark.com/@types/react-dom/download/@types/react-dom-17.0.9.tgz?cache=0&sync_timestamp=1629708935856&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Freact-dom%2Fdownload%2F%40types%2Freact-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add"
310 | integrity sha1-RBqYHanXvhFwQuGm/T2sSzD1Wt0=
311 | dependencies:
312 | "@types/react" "*"
313 |
314 | "@types/react@*", "@types/react@^17.0.2":
315 | version "17.0.24"
316 | resolved "https://registry.nlark.com/@types/react/download/@types/react-17.0.24.tgz#7e1b3f78d0fc53782543f9bce6d949959a5880bd"
317 | integrity sha1-fhs/eND8U3glQ/m85tlJlZpYgL0=
318 | dependencies:
319 | "@types/prop-types" "*"
320 | "@types/scheduler" "*"
321 | csstype "^3.0.2"
322 |
323 | "@types/reactcss@*":
324 | version "1.2.4"
325 | resolved "https://registry.nlark.com/@types/reactcss/download/@types/reactcss-1.2.4.tgz?cache=0&sync_timestamp=1629709158829&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Freactcss%2Fdownload%2F%40types%2Freactcss-1.2.4.tgz#66c5f6afe123ffa1a50dbe724aa1fe68eb9fab00"
326 | integrity sha1-ZsX2r+Ej/6GlDb5ySqH+aOufqwA=
327 | dependencies:
328 | "@types/react" "*"
329 |
330 | "@types/scheduler@*":
331 | version "0.16.2"
332 | resolved "https://registry.nlark.com/@types/scheduler/download/@types/scheduler-0.16.2.tgz?cache=0&sync_timestamp=1629709152024&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fscheduler%2Fdownload%2F%40types%2Fscheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
333 | integrity sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk=
334 |
335 | "@vitejs/plugin-react-refresh@^1.3.1":
336 | version "1.3.6"
337 | resolved "https://registry.nlark.com/@vitejs/plugin-react-refresh/download/@vitejs/plugin-react-refresh-1.3.6.tgz#19818392db01e81746cfeb04e096ab3010e79fe3"
338 | integrity sha1-GYGDktsB6BdGz+sE4JarMBDnn+M=
339 | dependencies:
340 | "@babel/core" "^7.14.8"
341 | "@babel/plugin-transform-react-jsx-self" "^7.14.5"
342 | "@babel/plugin-transform-react-jsx-source" "^7.14.5"
343 | "@rollup/pluginutils" "^4.1.1"
344 | react-refresh "^0.10.0"
345 |
346 | ansi-styles@^3.2.1:
347 | version "3.2.1"
348 | resolved "https://registry.nlark.com/ansi-styles/download/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
349 | integrity sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=
350 | dependencies:
351 | color-convert "^1.9.0"
352 |
353 | antd@^4.15.0:
354 | version "4.16.13"
355 | resolved "https://registry.nlark.com/antd/download/antd-4.16.13.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fantd%2Fdownload%2Fantd-4.16.13.tgz#e9b9b4a590db28747aae1cab98981649a35880af"
356 | integrity sha1-6bm0pZDbKHR6rhyrmJgWSaNYgK8=
357 | dependencies:
358 | "@ant-design/colors" "^6.0.0"
359 | "@ant-design/icons" "^4.6.3"
360 | "@ant-design/react-slick" "~0.28.1"
361 | "@babel/runtime" "^7.12.5"
362 | array-tree-filter "^2.1.0"
363 | classnames "^2.2.6"
364 | copy-to-clipboard "^3.2.0"
365 | lodash "^4.17.21"
366 | moment "^2.25.3"
367 | rc-cascader "~1.4.0"
368 | rc-checkbox "~2.3.0"
369 | rc-collapse "~3.1.0"
370 | rc-dialog "~8.6.0"
371 | rc-drawer "~4.3.0"
372 | rc-dropdown "~3.2.0"
373 | rc-field-form "~1.20.0"
374 | rc-image "~5.2.5"
375 | rc-input-number "~7.1.0"
376 | rc-mentions "~1.6.1"
377 | rc-menu "~9.0.12"
378 | rc-motion "^2.4.0"
379 | rc-notification "~4.5.7"
380 | rc-pagination "~3.1.9"
381 | rc-picker "~2.5.10"
382 | rc-progress "~3.1.0"
383 | rc-rate "~2.9.0"
384 | rc-resize-observer "^1.0.0"
385 | rc-select "~12.1.6"
386 | rc-slider "~9.7.1"
387 | rc-steps "~4.1.0"
388 | rc-switch "~3.2.0"
389 | rc-table "~7.15.1"
390 | rc-tabs "~11.10.0"
391 | rc-textarea "~0.3.0"
392 | rc-tooltip "~5.1.1"
393 | rc-tree "~4.2.1"
394 | rc-tree-select "~4.3.0"
395 | rc-trigger "^5.2.10"
396 | rc-upload "~4.3.0"
397 | rc-util "^5.13.1"
398 | scroll-into-view-if-needed "^2.2.25"
399 |
400 | anymatch@~3.1.2:
401 | version "3.1.2"
402 | resolved "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
403 | integrity sha1-wFV8CWrzLxBhmPT04qODU343hxY=
404 | dependencies:
405 | normalize-path "^3.0.0"
406 | picomatch "^2.0.4"
407 |
408 | array-tree-filter@^2.1.0:
409 | version "2.1.0"
410 | resolved "https://registry.nlark.com/array-tree-filter/download/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
411 | integrity sha1-hzrAD+yDdJ8lWsjdCDgUtPYykZA=
412 |
413 | async-validator@^3.0.3:
414 | version "3.5.2"
415 | resolved "https://registry.nlark.com/async-validator/download/async-validator-3.5.2.tgz?cache=0&sync_timestamp=1630393256517&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync-validator%2Fdownload%2Fasync-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500"
416 | integrity sha1-aOhmqWgk6LJpT/eoMcGiXETV5QA=
417 |
418 | binary-extensions@^2.0.0:
419 | version "2.2.0"
420 | resolved "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.2.0.tgz?cache=0&sync_timestamp=1610299308660&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
421 | integrity sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=
422 |
423 | braces@~3.0.2:
424 | version "3.0.2"
425 | resolved "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
426 | integrity sha1-NFThpGLujVmeI23zNs2epPiv4Qc=
427 | dependencies:
428 | fill-range "^7.0.1"
429 |
430 | browserslist@^4.16.6:
431 | version "4.17.0"
432 | resolved "https://registry.nlark.com/browserslist/download/browserslist-4.17.0.tgz?cache=0&sync_timestamp=1630836439306&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbrowserslist%2Fdownload%2Fbrowserslist-4.17.0.tgz#1fcd81ec75b41d6d4994fb0831b92ac18c01649c"
433 | integrity sha1-H82B7HW0HW1JlPsIMbkqwYwBZJw=
434 | dependencies:
435 | caniuse-lite "^1.0.30001254"
436 | colorette "^1.3.0"
437 | electron-to-chromium "^1.3.830"
438 | escalade "^3.1.1"
439 | node-releases "^1.1.75"
440 |
441 | camel-case@^4.1.2:
442 | version "4.1.2"
443 | resolved "https://registry.npm.taobao.org/camel-case/download/camel-case-4.1.2.tgz?cache=0&sync_timestamp=1606867297052&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcamel-case%2Fdownload%2Fcamel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
444 | integrity sha1-lygHKpVPgFIoIlpt7qazhGHhvVo=
445 | dependencies:
446 | pascal-case "^3.1.2"
447 | tslib "^2.0.3"
448 |
449 | caniuse-lite@^1.0.30001254:
450 | version "1.0.30001259"
451 | resolved "https://registry.nlark.com/caniuse-lite/download/caniuse-lite-1.0.30001259.tgz?cache=0&sync_timestamp=1632203439345&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcaniuse-lite%2Fdownload%2Fcaniuse-lite-1.0.30001259.tgz#ae21691d3da9c4be6144403ac40f71d9f6efd790"
452 | integrity sha1-riFpHT2pxL5hREA6xA9x2fbv15A=
453 |
454 | capital-case@^1.0.4:
455 | version "1.0.4"
456 | resolved "https://registry.npm.taobao.org/capital-case/download/capital-case-1.0.4.tgz?cache=0&sync_timestamp=1606867325844&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcapital-case%2Fdownload%2Fcapital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
457 | integrity sha1-nRMCkjU8kkn2sA+lhSvuOKcX5mk=
458 | dependencies:
459 | no-case "^3.0.4"
460 | tslib "^2.0.3"
461 | upper-case-first "^2.0.2"
462 |
463 | chalk@^2.0.0:
464 | version "2.4.2"
465 | resolved "https://registry.nlark.com/chalk/download/chalk-2.4.2.tgz?cache=0&sync_timestamp=1627646655305&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fchalk%2Fdownload%2Fchalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
466 | integrity sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=
467 | dependencies:
468 | ansi-styles "^3.2.1"
469 | escape-string-regexp "^1.0.5"
470 | supports-color "^5.3.0"
471 |
472 | change-case@^4.1.2:
473 | version "4.1.2"
474 | resolved "https://registry.npm.taobao.org/change-case/download/change-case-4.1.2.tgz?cache=0&sync_timestamp=1606867326259&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchange-case%2Fdownload%2Fchange-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
475 | integrity sha1-/t/F8TYEXiOYwEEO5EH5VwRkHhI=
476 | dependencies:
477 | camel-case "^4.1.2"
478 | capital-case "^1.0.4"
479 | constant-case "^3.0.4"
480 | dot-case "^3.0.4"
481 | header-case "^2.0.4"
482 | no-case "^3.0.4"
483 | param-case "^3.0.4"
484 | pascal-case "^3.1.2"
485 | path-case "^3.0.4"
486 | sentence-case "^3.0.4"
487 | snake-case "^3.0.4"
488 | tslib "^2.0.3"
489 |
490 | "chokidar@>=3.0.0 <4.0.0":
491 | version "3.5.2"
492 | resolved "https://registry.nlark.com/chokidar/download/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
493 | integrity sha1-26OXb8rbAW9m/TZQIdkWANAcHnU=
494 | dependencies:
495 | anymatch "~3.1.2"
496 | braces "~3.0.2"
497 | glob-parent "~5.1.2"
498 | is-binary-path "~2.1.0"
499 | is-glob "~4.0.1"
500 | normalize-path "~3.0.0"
501 | readdirp "~3.6.0"
502 | optionalDependencies:
503 | fsevents "~2.3.2"
504 |
505 | classnames@*, classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6:
506 | version "2.3.1"
507 | resolved "https://registry.npm.taobao.org/classnames/download/classnames-2.3.1.tgz?cache=0&sync_timestamp=1617400318265&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fclassnames%2Fdownload%2Fclassnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
508 | integrity sha1-38+jiR4wbsHa0QXQ6I9EF7hTXo4=
509 |
510 | color-convert@^1.9.0:
511 | version "1.9.3"
512 | resolved "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
513 | integrity sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=
514 | dependencies:
515 | color-name "1.1.3"
516 |
517 | color-name@1.1.3:
518 | version "1.1.3"
519 | resolved "https://registry.nlark.com/color-name/download/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
520 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
521 |
522 | colorette@^1.2.2, colorette@^1.3.0:
523 | version "1.4.0"
524 | resolved "https://registry.nlark.com/colorette/download/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
525 | integrity sha1-UZD7uHJ2JZqGrXAL/yxtb6o/ykA=
526 |
527 | compute-scroll-into-view@^1.0.17:
528 | version "1.0.17"
529 | resolved "https://registry.npm.taobao.org/compute-scroll-into-view/download/compute-scroll-into-view-1.0.17.tgz?cache=0&sync_timestamp=1614042283471&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcompute-scroll-into-view%2Fdownload%2Fcompute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
530 | integrity sha1-aojxis2dQunPS6pr7H4FImB6t6s=
531 |
532 | constant-case@^3.0.4:
533 | version "3.0.4"
534 | resolved "https://registry.npm.taobao.org/constant-case/download/constant-case-3.0.4.tgz?cache=0&sync_timestamp=1606867325763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fconstant-case%2Fdownload%2Fconstant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
535 | integrity sha1-O4Sprq9M8x7EXmv13pG9+wWJ+vE=
536 | dependencies:
537 | no-case "^3.0.4"
538 | tslib "^2.0.3"
539 | upper-case "^2.0.2"
540 |
541 | convert-source-map@^1.7.0:
542 | version "1.8.0"
543 | resolved "https://registry.nlark.com/convert-source-map/download/convert-source-map-1.8.0.tgz?cache=0&sync_timestamp=1624045304679&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fconvert-source-map%2Fdownload%2Fconvert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
544 | integrity sha1-8zc8MtIbTXgN2ABFFGhPt5HKQ2k=
545 | dependencies:
546 | safe-buffer "~5.1.1"
547 |
548 | copy-anything@^2.0.1:
549 | version "2.0.3"
550 | resolved "https://registry.npm.taobao.org/copy-anything/download/copy-anything-2.0.3.tgz?cache=0&sync_timestamp=1612447490459&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-anything%2Fdownload%2Fcopy-anything-2.0.3.tgz#842407ba02466b0df844819bbe3baebbe5d45d87"
551 | integrity sha1-hCQHugJGaw34RIGbvjuuu+XUXYc=
552 | dependencies:
553 | is-what "^3.12.0"
554 |
555 | copy-to-clipboard@^3.2.0:
556 | version "3.3.1"
557 | resolved "https://registry.npm.taobao.org/copy-to-clipboard/download/copy-to-clipboard-3.3.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-to-clipboard%2Fdownload%2Fcopy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
558 | integrity sha1-EVqhqZmP+rYZb5MHatbaO5E2Yq4=
559 | dependencies:
560 | toggle-selection "^1.0.6"
561 |
562 | csstype@^3.0.2:
563 | version "3.0.9"
564 | resolved "https://registry.nlark.com/csstype/download/csstype-3.0.9.tgz?cache=0&sync_timestamp=1631540709509&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcsstype%2Fdownload%2Fcsstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
565 | integrity sha1-ZBCvMbJr0FIJM9AsvGT86c4/vws=
566 |
567 | date-fns@2.x:
568 | version "2.24.0"
569 | resolved "https://registry.nlark.com/date-fns/download/date-fns-2.24.0.tgz#7d86dc0d93c87b76b63d213b4413337cfd1c105d"
570 | integrity sha1-fYbcDZPIe3a2PSE7RBMzfP0cEF0=
571 |
572 | dayjs@1.x:
573 | version "1.10.7"
574 | resolved "https://registry.nlark.com/dayjs/download/dayjs-1.10.7.tgz?cache=0&sync_timestamp=1631266519235&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fdayjs%2Fdownload%2Fdayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468"
575 | integrity sha1-LPX5Gt0oEWdIRAhmoKHSbzps5Gg=
576 |
577 | debug@^3.2.6:
578 | version "3.2.7"
579 | resolved "https://registry.nlark.com/debug/download/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
580 | integrity sha1-clgLfpFF+zm2Z2+cXl+xALk0F5o=
581 | dependencies:
582 | ms "^2.1.1"
583 |
584 | debug@^4.1.0, debug@^4.3.2:
585 | version "4.3.2"
586 | resolved "https://registry.nlark.com/debug/download/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
587 | integrity sha1-8KScGKyHeeMdSgxgKd+3aHPHQos=
588 | dependencies:
589 | ms "2.1.2"
590 |
591 | deepcopy@^2.1.0:
592 | version "2.1.0"
593 | resolved "https://registry.npm.taobao.org/deepcopy/download/deepcopy-2.1.0.tgz#2deb0dd52d079c2ecb7924b640a7c3abd4db1d6d"
594 | integrity sha1-LesN1S0HnC7LeSS2QKfDq9TbHW0=
595 | dependencies:
596 | type-detect "^4.0.8"
597 |
598 | dom-align@^1.7.0:
599 | version "1.12.2"
600 | resolved "https://registry.nlark.com/dom-align/download/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b"
601 | integrity sha1-D4Fk69DJwhsMeQMQSTzYVYkqzUs=
602 |
603 | dot-case@^3.0.4:
604 | version "3.0.4"
605 | resolved "https://registry.npm.taobao.org/dot-case/download/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
606 | integrity sha1-mytnDQCkMWZ6inW6Kc0bmICc51E=
607 | dependencies:
608 | no-case "^3.0.4"
609 | tslib "^2.0.3"
610 |
611 | electron-to-chromium@^1.3.830:
612 | version "1.3.846"
613 | resolved "https://registry.nlark.com/electron-to-chromium/download/electron-to-chromium-1.3.846.tgz#a55fd59613dbcaed609e965e3e88f42b08c401d3"
614 | integrity sha1-pV/VlhPbyu1gnpZePoj0KwjEAdM=
615 |
616 | errno@^0.1.1:
617 | version "0.1.8"
618 | resolved "https://registry.npm.taobao.org/errno/download/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
619 | integrity sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8=
620 | dependencies:
621 | prr "~1.0.1"
622 |
623 | es-module-lexer@^0.7.1:
624 | version "0.7.1"
625 | resolved "https://registry.nlark.com/es-module-lexer/download/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
626 | integrity sha1-wsjg9G8t8GJ0za8N0/OzPgoLJn0=
627 |
628 | esbuild@^0.12.17:
629 | version "0.12.28"
630 | resolved "https://registry.nlark.com/esbuild/download/esbuild-0.12.28.tgz?cache=0&sync_timestamp=1631587318573&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fesbuild%2Fdownload%2Fesbuild-0.12.28.tgz#84da0d2a0d0dee181281545271e0d65cf6fab1ef"
631 | integrity sha1-hNoNKg0N7hgSgVRSceDWXPb6se8=
632 |
633 | escalade@^3.1.1:
634 | version "3.1.1"
635 | resolved "https://registry.npm.taobao.org/escalade/download/escalade-3.1.1.tgz?cache=0&sync_timestamp=1602567224085&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescalade%2Fdownload%2Fescalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
636 | integrity sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA=
637 |
638 | escape-string-regexp@^1.0.5:
639 | version "1.0.5"
640 | resolved "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz?cache=0&sync_timestamp=1618677243201&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescape-string-regexp%2Fdownload%2Fescape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
641 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
642 |
643 | estree-walker@^2.0.1:
644 | version "2.0.2"
645 | resolved "https://registry.npm.taobao.org/estree-walker/download/estree-walker-2.0.2.tgz?cache=0&sync_timestamp=1611956983677&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Festree-walker%2Fdownload%2Festree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
646 | integrity sha1-UvAQF4wqTBF6d1fP6UKtt9LaTKw=
647 |
648 | fill-range@^7.0.1:
649 | version "7.0.1"
650 | resolved "https://registry.nlark.com/fill-range/download/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
651 | integrity sha1-GRmmp8df44ssfHflGYU12prN2kA=
652 | dependencies:
653 | to-regex-range "^5.0.1"
654 |
655 | fsevents@~2.3.2:
656 | version "2.3.2"
657 | resolved "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz?cache=0&sync_timestamp=1612536512306&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffsevents%2Fdownload%2Ffsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
658 | integrity sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=
659 |
660 | function-bind@^1.1.1:
661 | version "1.1.1"
662 | resolved "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
663 | integrity sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=
664 |
665 | gensync@^1.0.0-beta.2:
666 | version "1.0.0-beta.2"
667 | resolved "https://registry.nlark.com/gensync/download/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
668 | integrity sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=
669 |
670 | glob-parent@~5.1.2:
671 | version "5.1.2"
672 | resolved "https://registry.nlark.com/glob-parent/download/glob-parent-5.1.2.tgz?cache=0&sync_timestamp=1626760165717&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
673 | integrity sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=
674 | dependencies:
675 | is-glob "^4.0.1"
676 |
677 | globals@^11.1.0:
678 | version "11.12.0"
679 | resolved "https://registry.nlark.com/globals/download/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
680 | integrity sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=
681 |
682 | graceful-fs@^4.1.2:
683 | version "4.2.8"
684 | resolved "https://registry.nlark.com/graceful-fs/download/graceful-fs-4.2.8.tgz?cache=0&sync_timestamp=1628194078324&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fgraceful-fs%2Fdownload%2Fgraceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
685 | integrity sha1-5BK40z9eAGWTy9PO5t+fLOu+gCo=
686 |
687 | has-flag@^3.0.0:
688 | version "3.0.0"
689 | resolved "https://registry.nlark.com/has-flag/download/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
690 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
691 |
692 | has@^1.0.3:
693 | version "1.0.3"
694 | resolved "https://registry.nlark.com/has/download/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
695 | integrity sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=
696 | dependencies:
697 | function-bind "^1.1.1"
698 |
699 | header-case@^2.0.4:
700 | version "2.0.4"
701 | resolved "https://registry.npm.taobao.org/header-case/download/header-case-2.0.4.tgz?cache=0&sync_timestamp=1606867326152&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fheader-case%2Fdownload%2Fheader-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
702 | integrity sha1-WkLmO1UXc0nPQFvrjXdayruSwGM=
703 | dependencies:
704 | capital-case "^1.0.4"
705 | tslib "^2.0.3"
706 |
707 | iconv-lite@^0.4.4:
708 | version "0.4.24"
709 | resolved "https://registry.nlark.com/iconv-lite/download/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
710 | integrity sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=
711 | dependencies:
712 | safer-buffer ">= 2.1.2 < 3"
713 |
714 | image-size@~0.5.0:
715 | version "0.5.5"
716 | resolved "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz?cache=0&sync_timestamp=1618422554012&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimage-size%2Fdownload%2Fimage-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
717 | integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=
718 |
719 | is-binary-path@~2.1.0:
720 | version "2.1.0"
721 | resolved "https://registry.nlark.com/is-binary-path/download/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
722 | integrity sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=
723 | dependencies:
724 | binary-extensions "^2.0.0"
725 |
726 | is-core-module@^2.2.0:
727 | version "2.6.0"
728 | resolved "https://registry.nlark.com/is-core-module/download/is-core-module-2.6.0.tgz?cache=0&sync_timestamp=1629224656971&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fis-core-module%2Fdownload%2Fis-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19"
729 | integrity sha1-11U7JSb+Wbkro+QMjfdX7Ipwnhk=
730 | dependencies:
731 | has "^1.0.3"
732 |
733 | is-extglob@^2.1.1:
734 | version "2.1.1"
735 | resolved "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
736 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
737 |
738 | is-glob@^4.0.1, is-glob@~4.0.1:
739 | version "4.0.1"
740 | resolved "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
741 | integrity sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=
742 | dependencies:
743 | is-extglob "^2.1.1"
744 |
745 | is-number@^7.0.0:
746 | version "7.0.0"
747 | resolved "https://registry.nlark.com/is-number/download/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
748 | integrity sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=
749 |
750 | is-what@^3.12.0:
751 | version "3.14.1"
752 | resolved "https://registry.npm.taobao.org/is-what/download/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
753 | integrity sha1-4SIvRt3ahd6tD9HJ3xMXYOd3VcE=
754 |
755 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
756 | version "4.0.0"
757 | resolved "https://registry.nlark.com/js-tokens/download/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
758 | integrity sha1-GSA/tZmR35jjoocFDUZHzerzJJk=
759 |
760 | jsesc@^2.5.1:
761 | version "2.5.2"
762 | resolved "https://registry.npm.taobao.org/jsesc/download/jsesc-2.5.2.tgz?cache=0&sync_timestamp=1603891232110&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjsesc%2Fdownload%2Fjsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
763 | integrity sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=
764 |
765 | json2mq@^0.2.0:
766 | version "0.2.0"
767 | resolved "https://registry.npm.taobao.org/json2mq/download/json2mq-0.2.0.tgz?cache=0&sync_timestamp=1602340165972&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson2mq%2Fdownload%2Fjson2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
768 | integrity sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=
769 | dependencies:
770 | string-convert "^0.2.0"
771 |
772 | json5@^2.1.2:
773 | version "2.2.0"
774 | resolved "https://registry.npm.taobao.org/json5/download/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
775 | integrity sha1-Lf7+cgxrpSXZ69kJlQ8FFTFsiaM=
776 | dependencies:
777 | minimist "^1.2.5"
778 |
779 | less@^4.1.1:
780 | version "4.1.1"
781 | resolved "https://registry.npm.taobao.org/less/download/less-4.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fless%2Fdownload%2Fless-4.1.1.tgz#15bf253a9939791dc690888c3ff424f3e6c7edba"
782 | integrity sha1-Fb8lOpk5eR3GkIiMP/Qk8+bH7bo=
783 | dependencies:
784 | copy-anything "^2.0.1"
785 | parse-node-version "^1.0.1"
786 | tslib "^1.10.0"
787 | optionalDependencies:
788 | errno "^0.1.1"
789 | graceful-fs "^4.1.2"
790 | image-size "~0.5.0"
791 | make-dir "^2.1.0"
792 | mime "^1.4.1"
793 | needle "^2.5.2"
794 | source-map "~0.6.0"
795 |
796 | lodash-es@^4.17.15:
797 | version "4.17.21"
798 | resolved "https://registry.npm.taobao.org/lodash-es/download/lodash-es-4.17.21.tgz?cache=0&sync_timestamp=1613835893273&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash-es%2Fdownload%2Flodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
799 | integrity sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4=
800 |
801 | lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.21:
802 | version "4.17.21"
803 | resolved "https://registry.nlark.com/lodash/download/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
804 | integrity sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=
805 |
806 | loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
807 | version "1.4.0"
808 | resolved "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
809 | integrity sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=
810 | dependencies:
811 | js-tokens "^3.0.0 || ^4.0.0"
812 |
813 | lower-case@^2.0.2:
814 | version "2.0.2"
815 | resolved "https://registry.npm.taobao.org/lower-case/download/lower-case-2.0.2.tgz?cache=0&sync_timestamp=1606867333511&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flower-case%2Fdownload%2Flower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
816 | integrity sha1-b6I3xj29xKgsoP2ILkci3F5jTig=
817 | dependencies:
818 | tslib "^2.0.3"
819 |
820 | magic-string@^0.25.7:
821 | version "0.25.7"
822 | resolved "https://registry.nlark.com/magic-string/download/magic-string-0.25.7.tgz?cache=0&sync_timestamp=1618847046304&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fmagic-string%2Fdownload%2Fmagic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
823 | integrity sha1-P0l9b9NMZpxnmNy4IfLvMfVEUFE=
824 | dependencies:
825 | sourcemap-codec "^1.4.4"
826 |
827 | make-dir@^2.1.0:
828 | version "2.1.0"
829 | resolved "https://registry.nlark.com/make-dir/download/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
830 | integrity sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=
831 | dependencies:
832 | pify "^4.0.1"
833 | semver "^5.6.0"
834 |
835 | material-colors@^1.2.1:
836 | version "1.2.6"
837 | resolved "https://registry.npm.taobao.org/material-colors/download/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
838 | integrity sha1-bRlYhxEmmSzuzHL0vMTY8BCGX0Y=
839 |
840 | mime@^1.4.1:
841 | version "1.6.0"
842 | resolved "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
843 | integrity sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=
844 |
845 | minimist@^1.2.5:
846 | version "1.2.5"
847 | resolved "https://registry.nlark.com/minimist/download/minimist-1.2.5.tgz?cache=0&sync_timestamp=1618846813226&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
848 | integrity sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=
849 |
850 | moment@^2.24.0, moment@^2.25.3:
851 | version "2.29.1"
852 | resolved "https://registry.nlark.com/moment/download/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
853 | integrity sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=
854 |
855 | ms@2.1.2:
856 | version "2.1.2"
857 | resolved "https://registry.nlark.com/ms/download/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
858 | integrity sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=
859 |
860 | ms@^2.1.1:
861 | version "2.1.3"
862 | resolved "https://registry.nlark.com/ms/download/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
863 | integrity sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=
864 |
865 | nanoid@^3.1.23:
866 | version "3.1.25"
867 | resolved "https://registry.nlark.com/nanoid/download/nanoid-3.1.25.tgz?cache=0&sync_timestamp=1628771905854&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fnanoid%2Fdownload%2Fnanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
868 | integrity sha1-CcoydHwOVD8OGBS303k0d/nI4VI=
869 |
870 | needle@^2.5.2:
871 | version "2.9.1"
872 | resolved "https://registry.nlark.com/needle/download/needle-2.9.1.tgz?cache=0&sync_timestamp=1630674912294&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fneedle%2Fdownload%2Fneedle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684"
873 | integrity sha1-ItHf++NJDCuD4wH3cJtnNs2PJoQ=
874 | dependencies:
875 | debug "^3.2.6"
876 | iconv-lite "^0.4.4"
877 | sax "^1.2.4"
878 |
879 | no-case@^3.0.4:
880 | version "3.0.4"
881 | resolved "https://registry.nlark.com/no-case/download/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
882 | integrity sha1-02H9XJgA9VhVGoNp/A3NRmK2Ek0=
883 | dependencies:
884 | lower-case "^2.0.2"
885 | tslib "^2.0.3"
886 |
887 | node-releases@^1.1.75:
888 | version "1.1.76"
889 | resolved "https://registry.nlark.com/node-releases/download/node-releases-1.1.76.tgz?cache=0&sync_timestamp=1632151316505&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fnode-releases%2Fdownload%2Fnode-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e"
890 | integrity sha1-3yRbBisMr71SgqtnkvfczC2X824=
891 |
892 | normalize-path@^3.0.0, normalize-path@~3.0.0:
893 | version "3.0.0"
894 | resolved "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
895 | integrity sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=
896 |
897 | object-assign@^4.1.1:
898 | version "4.1.1"
899 | resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
900 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
901 |
902 | param-case@^3.0.4:
903 | version "3.0.4"
904 | resolved "https://registry.nlark.com/param-case/download/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
905 | integrity sha1-fRf+SqEr3jTUp32RrPtiGcqtAcU=
906 | dependencies:
907 | dot-case "^3.0.4"
908 | tslib "^2.0.3"
909 |
910 | parse-node-version@^1.0.1:
911 | version "1.0.1"
912 | resolved "https://registry.npm.taobao.org/parse-node-version/download/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
913 | integrity sha1-4rXb7eAOf6m8NjYH9TMn6LBzGJs=
914 |
915 | pascal-case@^3.1.2:
916 | version "3.1.2"
917 | resolved "https://registry.nlark.com/pascal-case/download/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
918 | integrity sha1-tI4O8rmOIF58Ha50fQsVCCN2YOs=
919 | dependencies:
920 | no-case "^3.0.4"
921 | tslib "^2.0.3"
922 |
923 | path-case@^3.0.4:
924 | version "3.0.4"
925 | resolved "https://registry.npm.taobao.org/path-case/download/path-case-3.0.4.tgz?cache=0&sync_timestamp=1606867325967&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-case%2Fdownload%2Fpath-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
926 | integrity sha1-kWhkUzTrlCZYN1xW+AtMDLX4LG8=
927 | dependencies:
928 | dot-case "^3.0.4"
929 | tslib "^2.0.3"
930 |
931 | path-parse@^1.0.6:
932 | version "1.0.7"
933 | resolved "https://registry.nlark.com/path-parse/download/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
934 | integrity sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=
935 |
936 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
937 | version "2.3.0"
938 | resolved "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz?cache=0&sync_timestamp=1621648246651&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpicomatch%2Fdownload%2Fpicomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
939 | integrity sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=
940 |
941 | pify@^4.0.1:
942 | version "4.0.1"
943 | resolved "https://registry.nlark.com/pify/download/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
944 | integrity sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=
945 |
946 | postcss@^8.3.6:
947 | version "8.3.6"
948 | resolved "https://registry.nlark.com/postcss/download/postcss-8.3.6.tgz?cache=0&sync_timestamp=1626882933935&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpostcss%2Fdownload%2Fpostcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
949 | integrity sha1-JzDddql5afN/U7mmCWGXvjEcxOo=
950 | dependencies:
951 | colorette "^1.2.2"
952 | nanoid "^3.1.23"
953 | source-map-js "^0.6.2"
954 |
955 | prop-types@^15.5.10:
956 | version "15.7.2"
957 | resolved "https://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz?cache=0&sync_timestamp=1615984425557&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fprop-types%2Fdownload%2Fprop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
958 | integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU=
959 | dependencies:
960 | loose-envify "^1.4.0"
961 | object-assign "^4.1.1"
962 | react-is "^16.8.1"
963 |
964 | prr@~1.0.1:
965 | version "1.0.1"
966 | resolved "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
967 | integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
968 |
969 | rc-align@^4.0.0:
970 | version "4.0.11"
971 | resolved "https://registry.nlark.com/rc-align/download/rc-align-4.0.11.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-align%2Fdownload%2Frc-align-4.0.11.tgz#8198c62db266bc1b8ef05e56c13275bf72628a5e"
972 | integrity sha1-gZjGLbJmvBuO8F5WwTJ1v3Jiil4=
973 | dependencies:
974 | "@babel/runtime" "^7.10.1"
975 | classnames "2.x"
976 | dom-align "^1.7.0"
977 | lodash "^4.17.21"
978 | rc-util "^5.3.0"
979 | resize-observer-polyfill "^1.5.1"
980 |
981 | rc-cascader@~1.4.0:
982 | version "1.4.3"
983 | resolved "https://registry.nlark.com/rc-cascader/download/rc-cascader-1.4.3.tgz?cache=0&sync_timestamp=1631514687405&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-cascader%2Fdownload%2Frc-cascader-1.4.3.tgz#d91b0dcf8157b60ebe9ec3e58b4db054d5299464"
984 | integrity sha1-2RsNz4FXtg6+nsPli02wVNUplGQ=
985 | dependencies:
986 | "@babel/runtime" "^7.12.5"
987 | array-tree-filter "^2.1.0"
988 | rc-trigger "^5.0.4"
989 | rc-util "^5.0.1"
990 | warning "^4.0.1"
991 |
992 | rc-checkbox@~2.3.0:
993 | version "2.3.2"
994 | resolved "https://registry.npm.taobao.org/rc-checkbox/download/rc-checkbox-2.3.2.tgz?cache=0&sync_timestamp=1607402886550&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-checkbox%2Fdownload%2Frc-checkbox-2.3.2.tgz#f91b3678c7edb2baa8121c9483c664fa6f0aefc1"
995 | integrity sha1-+Rs2eMftsrqoEhyUg8Zk+m8K78E=
996 | dependencies:
997 | "@babel/runtime" "^7.10.1"
998 | classnames "^2.2.1"
999 |
1000 | rc-collapse@~3.1.0:
1001 | version "3.1.2"
1002 | resolved "https://registry.nlark.com/rc-collapse/download/rc-collapse-3.1.2.tgz#76028a811b845d03d9460ccc409c7ea8ad09db14"
1003 | integrity sha1-dgKKgRuEXQPZRgzMQJx+qK0J2xQ=
1004 | dependencies:
1005 | "@babel/runtime" "^7.10.1"
1006 | classnames "2.x"
1007 | rc-motion "^2.3.4"
1008 | rc-util "^5.2.1"
1009 | shallowequal "^1.1.0"
1010 |
1011 | rc-dialog@~8.6.0:
1012 | version "8.6.0"
1013 | resolved "https://registry.nlark.com/rc-dialog/download/rc-dialog-8.6.0.tgz?cache=0&sync_timestamp=1627272051827&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-dialog%2Fdownload%2Frc-dialog-8.6.0.tgz#3b228dac085de5eed8c6237f31162104687442e7"
1014 | integrity sha1-OyKNrAhd5e7YxiN/MRYhBGh0Quc=
1015 | dependencies:
1016 | "@babel/runtime" "^7.10.1"
1017 | classnames "^2.2.6"
1018 | rc-motion "^2.3.0"
1019 | rc-util "^5.6.1"
1020 |
1021 | rc-drawer@~4.3.0:
1022 | version "4.3.1"
1023 | resolved "https://registry.nlark.com/rc-drawer/download/rc-drawer-4.3.1.tgz?cache=0&sync_timestamp=1630560040693&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-drawer%2Fdownload%2Frc-drawer-4.3.1.tgz#356333a7af01b777abd685c96c2ce62efb44f3f3"
1024 | integrity sha1-NWMzp68Bt3er1oXJbCzmLvtE8/M=
1025 | dependencies:
1026 | "@babel/runtime" "^7.10.1"
1027 | classnames "^2.2.6"
1028 | rc-util "^5.7.0"
1029 |
1030 | rc-dropdown@^3.2.0, rc-dropdown@~3.2.0:
1031 | version "3.2.0"
1032 | resolved "https://registry.npm.taobao.org/rc-dropdown/download/rc-dropdown-3.2.0.tgz?cache=0&sync_timestamp=1600332823107&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-dropdown%2Fdownload%2Frc-dropdown-3.2.0.tgz#da6c2ada403842baee3a9e909a0b1a91ba3e1090"
1033 | integrity sha1-2mwq2kA4QrruOp6Qmgsakbo+EJA=
1034 | dependencies:
1035 | "@babel/runtime" "^7.10.1"
1036 | classnames "^2.2.6"
1037 | rc-trigger "^5.0.4"
1038 |
1039 | rc-field-form@~1.20.0:
1040 | version "1.20.1"
1041 | resolved "https://registry.nlark.com/rc-field-form/download/rc-field-form-1.20.1.tgz#d1c51888107cf075b42704b7b575bef84c359291"
1042 | integrity sha1-0cUYiBB88HW0JwS3tXW++Ew1kpE=
1043 | dependencies:
1044 | "@babel/runtime" "^7.8.4"
1045 | async-validator "^3.0.3"
1046 | rc-util "^5.8.0"
1047 |
1048 | rc-image@~5.2.5:
1049 | version "5.2.5"
1050 | resolved "https://registry.nlark.com/rc-image/download/rc-image-5.2.5.tgz?cache=0&sync_timestamp=1627889032895&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-image%2Fdownload%2Frc-image-5.2.5.tgz#44e6ffc842626827960e7ab72e1c0d6f3a8ce440"
1051 | integrity sha1-ROb/yEJiaCeWDnq3LhwNbzqM5EA=
1052 | dependencies:
1053 | "@babel/runtime" "^7.11.2"
1054 | classnames "^2.2.6"
1055 | rc-dialog "~8.6.0"
1056 | rc-util "^5.0.6"
1057 |
1058 | rc-input-number@~7.1.0:
1059 | version "7.1.4"
1060 | resolved "https://registry.nlark.com/rc-input-number/download/rc-input-number-7.1.4.tgz?cache=0&sync_timestamp=1631779750767&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-input-number%2Fdownload%2Frc-input-number-7.1.4.tgz#9d7410c91ff8dc6384d0233c20df278982989f9a"
1061 | integrity sha1-nXQQyR/43GOE0CM8IN8niYKYn5o=
1062 | dependencies:
1063 | "@babel/runtime" "^7.10.1"
1064 | classnames "^2.2.5"
1065 | rc-util "^5.9.8"
1066 |
1067 | rc-mentions@~1.6.1:
1068 | version "1.6.1"
1069 | resolved "https://registry.nlark.com/rc-mentions/download/rc-mentions-1.6.1.tgz#46035027d64aa33ef840ba0fbd411871e34617ae"
1070 | integrity sha1-RgNQJ9ZKoz74QLoPvUEYceNGF64=
1071 | dependencies:
1072 | "@babel/runtime" "^7.10.1"
1073 | classnames "^2.2.6"
1074 | rc-menu "^9.0.0"
1075 | rc-textarea "^0.3.0"
1076 | rc-trigger "^5.0.4"
1077 | rc-util "^5.0.1"
1078 |
1079 | rc-menu@^9.0.0, rc-menu@~9.0.12:
1080 | version "9.0.12"
1081 | resolved "https://registry.nlark.com/rc-menu/download/rc-menu-9.0.12.tgz#492c4bb07a596e2ce07587c669b27ee28c3810c5"
1082 | integrity sha1-SSxLsHpZbizgdYfGabJ+4ow4EMU=
1083 | dependencies:
1084 | "@babel/runtime" "^7.10.1"
1085 | classnames "2.x"
1086 | rc-motion "^2.4.3"
1087 | rc-overflow "^1.2.0"
1088 | rc-trigger "^5.1.2"
1089 | rc-util "^5.12.0"
1090 | shallowequal "^1.1.0"
1091 |
1092 | rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.0, rc-motion@^2.4.3:
1093 | version "2.4.4"
1094 | resolved "https://registry.nlark.com/rc-motion/download/rc-motion-2.4.4.tgz?cache=0&sync_timestamp=1622690263011&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-motion%2Fdownload%2Frc-motion-2.4.4.tgz#e995d5fa24fc93065c24f714857cf2677d655bb0"
1095 | integrity sha1-6ZXV+iT8kwZcJPcUhXzyZ31lW7A=
1096 | dependencies:
1097 | "@babel/runtime" "^7.11.1"
1098 | classnames "^2.2.1"
1099 | rc-util "^5.2.1"
1100 |
1101 | rc-notification@~4.5.7:
1102 | version "4.5.7"
1103 | resolved "https://registry.nlark.com/rc-notification/download/rc-notification-4.5.7.tgz#265e6e6a0c1a0fac63d6abd4d832eb8ff31522f1"
1104 | integrity sha1-Jl5uagwaD6xj1qvU2DLrj/MVIvE=
1105 | dependencies:
1106 | "@babel/runtime" "^7.10.1"
1107 | classnames "2.x"
1108 | rc-motion "^2.2.0"
1109 | rc-util "^5.0.1"
1110 |
1111 | rc-overflow@^1.0.0, rc-overflow@^1.2.0:
1112 | version "1.2.2"
1113 | resolved "https://registry.nlark.com/rc-overflow/download/rc-overflow-1.2.2.tgz#95b0222016c0cdbdc0db85f569c262e7706a5f22"
1114 | integrity sha1-lbAiIBbAzb3A24X1acJi53BqXyI=
1115 | dependencies:
1116 | "@babel/runtime" "^7.11.1"
1117 | classnames "^2.2.1"
1118 | rc-resize-observer "^1.0.0"
1119 | rc-util "^5.5.1"
1120 |
1121 | rc-pagination@~3.1.9:
1122 | version "3.1.9"
1123 | resolved "https://registry.nlark.com/rc-pagination/download/rc-pagination-3.1.9.tgz#797ad75d85b1ef7a82801207ead410110337fdd6"
1124 | integrity sha1-eXrXXYWx73qCgBIH6tQQEQM3/dY=
1125 | dependencies:
1126 | "@babel/runtime" "^7.10.1"
1127 | classnames "^2.2.1"
1128 |
1129 | rc-picker@~2.5.10:
1130 | version "2.5.18"
1131 | resolved "https://registry.nlark.com/rc-picker/download/rc-picker-2.5.18.tgz#f84859815ef3f874ade689714a41151e709292c3"
1132 | integrity sha1-+EhZgV7z+HSt5olxSkEVHnCSksM=
1133 | dependencies:
1134 | "@babel/runtime" "^7.10.1"
1135 | classnames "^2.2.1"
1136 | date-fns "2.x"
1137 | dayjs "1.x"
1138 | moment "^2.24.0"
1139 | rc-trigger "^5.0.4"
1140 | rc-util "^5.4.0"
1141 | shallowequal "^1.1.0"
1142 |
1143 | rc-progress@~3.1.0:
1144 | version "3.1.4"
1145 | resolved "https://registry.nlark.com/rc-progress/download/rc-progress-3.1.4.tgz#66040d0fae7d8ced2b38588378eccb2864bad615"
1146 | integrity sha1-ZgQND659jO0rOFiDeOzLKGS61hU=
1147 | dependencies:
1148 | "@babel/runtime" "^7.10.1"
1149 | classnames "^2.2.6"
1150 |
1151 | rc-rate@~2.9.0:
1152 | version "2.9.1"
1153 | resolved "https://registry.npm.taobao.org/rc-rate/download/rc-rate-2.9.1.tgz?cache=0&sync_timestamp=1605573484361&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-rate%2Fdownload%2Frc-rate-2.9.1.tgz#e43cb95c4eb90a2c1e0b16ec6614d8c43530a731"
1154 | integrity sha1-5Dy5XE65CiweCxbsZhTYxDUwpzE=
1155 | dependencies:
1156 | "@babel/runtime" "^7.10.1"
1157 | classnames "^2.2.5"
1158 | rc-util "^5.0.1"
1159 |
1160 | rc-resize-observer@^1.0.0:
1161 | version "1.0.1"
1162 | resolved "https://registry.nlark.com/rc-resize-observer/download/rc-resize-observer-1.0.1.tgz?cache=0&sync_timestamp=1630563985540&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-resize-observer%2Fdownload%2Frc-resize-observer-1.0.1.tgz#ccd0986543ff1bf49f8a581e8ac4bb714ed24dcd"
1163 | integrity sha1-zNCYZUP/G/SfilgeisS7cU7STc0=
1164 | dependencies:
1165 | "@babel/runtime" "^7.10.1"
1166 | classnames "^2.2.1"
1167 | rc-util "^5.0.0"
1168 | resize-observer-polyfill "^1.5.1"
1169 |
1170 | rc-select@^12.0.0, rc-select@~12.1.6:
1171 | version "12.1.13"
1172 | resolved "https://registry.nlark.com/rc-select/download/rc-select-12.1.13.tgz#c33560ccb9339d30695b52458f55efc35af35273"
1173 | integrity sha1-wzVgzLkznTBpW1JFj1Xvw1rzUnM=
1174 | dependencies:
1175 | "@babel/runtime" "^7.10.1"
1176 | classnames "2.x"
1177 | rc-motion "^2.0.1"
1178 | rc-overflow "^1.0.0"
1179 | rc-trigger "^5.0.4"
1180 | rc-util "^5.9.8"
1181 | rc-virtual-list "^3.2.0"
1182 |
1183 | rc-slider@~9.7.1:
1184 | version "9.7.2"
1185 | resolved "https://registry.npm.taobao.org/rc-slider/download/rc-slider-9.7.2.tgz?cache=0&sync_timestamp=1616675519253&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-slider%2Fdownload%2Frc-slider-9.7.2.tgz#282f571f7582752ebaa33964e441184f4e79ad74"
1186 | integrity sha1-KC9XH3WCdS66ozlk5EEYT055rXQ=
1187 | dependencies:
1188 | "@babel/runtime" "^7.10.1"
1189 | classnames "^2.2.5"
1190 | rc-tooltip "^5.0.1"
1191 | rc-util "^5.0.0"
1192 | shallowequal "^1.1.0"
1193 |
1194 | rc-steps@~4.1.0:
1195 | version "4.1.3"
1196 | resolved "https://registry.npm.taobao.org/rc-steps/download/rc-steps-4.1.3.tgz?cache=0&sync_timestamp=1603284176603&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frc-steps%2Fdownload%2Frc-steps-4.1.3.tgz#208580e22db619e3830ddb7fa41bc886c65d9803"
1197 | integrity sha1-IIWA4i22GeODDdt/pBvIhsZdmAM=
1198 | dependencies:
1199 | "@babel/runtime" "^7.10.2"
1200 | classnames "^2.2.3"
1201 | rc-util "^5.0.1"
1202 |
1203 | rc-switch@~3.2.0:
1204 | version "3.2.2"
1205 | resolved "https://registry.nlark.com/rc-switch/download/rc-switch-3.2.2.tgz#d001f77f12664d52595b4f6fb425dd9e66fba8e8"
1206 | integrity sha1-0AH3fxJmTVJZW09vtCXdnmb7qOg=
1207 | dependencies:
1208 | "@babel/runtime" "^7.10.1"
1209 | classnames "^2.2.1"
1210 | rc-util "^5.0.1"
1211 |
1212 | rc-table@~7.15.1:
1213 | version "7.15.2"
1214 | resolved "https://registry.nlark.com/rc-table/download/rc-table-7.15.2.tgz?cache=0&sync_timestamp=1631688916566&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-table%2Fdownload%2Frc-table-7.15.2.tgz#f6ab73b2cfb1c76f3cf9682c855561423c6b5b22"
1215 | integrity sha1-9qtzss+xx288+WgshVVhQjxrWyI=
1216 | dependencies:
1217 | "@babel/runtime" "^7.10.1"
1218 | classnames "^2.2.5"
1219 | rc-resize-observer "^1.0.0"
1220 | rc-util "^5.13.0"
1221 | shallowequal "^1.1.0"
1222 |
1223 | rc-tabs@~11.10.0:
1224 | version "11.10.1"
1225 | resolved "https://registry.nlark.com/rc-tabs/download/rc-tabs-11.10.1.tgz#7b112f78bac998480c777ae160adc425e3fdb7cb"
1226 | integrity sha1-exEveLrJmEgMd3rhYK3EJeP9t8s=
1227 | dependencies:
1228 | "@babel/runtime" "^7.11.2"
1229 | classnames "2.x"
1230 | rc-dropdown "^3.2.0"
1231 | rc-menu "^9.0.0"
1232 | rc-resize-observer "^1.0.0"
1233 | rc-util "^5.5.0"
1234 |
1235 | rc-textarea@^0.3.0, rc-textarea@~0.3.0:
1236 | version "0.3.5"
1237 | resolved "https://registry.nlark.com/rc-textarea/download/rc-textarea-0.3.5.tgz#07ed445dddb94e5ae6764676923a49bddad9b2ec"
1238 | integrity sha1-B+1EXd25TlrmdkZ2kjpJvdrZsuw=
1239 | dependencies:
1240 | "@babel/runtime" "^7.10.1"
1241 | classnames "^2.2.1"
1242 | rc-resize-observer "^1.0.0"
1243 | rc-util "^5.7.0"
1244 |
1245 | rc-tooltip@^5.0.1, rc-tooltip@~5.1.1:
1246 | version "5.1.1"
1247 | resolved "https://registry.nlark.com/rc-tooltip/download/rc-tooltip-5.1.1.tgz?cache=0&sync_timestamp=1620376922725&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-tooltip%2Fdownload%2Frc-tooltip-5.1.1.tgz#94178ed162d0252bc4993b725f5dc2ac0fccf154"
1248 | integrity sha1-lBeO0WLQJSvEmTtyX13CrA/M8VQ=
1249 | dependencies:
1250 | "@babel/runtime" "^7.11.2"
1251 | rc-trigger "^5.0.0"
1252 |
1253 | rc-tree-select@~4.3.0:
1254 | version "4.3.3"
1255 | resolved "https://registry.nlark.com/rc-tree-select/download/rc-tree-select-4.3.3.tgz?cache=0&sync_timestamp=1631588798554&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-tree-select%2Fdownload%2Frc-tree-select-4.3.3.tgz#28eba4d8a8dc8c0f9b61d83ce465842a6915eca4"
1256 | integrity sha1-KOuk2KjcjA+bYdg85GWEKmkV7KQ=
1257 | dependencies:
1258 | "@babel/runtime" "^7.10.1"
1259 | classnames "2.x"
1260 | rc-select "^12.0.0"
1261 | rc-tree "^4.0.0"
1262 | rc-util "^5.0.5"
1263 |
1264 | rc-tree@^4.0.0, rc-tree@~4.2.1:
1265 | version "4.2.2"
1266 | resolved "https://registry.nlark.com/rc-tree/download/rc-tree-4.2.2.tgz?cache=0&sync_timestamp=1630923510026&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-tree%2Fdownload%2Frc-tree-4.2.2.tgz#4429187cbbfbecbe989714a607e3de8b3ab7763f"
1267 | integrity sha1-RCkYfLv77L6YlxSmB+Peizq3dj8=
1268 | dependencies:
1269 | "@babel/runtime" "^7.10.1"
1270 | classnames "2.x"
1271 | rc-motion "^2.0.1"
1272 | rc-util "^5.0.0"
1273 | rc-virtual-list "^3.0.1"
1274 |
1275 | rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10:
1276 | version "5.2.10"
1277 | resolved "https://registry.nlark.com/rc-trigger/download/rc-trigger-5.2.10.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-trigger%2Fdownload%2Frc-trigger-5.2.10.tgz#8a0057a940b1b9027eaa33beec8a6ecd85cce2b1"
1278 | integrity sha1-igBXqUCxuQJ+qjO+7IpuzYXM4rE=
1279 | dependencies:
1280 | "@babel/runtime" "^7.11.2"
1281 | classnames "^2.2.6"
1282 | rc-align "^4.0.0"
1283 | rc-motion "^2.0.0"
1284 | rc-util "^5.5.0"
1285 |
1286 | rc-upload@~4.3.0:
1287 | version "4.3.1"
1288 | resolved "https://registry.nlark.com/rc-upload/download/rc-upload-4.3.1.tgz?cache=0&sync_timestamp=1623401069520&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-upload%2Fdownload%2Frc-upload-4.3.1.tgz#d6ee66b8bd1e1dd2f78526c486538423f7e7ed84"
1289 | integrity sha1-1u5muL0eHdL3hSbEhlOEI/fn7YQ=
1290 | dependencies:
1291 | "@babel/runtime" "^7.10.1"
1292 | classnames "^2.2.5"
1293 | rc-util "^5.2.0"
1294 |
1295 | rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.5, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.13.0, rc-util@^5.13.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.5.1, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8:
1296 | version "5.14.0"
1297 | resolved "https://registry.nlark.com/rc-util/download/rc-util-5.14.0.tgz?cache=0&sync_timestamp=1631611509279&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-util%2Fdownload%2Frc-util-5.14.0.tgz#52c650e27570c2c47f7936c7d32eaec5212492a8"
1298 | integrity sha1-UsZQ4nVwwsR/eTbH0y6uxSEkkqg=
1299 | dependencies:
1300 | "@babel/runtime" "^7.12.5"
1301 | react-is "^16.12.0"
1302 | shallowequal "^1.1.0"
1303 |
1304 | rc-virtual-list@^3.0.1, rc-virtual-list@^3.2.0:
1305 | version "3.4.1"
1306 | resolved "https://registry.nlark.com/rc-virtual-list/download/rc-virtual-list-3.4.1.tgz?cache=0&sync_timestamp=1630653487624&other_urls=https%3A%2F%2Fregistry.nlark.com%2Frc-virtual-list%2Fdownload%2Frc-virtual-list-3.4.1.tgz#1f3b41391acf033a6c7e84c2f4e8a4ee0dc72807"
1307 | integrity sha1-HztBORrPAzpsfoTC9Oik7g3HKAc=
1308 | dependencies:
1309 | classnames "^2.2.6"
1310 | rc-resize-observer "^1.0.0"
1311 | rc-util "^5.0.7"
1312 |
1313 | react-color@^2.19.3:
1314 | version "2.19.3"
1315 | resolved "https://registry.nlark.com/react-color/download/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d"
1316 | integrity sha1-7GxrRWgxKjxqGEIKsEcuFGqlaD0=
1317 | dependencies:
1318 | "@icons/material" "^0.2.4"
1319 | lodash "^4.17.15"
1320 | lodash-es "^4.17.15"
1321 | material-colors "^1.2.1"
1322 | prop-types "^15.5.10"
1323 | reactcss "^1.2.0"
1324 | tinycolor2 "^1.4.1"
1325 |
1326 | react-dom@^17.0.1:
1327 | version "17.0.2"
1328 | resolved "https://registry.nlark.com/react-dom/download/react-dom-17.0.2.tgz?cache=0&sync_timestamp=1632264147314&other_urls=https%3A%2F%2Fregistry.nlark.com%2Freact-dom%2Fdownload%2Freact-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
1329 | integrity sha1-7P+2hF462Nv83EmPDQqTlzZQLCM=
1330 | dependencies:
1331 | loose-envify "^1.1.0"
1332 | object-assign "^4.1.1"
1333 | scheduler "^0.20.2"
1334 |
1335 | react-is@^16.12.0, react-is@^16.8.1:
1336 | version "16.13.1"
1337 | resolved "https://registry.nlark.com/react-is/download/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1338 | integrity sha1-eJcppNw23imZ3BVt1sHZwYzqVqQ=
1339 |
1340 | react-refresh@^0.10.0:
1341 | version "0.10.0"
1342 | resolved "https://registry.nlark.com/react-refresh/download/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
1343 | integrity sha1-L1NslmDAubHVAGhNnlKmXnQE9+M=
1344 |
1345 | react@^17.0.1:
1346 | version "17.0.2"
1347 | resolved "https://registry.nlark.com/react/download/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
1348 | integrity sha1-0LXMUW0p6z7uOD91tihkz7aAADc=
1349 | dependencies:
1350 | loose-envify "^1.1.0"
1351 | object-assign "^4.1.1"
1352 |
1353 | reactcss@^1.2.0:
1354 | version "1.2.3"
1355 | resolved "https://registry.npm.taobao.org/reactcss/download/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
1356 | integrity sha1-wAATh15Vexzw39mjaKHD2rO1SN0=
1357 | dependencies:
1358 | lodash "^4.0.1"
1359 |
1360 | readdirp@~3.6.0:
1361 | version "3.6.0"
1362 | resolved "https://registry.npm.taobao.org/readdirp/download/readdirp-3.6.0.tgz?cache=0&sync_timestamp=1615717369278&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
1363 | integrity sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=
1364 | dependencies:
1365 | picomatch "^2.2.1"
1366 |
1367 | regenerator-runtime@^0.13.4:
1368 | version "0.13.9"
1369 | resolved "https://registry.nlark.com/regenerator-runtime/download/regenerator-runtime-0.13.9.tgz?cache=0&sync_timestamp=1626993001371&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
1370 | integrity sha1-iSV0Kpj/2QgUmI11Zq0wyjsmO1I=
1371 |
1372 | resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
1373 | version "1.5.1"
1374 | resolved "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
1375 | integrity sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ=
1376 |
1377 | resolve@^1.20.0:
1378 | version "1.20.0"
1379 | resolved "https://registry.nlark.com/resolve/download/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
1380 | integrity sha1-YpoBP7P3B1XW8LeTXMHCxTeLGXU=
1381 | dependencies:
1382 | is-core-module "^2.2.0"
1383 | path-parse "^1.0.6"
1384 |
1385 | rollup@^2.38.5:
1386 | version "2.56.3"
1387 | resolved "https://registry.nlark.com/rollup/download/rollup-2.56.3.tgz#b63edadd9851b0d618a6d0e6af8201955a77aeff"
1388 | integrity sha1-tj7a3ZhRsNYYptDmr4IBlVp3rv8=
1389 | optionalDependencies:
1390 | fsevents "~2.3.2"
1391 |
1392 | safe-buffer@~5.1.1:
1393 | version "5.1.2"
1394 | resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
1395 | integrity sha1-mR7GnSluAxN0fVm9/St0XDX4go0=
1396 |
1397 | "safer-buffer@>= 2.1.2 < 3":
1398 | version "2.1.2"
1399 | resolved "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
1400 | integrity sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=
1401 |
1402 | sass@^1.42.0:
1403 | version "1.42.0"
1404 | resolved "https://registry.nlark.com/sass/download/sass-1.42.0.tgz#dc485e5cf7890a22fd6ccc6856c64f297239d595"
1405 | integrity sha1-3EheXPeJCiL9bMxoVsZPKXI51ZU=
1406 | dependencies:
1407 | chokidar ">=3.0.0 <4.0.0"
1408 |
1409 | sax@^1.2.4:
1410 | version "1.2.4"
1411 | resolved "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
1412 | integrity sha1-KBYjTiN4vdxOU1T6tcqold9xANk=
1413 |
1414 | scheduler@^0.20.2:
1415 | version "0.20.2"
1416 | resolved "https://registry.nlark.com/scheduler/download/scheduler-0.20.2.tgz?cache=0&sync_timestamp=1632264118861&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fscheduler%2Fdownload%2Fscheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
1417 | integrity sha1-S67jlDbjSqk7SHS93L8P6Li1DpE=
1418 | dependencies:
1419 | loose-envify "^1.1.0"
1420 | object-assign "^4.1.1"
1421 |
1422 | scroll-into-view-if-needed@^2.2.25:
1423 | version "2.2.28"
1424 | resolved "https://registry.npm.taobao.org/scroll-into-view-if-needed/download/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a"
1425 | integrity sha1-WhWy9YpSZCyIyOylhGROAXA9ZFo=
1426 | dependencies:
1427 | compute-scroll-into-view "^1.0.17"
1428 |
1429 | semver@^5.6.0:
1430 | version "5.7.1"
1431 | resolved "https://registry.npm.taobao.org/semver/download/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
1432 | integrity sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=
1433 |
1434 | semver@^6.3.0:
1435 | version "6.3.0"
1436 | resolved "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
1437 | integrity sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=
1438 |
1439 | sentence-case@^3.0.4:
1440 | version "3.0.4"
1441 | resolved "https://registry.npm.taobao.org/sentence-case/download/sentence-case-3.0.4.tgz?cache=0&sync_timestamp=1606867325535&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsentence-case%2Fdownload%2Fsentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
1442 | integrity sha1-NkWnuMEXx4f96HAgViJbtipFEx8=
1443 | dependencies:
1444 | no-case "^3.0.4"
1445 | tslib "^2.0.3"
1446 | upper-case-first "^2.0.2"
1447 |
1448 | shallowequal@^1.1.0:
1449 | version "1.1.0"
1450 | resolved "https://registry.npm.taobao.org/shallowequal/download/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
1451 | integrity sha1-GI1SHelbkIdAT9TctosT3wrk5/g=
1452 |
1453 | snake-case@^3.0.4:
1454 | version "3.0.4"
1455 | resolved "https://registry.npm.taobao.org/snake-case/download/snake-case-3.0.4.tgz?cache=0&sync_timestamp=1606867326057&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsnake-case%2Fdownload%2Fsnake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
1456 | integrity sha1-Tyu9Vo6ZNavf1ZPzTGkdrbScRSw=
1457 | dependencies:
1458 | dot-case "^3.0.4"
1459 | tslib "^2.0.3"
1460 |
1461 | source-map-js@^0.6.2:
1462 | version "0.6.2"
1463 | resolved "https://registry.npm.taobao.org/source-map-js/download/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
1464 | integrity sha1-C7XeYxtBz72mz7qL0FqA79/SOF4=
1465 |
1466 | source-map@^0.5.0:
1467 | version "0.5.7"
1468 | resolved "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
1469 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
1470 |
1471 | source-map@~0.6.0:
1472 | version "0.6.1"
1473 | resolved "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
1474 | integrity sha1-dHIq8y6WFOnCh6jQu95IteLxomM=
1475 |
1476 | sourcemap-codec@^1.4.4:
1477 | version "1.4.8"
1478 | resolved "https://registry.npm.taobao.org/sourcemap-codec/download/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
1479 | integrity sha1-6oBL2UhXQC5pktBaOO8a41qatMQ=
1480 |
1481 | string-convert@^0.2.0:
1482 | version "0.2.1"
1483 | resolved "https://registry.nlark.com/string-convert/download/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
1484 | integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=
1485 |
1486 | supports-color@^5.3.0:
1487 | version "5.5.0"
1488 | resolved "https://registry.nlark.com/supports-color/download/supports-color-5.5.0.tgz?cache=0&sync_timestamp=1626703414084&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
1489 | integrity sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=
1490 | dependencies:
1491 | has-flag "^3.0.0"
1492 |
1493 | tinycolor2@^1.4.1:
1494 | version "1.4.2"
1495 | resolved "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
1496 | integrity sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM=
1497 |
1498 | to-fast-properties@^2.0.0:
1499 | version "2.0.0"
1500 | resolved "https://registry.nlark.com/to-fast-properties/download/to-fast-properties-2.0.0.tgz?cache=0&sync_timestamp=1628418893613&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fto-fast-properties%2Fdownload%2Fto-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
1501 | integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
1502 |
1503 | to-regex-range@^5.0.1:
1504 | version "5.0.1"
1505 | resolved "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
1506 | integrity sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=
1507 | dependencies:
1508 | is-number "^7.0.0"
1509 |
1510 | toggle-selection@^1.0.6:
1511 | version "1.0.6"
1512 | resolved "https://registry.npm.taobao.org/toggle-selection/download/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
1513 | integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
1514 |
1515 | tslib@^1.10.0:
1516 | version "1.14.1"
1517 | resolved "https://registry.nlark.com/tslib/download/tslib-1.14.1.tgz?cache=0&sync_timestamp=1628722556410&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftslib%2Fdownload%2Ftslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
1518 | integrity sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=
1519 |
1520 | tslib@^2.0.3:
1521 | version "2.3.1"
1522 | resolved "https://registry.nlark.com/tslib/download/tslib-2.3.1.tgz?cache=0&sync_timestamp=1628722556410&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftslib%2Fdownload%2Ftslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
1523 | integrity sha1-6KM1rdXOrlGqJh0ypJAVjvBC7wE=
1524 |
1525 | type-detect@^4.0.8:
1526 | version "4.0.8"
1527 | resolved "https://registry.npm.taobao.org/type-detect/download/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
1528 | integrity sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=
1529 |
1530 | typescript@^4.1.5:
1531 | version "4.4.3"
1532 | resolved "https://registry.nlark.com/typescript/download/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324"
1533 | integrity sha1-vcVAfKorEJ79T4L+EwZW+XeikyQ=
1534 |
1535 | upper-case-first@^2.0.2:
1536 | version "2.0.2"
1537 | resolved "https://registry.npm.taobao.org/upper-case-first/download/upper-case-first-2.0.2.tgz?cache=0&sync_timestamp=1606867326586&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fupper-case-first%2Fdownload%2Fupper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
1538 | integrity sha1-mSwyc/iCq9GdHgKJTMFHEX+EQyQ=
1539 | dependencies:
1540 | tslib "^2.0.3"
1541 |
1542 | upper-case@^2.0.2:
1543 | version "2.0.2"
1544 | resolved "https://registry.npm.taobao.org/upper-case/download/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
1545 | integrity sha1-2JgQgj+qsd8VSbfZenb4Ziuub3o=
1546 | dependencies:
1547 | tslib "^2.0.3"
1548 |
1549 | vite-plugin-babel-import@^2.0.5:
1550 | version "2.0.5"
1551 | resolved "https://registry.nlark.com/vite-plugin-babel-import/download/vite-plugin-babel-import-2.0.5.tgz#733df07dafa9410cae06f820604aebf0b3464d33"
1552 | integrity sha1-cz3wfa+pQQyuBvggYErr8LNGTTM=
1553 | dependencies:
1554 | "@babel/generator" "^7.12.11"
1555 | "@babel/helper-module-imports" "^7.12.5"
1556 | "@babel/parser" "^7.12.11"
1557 | "@babel/traverse" "^7.12.12"
1558 | "@babel/types" "^7.12.12"
1559 | change-case "^4.1.2"
1560 |
1561 | vite-plugin-style-import@^1.2.1:
1562 | version "1.2.1"
1563 | resolved "https://registry.nlark.com/vite-plugin-style-import/download/vite-plugin-style-import-1.2.1.tgz#730c7b0e1f61ed685974fe09d2d5094858491be6"
1564 | integrity sha1-cwx7Dh9h7WhZdP4J0tUJSFhJG+Y=
1565 | dependencies:
1566 | "@rollup/pluginutils" "^4.1.1"
1567 | change-case "^4.1.2"
1568 | debug "^4.3.2"
1569 | es-module-lexer "^0.7.1"
1570 | magic-string "^0.25.7"
1571 |
1572 | vite@^2.0.1:
1573 | version "2.5.10"
1574 | resolved "https://registry.nlark.com/vite/download/vite-2.5.10.tgz?cache=0&sync_timestamp=1632141709201&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvite%2Fdownload%2Fvite-2.5.10.tgz#c598e3b5a7e1956ffc52eb3b3420d177fc2ed2a5"
1575 | integrity sha1-xZjjtafhlW/8Uus7NCDRd/wu0qU=
1576 | dependencies:
1577 | esbuild "^0.12.17"
1578 | postcss "^8.3.6"
1579 | resolve "^1.20.0"
1580 | rollup "^2.38.5"
1581 | optionalDependencies:
1582 | fsevents "~2.3.2"
1583 |
1584 | warning@^4.0.1:
1585 | version "4.0.3"
1586 | resolved "https://registry.nlark.com/warning/download/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
1587 | integrity sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=
1588 | dependencies:
1589 | loose-envify "^1.0.0"
1590 |
--------------------------------------------------------------------------------