├── .editorconfig
├── .env.production
├── .eslintignore
├── .gitignore
├── .npmignore
├── .vscode
├── extensions.json
└── settings.json
├── CNAME
├── LICENSE
├── README.md
├── index.html
├── introduction.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── icon
│ │ ├── add-outline.svg
│ │ ├── code-download-outline.svg
│ │ ├── code-working-outline.svg
│ │ ├── copy-outline.svg
│ │ ├── eye-outline.svg
│ │ ├── login-after.svg
│ │ ├── login-before.svg
│ │ ├── power-outline.svg
│ │ ├── trash-outline.svg
│ │ └── tree-structure.png
│ ├── logo.png
│ ├── logo
│ │ ├── antd-n.svg
│ │ ├── element-n.png
│ │ ├── html-n.png
│ │ ├── iview-n.png
│ │ ├── quasar-n.png
│ │ └── vant-n.png
│ └── nestable.css
├── components-v2
│ ├── ToolsBar.vue
│ └── VCC.vue
├── components
│ ├── AttributeInput.vue
│ ├── Code.vue
│ ├── CodeEditor.vue
│ ├── CodeStructure.vue
│ ├── JSCodeEditorDialog.vue
│ ├── Main.vue
│ ├── Preview.vue
│ ├── RawComponents.vue
│ ├── VueCodeParseDialog.vue
│ ├── halower-tree.min.css
│ ├── nested.vue
│ └── prism.css
├── libs
│ ├── UIComponentInit.js
│ ├── bro-ele-config.js
│ ├── bundle-core-esm.js
│ ├── bundle-html2json-common.js
│ ├── bundle-html2json-esm.js
│ ├── bundle-json2html-common.js
│ ├── code-generator-factory.js
│ ├── directiveCheck.js
│ ├── main-panel.js
│ ├── presetAttribute.js
│ ├── singleIndexOutput.js
│ ├── split-init.js
│ ├── store.js
│ ├── stringify-object.js
│ └── v2-tree.js
├── main.js
├── map
│ ├── data.index.js
│ ├── load.js
│ ├── method.index.js
│ ├── style.index.js
│ └── template.index.js
├── rawComponents
│ ├── antd
│ │ ├── button.vue
│ │ └── index.vue
│ ├── echart
│ │ ├── chart.vue
│ │ ├── index.vue
│ │ └── init.vue
│ ├── element
│ │ ├── button.vue
│ │ ├── container.vue
│ │ ├── dialog.vue
│ │ ├── final.vue
│ │ ├── form-base.vue
│ │ ├── form.vue
│ │ ├── icon.vue
│ │ ├── image.vue
│ │ ├── index.vue
│ │ ├── layout.vue
│ │ ├── menu.vue
│ │ └── table.vue
│ ├── iview
│ │ ├── button.vue
│ │ ├── icon.vue
│ │ ├── index.vue
│ │ ├── layout.vue
│ │ ├── other.vue
│ │ └── template.vue
│ ├── quasar
│ │ ├── base.vue
│ │ ├── button.vue
│ │ └── index.vue
│ ├── raw
│ │ └── index.vue
│ └── vant
│ │ ├── button.vue
│ │ ├── display.vue
│ │ ├── form.vue
│ │ ├── icon.vue
│ │ ├── index.vue
│ │ ├── layout.vue
│ │ ├── nav.vue
│ │ └── template.vue
├── script
│ ├── compile.js
│ ├── compileComponent.js
│ └── distClear.js
├── test
│ └── compileVueOnline.vue
└── utils
│ ├── common.js
│ ├── forCode.js
│ ├── initRawComponent.js
│ └── lineHelper.js
├── vite.config.js
└── vue.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | end_of_line = lf
5 | trim_trailing_whitespace = true
6 | insert_final_newline = true
7 | max_line_length = 100
8 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | PUBLIC_PATH=https://static.imonkey.xueersi.com/vue-code-creater/
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | *.vue
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | demo
2 | src
3 | .env.*
4 | vue.config.js
5 | .DS_Store
6 | .editorconfig
7 | .vscode/launch.json
8 | dist/vcc.umd.js
9 | dist/vcc.common.js
10 |
11 | *.html
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["eamodio.gitlens", "esbenp.prettier-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "editor.tabSize": 2,
4 | "prettier.printWidth": 120,
5 | "prettier.tabWidth": 2,
6 | "prettier.useTabs": false,
7 | "prettier.semi": true,
8 | "prettier.singleQuote": true,
9 | "prettier.quoteProps": "as-needed",
10 | "prettier.jsxSingleQuote": false,
11 | "prettier.trailingComma": "all",
12 | "prettier.bracketSpacing": true,
13 | "prettier.arrowParens": "always",
14 | "prettier.requirePragma": false,
15 | "prettier.insertPragma": false,
16 | "prettier.proseWrap": "preserve",
17 | "prettier.htmlWhitespaceSensitivity": "css",
18 | "prettier.vueIndentScriptAndStyle": false,
19 | "prettier.endOfLine": "lf",
20 | "prettier.embeddedLanguageFormatting": "auto",
21 | "prettier.singleAttributePerLine": true
22 | }
23 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | vcc.surge.sh
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Sahadev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VCC 3
2 |
3 | VCC(Vue Compontent Creator)是 Low Code Generator 中独立的 Vue 组件代码编辑器。可以独立运行。当前你看到的是基于 Vue3 的 VCC 3 版本。
4 |
5 | **通过它可以通过拖拽快速完成 Vue 组件代码骨架的搭建。详见后文视频介绍链接。**
6 |
7 | > 点击这里快速预览效果:[https://vcc3.surge.sh/](https://vcc3.surge.sh/) 当前已经升级至 Vue3 + Vite。
8 |
9 | #### 使用示例
10 |
11 | 请移步至使用 Demo:[https://github.com/sahadev/vcc3-use-demo](https://github.com/sahadev/vcc3-use-demo)
12 |
13 | ## 本地如何运行此项目
14 |
15 | 首先进行安装:
16 |
17 | ```sh
18 | npm i
19 | ```
20 |
21 | 再进行启动(Vite):
22 |
23 | ```
24 | npm run dev
25 | ```
26 |
27 | 运行完成后,就可以访问[http://localhost:9818/](http://localhost:9818/)预览效果了.
28 |
29 | ## 使用介绍
30 |
31 | 此前在 B 站上录了两段视频。可以通过这两段视频简单了解如何使用它:
32 | [【拖拽式Vue组件代码生成平台(LCG)介绍视频】 https://www.bilibili.com/video/BV17h411f7g2/)
33 | [【拖拽式Vue组件代码生成平台(LCG)介绍视频】 https://www.bilibili.com/video/BV17h411f7g2/)
34 |
35 | ### 说明文档
36 |
37 | [https://vcc3-docs.surge.sh/#/](https://vcc3-docs.surge.sh/#/)
38 |
39 | ### 功能更新日志
40 |
41 | - 2022 年 03 月 16 日 支持生成单页 Html,支持 Vue2 以及 Vue3,并支持一键部署至 VCC 服务器。
42 | - 2023 年 12 月 06 日 更新 Element 组件库版本、更新 Vue 框架版本。
43 |
44 | ### 核心仓库
45 |
46 | VCC 依赖于一个核心的代码转换库:[vue-component-code-creater](https://github.com/sahadev/vue-component-code-creater),通过这个库来完成 Vue 文件的解析和 Vue 文件的生成。如果需要更改核心实现,可通过此库提供的源码进行修改。
47 |
48 | ## 贡献
49 |
50 | 1. Fork 仓库
51 | 2. 创建分支 (`git checkout -b my-new-feature`)
52 | 3. 提交修改 (`git commit -am 'Add some feature'`)
53 | 4. 推送 (`git push origin my-new-feature`)
54 | 5. 创建 PR
55 |
56 | ## 欢迎 fork 和反馈
57 |
58 | 如有建议或意见,欢迎在 github [issues](https://github.com/sahadev/vue-component-creater-ui/issues) 区提问
59 |
60 | ## 协议
61 |
62 | 本仓库遵循 [MIT 协议](http://www.opensource.org/licenses/MIT)
63 |
64 | ## 有疑问?
65 |
66 | 可以通过sahadev@foxmail.com给我发送邮件,我会及时回复的。
67 |
68 | 或者加群和大家一起讨论吧! 可以加我微信:SAHADEV-smile,我拉你入群。加我微信时请备注 VCC。
69 |
70 | 另外我也特别希望可以和大家一起做这个项目。这个项目目前主要面对的是前端开发者。后期可以面向后端开发者与产品与 UE。
71 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 拖拽式Vue组件代码生成编辑器(VCC)
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/introduction.md:
--------------------------------------------------------------------------------
1 | # vue-component-creater
2 |
3 | 拖拽式的Vue组件生成平台
4 |
5 | 正式环境地址:https://lc.100tal.com/#/
6 |
7 | ## Project setup
8 |
9 | ```
10 | yarn install
11 | ```
12 |
13 | ### Compiles and hot-reloads for development
14 |
15 | ```
16 | yarn run serve
17 | ```
18 |
19 | ### Compiles and minifies for production
20 |
21 | ```
22 | yarn run build
23 | ```
24 |
25 | ### Lints and fixes files
26 |
27 | ```
28 | yarn run lint
29 | ```
30 |
31 | ### 优势:
32 |
33 | - 搭建速度快。
34 | - 效果可视化。
35 | - 代码实时生成。
36 | - 提供主流预制模板。
37 | - 无需每次查找组件源代码,节省查找时间。
38 | - 提供主流预制属性。
39 | - 支持属性、模板自行扩展。
40 |
41 | ### 规划:
42 |
43 | 目前仅集成了部分市面上流行的组件库。组件库的集成希望可以通过开源的方式让大家自助添加。
44 |
45 | 添加的流程为:
46 | 在 src/rawComponents 目录下建立以组件名为名称的文件夹(例如:element),并在该文件夹内提供一个 index.vue 文件,然后再将此文件引入到 src/components/RawComponents.vue 文件中。
47 |
48 | \*.vue 文件建议以组件类型划分,例如 Botton 类的组件就可以都放在 button.vue 文件中,然后由 index.vue 文件引用。这样 Botton 的路径为:src/rawComponents/element/button.vue
49 |
50 | 当组件添加完毕后,需要给组件内的元素添加 lc-mark 的标志,这样这个组件才可以被**拖拽**。例如 element 的 Button:
51 |
52 | ```html
53 | 默认按钮
54 | ```
55 |
56 | 当一切就就绪后,需要对所有的组件进行重新编译才可以正常使用,需执行 npm run compileComponent 命令。此命令会对 src/rawComponents 下所有的组件进行重新编译。如果有不希望编译的,请在 src/script/compile.js 中修改 ignore 属性。
57 |
58 | > **需要注意的是:** 一类组件的 index.vue 文件最好不要编译,因为对这个文件内容的顺序是有要求的,比如**分割线**。而编译会使组件内元素的顺序重新排布(2020年12月28日已经将此问题修复)。
59 |
60 | ### TODO:
61 |
62 | - 常用模板
63 | - 预制常用属性
64 | - 高亮当前属性编辑元素-跑马灯效果 -> 左下箭头高亮展示(2020 年 09 月 21 日效果不好,2020年12月28日已通过outline实现)✔️
65 | - 尝试从 vue 运行时入手,获取更多原始信息(2020 年 09 月 21 日开始已经废弃,通过迂回方式获得更多信息)✔️
66 | - 提供调试控制台,输出当前代码结构关系图,类似 vue-tools(2020 年 10 月 02 日 已实现基本的文本形式的结构输出,需要树形的可折叠结构)。✔️
67 | - 将组件库、预制属性、属性解析规则挪至单独的项目中。
68 | - 支持生成预制的 data,methods。✔️
69 | - 我的常用:组件元素、整个组件。
70 | - 接入自有的数据统计工具:神策。✔️
71 |
72 | ---
73 |
74 | 使用过程中发现的问题:
75 |
76 | - 1.不支持删除某个元素。 ✔️
77 | - 2.编辑 input 会造成高亮框混乱。 ✔️
78 | - 3.没有下拉组件。✔️
79 | - 4.删除外框内容子元素会一同被清空(两个 div 嵌套)。✔️
80 | - 5.input 标签无法生成代码。 ✔️
81 | - 6.提供一部分常用的组合组件。key:input ✔️
82 | - 7.取消对 Html 元素的排序。需要更改 json2xml 的数据结构。Json 数据生成规则也需要变动。 ✔️
83 | - 8.提供一些常见的模板。✔️
84 | - 9.对复合组件还不支持 ✔️
85 | - 10.针对某些组件预设样式。例如分页组件一定是在最下面且宽度为 100%的。
86 | - 11.要对大型表单的搭建良好支持(例如课程管理页面的搭建https://wiki.zhiyinlou.com/pages/viewpage.action?pageId=60305227)。
87 | - 12.预制更多常用的复合组件:{ key: value }。
88 | - 13.更改父组件的属性,子组件被删除。✔️
89 | - 14.对于:inline="true"的解析与生成需要完整支持,目前只能输出:inline。✔️
90 |
91 | ---
92 |
93 | 2020 年 09 月 16 日 目前的进展:
94 | - 1.当下已支持通过更改 Vue-loader 实现在运行时拿到组件源代码。具体可以查看 CommitID:c66593ff87c07c60670e634f50f49e030f68b63b,该次提交已可以在控制台看到源代码输出。
95 | - 2.重新定义源代码的获取方式: 通过将源代码解析为 Json 结构, 然后在编辑时通过对对象的操作实现代码的生成。所以这里涉及到对 html 的解析与生成。解析部分需要重写,生成逻辑之前已完成。
96 |
97 | 2020 年 09 月 17 日 目前进展:
98 | 嵌套结构基本稳定,可进行各种组合
99 | 输出代码已移除 ID 属性
100 | TODO: 稳固属性编辑,输出代码值类型默认为字符串
101 |
102 | 2020 年 09 月 19 日
103 | 编辑的基本单位应当以最小组件,即不能再拆分的单位
104 |
105 | 2020 年 09 月 22 日
106 | 右上角上显示的统计:
107 |
108 | - 1.累计访问次数(PV)。
109 | - 2.生成的组件数(点击复制的统计)。
110 | - 3.基础组件数(统计可拖拽的基础组件)。
111 |
112 | 2020 年 09 月 23 日
113 | 开始规划组件库,分设常用、组件厂商,下设组件类型
114 | 需要彻底修改 xml 解析库,保证原始顺序不出错,或者不要编译 index.vue
115 |
116 | 2020 年 10 月 16 日 1.支持拖入时位置确认,可以在前、在中、在后,目前只能追加最后。 2.支持已拖入的拖动调整。
117 |
118 | 2020年11月10日
119 |
120 | - 需要在成熟的组件库下添加超链接,方便直接去该网站获得具体的属性说明。
121 | - 完善iView组件库
122 | - 集成Quasar组件库:http://www.quasarchs.com/
123 | - 这个东西是主要针对什么人群,要解决什么痛点?
124 |
125 | 2020年11月17日
126 |
127 | - 属性编辑增加按钮需要调整:因为最后一个属性是无法删除的。
128 | - 元素的删除支持快捷键.
129 | - Form表单增加内容为按钮的。
130 | - 属性的输入要全部为字符串,不能为boolean。
131 | - 无法在div内部的后面再追加。:center中
132 | - 快速搜索输入框已有内容无法再次弹出
133 | - 复制代码有问题,内容复制不上 ✔️
134 |
135 | 2020年12月04日
136 | 在编译form-base文件时,发现html与js都出了问题,问题发生在没有闭合标签的img以及下面这段JS代码上:
137 | ```js
138 |
139 | const isJPG = file.type === 'image/jpeg';
140 | const isLt2M = file.size / 1024 / 1024 < 2;
141 |
142 | if (!isJPG) {
143 | this.$message.error('上传头像图片只能是 JPG 格式!');
144 | }
145 | if (!isLt2M) {
146 | this.$message.error('上传头像图片大小不能超过 2MB!');
147 | }
148 | return isJPG && isLt2M;
149 |
150 | ```
151 |
152 | **注意:** 在编辑组件库时,在script标签内部慎用大于小于号,会导致预编译阶段文本内容解析出错!
153 |
154 | 2021年01月08日10:27:25
155 | 在组件的css中不能使用scss的写法,否则编译Vue文件会不通过
156 |
157 | 2021年01月11日
158 | 今天支持重新命名文件中重复声明的变量,但**在文件中不能声明与prop相同的声明**
159 |
160 | ---
161 |
162 | ### 下一步重点工作
163 |
164 | 1.做大量实践,探索组件单元最合适的组成。✔️
165 | 2.hover 方式标记组件的区域范围。✔️
166 | 3.增加沟通群二维码。❎
167 | 4.增加UI库vuetifyjs:https://vuetifyjs.com/zh-Hans/
168 | 5.增加UI库kendo:https://www.telerik.com/kendo-vue-ui
169 |
170 | ---
171 |
172 | ### 核心原理介绍
173 |
174 | 我们知道,在编写后的 vue 代码在运行时会生成实际的 Html 代码,而组件生成平台的职责是将这些 Html 再转换为 vue 代码。
175 |
176 | 为了达到这样的目的,我们目前可行的思路是:将原始的代码文件进行预编译:对指定的 vue 组件分配一个随机 ID,并将这个 vue 文件的代码结构转换为 Json,以 map 的形式存储于 map.js 文件中。在运行时,将 map.js 文件加载进内存。当拖动某个被 lc-mark 标记的元素时,我们可获得这个元素相应的 ID,再通过这个 ID 到 map 中查找,于是获得了对应的原始代码结构。当拖入到某个元素中时,也通过相同的方式获得目标元素的原始代码,再将这两部分原始代码合并,并建立上下级关系。随后,通过新的代码结构,分析对应的@click、v-model、class 等我们所关注的属性,然后再将其生成对应的代码插入到将要生成的 Vue 组件中。如此,便形成了一个较为完整的 Vue 组件代码。
177 |
178 | 为了实现以上思路,有几项关键技术:
179 |
180 | - 对 Vue 组件的解析与生成
181 | - 上下级组件之间的数据结构关系
182 | - html 元素与 Vue 代码之间吻合的对应关系
183 | - Vue 代码的关键字解析,如@click
184 | - 将代码转换为对象,将对象转换为代码
185 | - 辅助线的定位与绘制
186 |
187 | ### 标准
188 |
189 | 本项目中所使用的标准色为:
190 |
191 | - 蓝色:#435466;
192 | - 绿色:#4dba87;
193 | - 红色:#ff6159;
194 |
195 | ---
196 |
197 | #### 不能接入到本平台的或者会出现不符合预期的元素
198 |
199 | - el-dialog 无法直接显示在左侧组件栏中(2020年12月28日已通过一个按钮代表解决)
200 | - el-input-number 拖拽时会抢占焦点,无法拖拽 ✔️
201 | - el-select lc-mark标记的元素与实际运行时的元素不是同一个,无法找到其原始代码 ✔️
202 | - el-switch 原因与el-select一致 ✔️ 通过检视图解决
203 | - el-time-select 原因与el-select一致 ✔️
204 | - el-image 因为某种原因,没有正常完成初始化工作 ✔️
205 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lcg-vcc3",
3 | "description": "Low Code Generator -> Vue Component Creater",
4 | "version": "0.5.3",
5 | "private": false,
6 | "keywords": [
7 | "low-code",
8 | "editor"
9 | ],
10 | "main": "./dist/vcc3.umd.min.js",
11 | "scripts": {
12 | "dev": "vite --port 9818",
13 | "serve": "vite --port 9818",
14 | "build:release": "vite build --base=https://static.imonkey.xueersi.com/vcc3/",
15 | "build": "vue-cli-service build --report --target lib --name vcc3 './src/components-v2/VCC.vue'",
16 | "build:win": "vue-cli-service build --report --target lib --name vcc3 ./src/components-v2/VCC.vue && node ./src/script/distClear.js",
17 | "compileAndbuild:dev": "npm run compileComponent && vue-cli-service build",
18 | "lint": "vue-cli-service lint",
19 | "build:prod": "vue-cli-service build --mode production",
20 | "compileAndBuild:prod": "npm run compileComponent && vue-cli-service build --mode production",
21 | "compileComponent": "node ./src/script/compile.js",
22 | "debugParser": "node ./src/test/parserJsCode.js"
23 | },
24 | "dependencies": {
25 | "@element-plus/icons-vue": "^2.3.1",
26 | "@vitejs/plugin-vue": "^1.10.0",
27 | "@vue/compiler-sfc": "^3.2.22",
28 | "ant-design-vue": "^3.0.0-alpha.14",
29 | "axios": "^0.21.4",
30 | "codemirror-editor-vue3": "^0.2.4",
31 | "copy-to-clipboard": "^3.3.1",
32 | "crypto-random-string": "^3.3.1",
33 | "css": "^3.0.0",
34 | "css-scoped": "^1.0.0",
35 | "echarts": "^5.4.3",
36 | "ejs": "^3.1.6",
37 | "element-plus": "^2.4.3",
38 | "escodegen": "^2.0.0",
39 | "espree": "^7.3.0",
40 | "eventemitter3": "^4.0.7",
41 | "file-saver": "^2.0.2",
42 | "fs-extra": "^9.0.1",
43 | "glob": "^7.1.6",
44 | "keymaster": "^1.6.2",
45 | "lodash-es": "^4.17.21",
46 | "nanoid": "^3.1.30",
47 | "prettier": "^2.4.0",
48 | "split.js": "^1.6.2",
49 | "vant": "^3.3.7",
50 | "vue": "^3.3.10",
51 | "vue-echarts": "^6.6.2",
52 | "vue-github-button": "^1.3.0",
53 | "vue-template-compiler": "^2.6.14",
54 | "vuedraggable": "^4.1.0",
55 | "vuex": "^4.0.2"
56 | },
57 | "devDependencies": {
58 | "@babel/generator": "^7.11.6",
59 | "@rollup/plugin-dynamic-import-vars": "^1.4.1",
60 | "@vue/eslint-config-airbnb": "^5.0.2",
61 | "babel-eslint": "^10.0.3",
62 | "eslint": "^6.7.2",
63 | "eslint-plugin-import": "^2.20.1",
64 | "eslint-plugin-vue": "^6.1.2",
65 | "is-obj": "^3.0.0",
66 | "is-regexp": "^3.0.0",
67 | "lint-staged": "^9.5.0",
68 | "sass": "^1.25.0",
69 | "sass-loader": "^8.0.2",
70 | "@vue/cli-service": "^5.0.8",
71 | "vite": "^2.6.14"
72 | },
73 | "eslintConfig": {
74 | "root": true,
75 | "env": {
76 | "node": true
77 | },
78 | "extends": [
79 | "plugin:vue/essential"
80 | ],
81 | "parserOptions": {
82 | "parser": "babel-eslint"
83 | },
84 | "rules": {}
85 | },
86 | "browserslist": [
87 | "> 1%",
88 | "last 2 versions"
89 | ],
90 | "gitHooks": {
91 | "pre-commit": "lint-staged"
92 | },
93 | "lint-staged": {
94 | "*.{js,jsx,vue}": [
95 | "vue-cli-service lint",
96 | "git add"
97 | ]
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 拖拽式Vue组件代码生成编辑器(VCC)
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icon/add-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/code-download-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/code-working-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/copy-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/eye-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/login-after.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/login-before.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/power-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/trash-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/tree-structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/icon/tree-structure.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo/antd-n.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/logo/element-n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo/element-n.png
--------------------------------------------------------------------------------
/src/assets/logo/html-n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo/html-n.png
--------------------------------------------------------------------------------
/src/assets/logo/iview-n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo/iview-n.png
--------------------------------------------------------------------------------
/src/assets/logo/quasar-n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo/quasar-n.png
--------------------------------------------------------------------------------
/src/assets/logo/vant-n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sahadev/vue-component-creater-ui/ddcdf5d08ab33592cc2981ee81279b0394ce95d7/src/assets/logo/vant-n.png
--------------------------------------------------------------------------------
/src/assets/nestable.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Style for nestable
3 | */
4 | .nestable {
5 | position: relative;
6 | }
7 |
8 | .nestable-rtl {
9 | direction: rtl;
10 | }
11 |
12 | .nestable .nestable-list {
13 | margin: 0;
14 | padding: 0 0 0 40px;
15 | list-style-type: none;
16 | }
17 |
18 | .nestable-rtl .nestable-list {
19 | padding: 0 40px 0 0;
20 | }
21 |
22 | .nestable>.nestable-list {
23 | padding: 0;
24 | }
25 |
26 | .nestable-item,
27 | .nestable-item-copy {
28 | margin: 10px 0 0;
29 | }
30 |
31 | .nestable-item:first-child,
32 | .nestable-item-copy:first-child {
33 | margin-top: 0;
34 | }
35 |
36 | .nestable-item .nestable-list,
37 | .nestable-item-copy .nestable-list {
38 | margin-top: 10px;
39 | }
40 |
41 | .nestable-item {
42 | position: relative;
43 | }
44 |
45 | .nestable-item.is-dragging .nestable-list {
46 | pointer-events: none;
47 | }
48 |
49 | .nestable-item.is-dragging * {
50 | opacity: 0;
51 | filter: alpha(opacity=0);
52 | }
53 |
54 | .nestable-item.is-dragging:before {
55 | content: ' ';
56 | position: absolute;
57 | top: 0;
58 | left: 0;
59 | right: 0;
60 | bottom: 0;
61 | background-color: rgba(106, 127, 233, 0.274);
62 | border: 1px dashed rgb(73, 100, 241);
63 | -webkit-border-radius: 5px;
64 | border-radius: 5px;
65 | }
66 |
67 | .nestable-drag-layer {
68 | position: fixed;
69 | top: 0;
70 | left: 0;
71 | z-index: 100;
72 | pointer-events: none;
73 | }
74 |
75 | .nestable-rtl .nestable-drag-layer {
76 | left: auto;
77 | right: 0;
78 | }
79 |
80 | .nestable-drag-layer>.nestable-list {
81 | position: absolute;
82 | top: 0;
83 | left: 0;
84 | padding: 0;
85 | background-color: rgba(106, 127, 233, 0.274);
86 | }
87 |
88 | .nestable-rtl .nestable-drag-layer>.nestable-list {
89 | padding: 0;
90 | }
91 |
92 | .nestable [draggable="true"] {
93 | cursor: move;
94 | }
95 |
96 | .nestable-handle {
97 | display: inline;
98 | }
99 |
100 | .expandable .has-children ol {
101 | display: none;
102 | }
103 |
104 | .expandable .has-children.is-active>ol {
105 | display: block;
106 | }
--------------------------------------------------------------------------------
/src/components-v2/ToolsBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
47 |
48 |
49 |
97 |
98 |
116 |
117 |
149 |
--------------------------------------------------------------------------------
/src/components/Code.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 | 使用代码前请确认相应的组件库已集成至项目
7 |
8 |
9 |
10 | 输出形式:
11 |
12 | Vue
13 | 单页Html
14 |
15 |
16 |
17 |
18 | 选择所使用的组件库:
19 |
20 | Element UI
21 | Ant Design
22 | Vant
23 |
24 |
25 |
26 | 选择Vue版本:
27 |
28 | Vue 2
29 | Vue 3
30 |
31 |
32 |
33 | 代码获取方式:
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 一键部署至VCC静态页面托管服务
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
185 |
186 |
--------------------------------------------------------------------------------
/src/components/CodeEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
125 |
126 |
143 |
144 |
--------------------------------------------------------------------------------
/src/components/CodeStructure.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
组件结构检视图
6 |
7 | Components
8 | Structure
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
119 |
120 |
--------------------------------------------------------------------------------
/src/components/JSCodeEditorDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
确认修改
8 |
请检查语法错误:{{error}}
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 | 帮助与说明
18 |
19 |
Tips: 建议看一下使用说明
20 |
21 |
22 |
23 |
24 |
25 |
26 |
129 |
130 |
--------------------------------------------------------------------------------
/src/components/Main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 | Essential Links
5 |
6 | 曙光?
7 |
8 | Button
9 |
10 |
11 |
12 |
22 |
23 |
32 |
33 |
62 |
--------------------------------------------------------------------------------
/src/components/Preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/VueCodeParseDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
开始解析
8 |
Tips: 解析成功后VCC将展示解析后的效果
9 |
请检查语法错误:{{error}}
10 |
11 |
12 |
13 |
14 |
15 |
99 |
100 |
--------------------------------------------------------------------------------
/src/components/nested.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | {{ getRawComponentKey(element) }}
7 |
8 |
9 |
10 |
11 |
12 |
81 |
--------------------------------------------------------------------------------
/src/components/prism.css:
--------------------------------------------------------------------------------
1 | /* PrismJS 1.20.0
2 | https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript */
3 | /**
4 | * okaidia theme for JavaScript, CSS and HTML
5 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/
6 | * @author ocodia
7 | */
8 |
9 | code,
10 | pre {
11 | color: #f8f8f2;
12 | background: none;
13 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
14 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
15 | font-size: 1em;
16 | text-align: left;
17 | white-space: pre;
18 | word-spacing: normal;
19 | word-break: normal;
20 | word-wrap: normal;
21 | line-height: 1.5;
22 |
23 | -moz-tab-size: 4;
24 | -o-tab-size: 4;
25 | tab-size: 4;
26 |
27 | -webkit-hyphens: none;
28 | -moz-hyphens: none;
29 | -ms-hyphens: none;
30 | hyphens: none;
31 | }
32 |
33 | /* Code blocks */
34 | pre {
35 | padding: 1em;
36 | margin: 0.5em 0;
37 | overflow: auto;
38 | border-radius: 0.3em;
39 | }
40 |
41 | :not(pre) > code,
42 | pre {
43 | background: #272822;
44 | }
45 |
46 | /* Inline code */
47 | :not(pre) > code {
48 | padding: 0.1em;
49 | border-radius: 0.3em;
50 | white-space: normal;
51 | }
52 |
53 | .token.comment,
54 | .token.prolog,
55 | .token.doctype,
56 | .token.cdata {
57 | color: slategray;
58 | }
59 |
60 | .token.punctuation {
61 | color: #f8f8f2;
62 | }
63 |
64 | .token.namespace {
65 | opacity: 0.7;
66 | }
67 |
68 | .token.property,
69 | .token.tag,
70 | .token.constant,
71 | .token.symbol,
72 | .token.deleted {
73 | color: #f92672;
74 | }
75 |
76 | .token.boolean,
77 | .token.number {
78 | color: #ae81ff;
79 | }
80 |
81 | .token.selector,
82 | .token.attr-name,
83 | .token.string,
84 | .token.char,
85 | .token.builtin,
86 | .token.inserted {
87 | color: #a6e22e;
88 | }
89 |
90 | .token.operator,
91 | .token.entity,
92 | .token.url,
93 | .language-css .token.string,
94 | .style .token.string,
95 | .token.variable {
96 | color: #f8f8f2;
97 | }
98 |
99 | .token.atrule,
100 | .token.attr-value,
101 | .token.function,
102 | .token.class-name {
103 | color: #e6db74;
104 | }
105 |
106 | .token.keyword {
107 | color: #66d9ef;
108 | }
109 |
110 | .token.regex,
111 | .token.important {
112 | color: #fd971f;
113 | }
114 |
115 | .token.important,
116 | .token.bold {
117 | font-weight: bold;
118 | }
119 | .token.italic {
120 | font-style: italic;
121 | }
122 |
123 | .token.entity {
124 | cursor: help;
125 | }
126 |
--------------------------------------------------------------------------------
/src/libs/UIComponentInit.js:
--------------------------------------------------------------------------------
1 | // 其它UI组件库应该在这里集成
2 | function loadVant() {
3 | (() => import("vant/lib/index.css"))();
4 | const vantLoadPromise = (() => import("vant"))();
5 | return vantLoadPromise;
6 | }
7 |
8 | function loadAntD() {
9 | (() => import("ant-design-vue/dist/antd.css"))();
10 | const vantLoadPromise = (() => import("ant-design-vue"))();
11 | return vantLoadPromise;
12 | }
13 |
14 | export default function loadCompontents() {
15 | return Promise.all([loadAntD(), loadVant()]);
16 | }
17 |
--------------------------------------------------------------------------------
/src/libs/bro-ele-config.js:
--------------------------------------------------------------------------------
1 |
2 | import { getRawComponentKey } from '@/utils/common'
3 | import { replaceRowID, removeAllID, updateLinkTree, linkRelationShip } from '@/utils/forCode'
4 |
5 | export function brotherEleEnum() {
6 | return [{
7 | name: 'el-option',
8 | ifInDoc: false // 这个组件是否默认在Dom上展示,如果不展示,则添加后不需要更新Dom,否则需要更新Dom
9 | },
10 | {
11 | name: 'el-table-column',
12 | ifInDoc: false
13 | },
14 | {
15 | name: 'el-checkbox',
16 | ifInDoc: true
17 | },
18 | {
19 | name: 'el-radio',
20 | ifInDoc: true
21 | }];
22 | }
23 |
24 | function checkIsInVaildElement(event) {
25 | return new Promise((resolve, reject) => {
26 | const target = event.path.find(item => item.attributes.lc_id.nodeValue);
27 | // 获取带有ID的原始结构,这个ID用于生成一个不同ID的副本
28 | const __rawVueInfo__ = window.treeWithID && window.treeWithID[target.attributes.lc_id.nodeValue];
29 | if (target && __rawVueInfo__) {
30 | const key = getRawComponentKey(__rawVueInfo__);
31 | const result = brotherEleEnum().find(item => item.name === key && item.ifInDoc);
32 | if (result) {
33 | resolve({ target, __rawVueInfo__ });
34 | } else {
35 | reject();
36 | }
37 | }
38 | })
39 | }
40 |
41 | export function copyBroCode(__rawVueInfo__){
42 | // 初始化原型
43 | let newDropObj = Object.create(__rawVueInfo__.__proto__);
44 | // 拷贝内部属性
45 | Object.assign(newDropObj, JSON.parse(JSON.stringify(__rawVueInfo__)));
46 |
47 | newDropObj.__proto__.parentCodeNode.__children.push(newDropObj);
48 | }
49 |
50 | /**
51 | * 这个方法是给控制区域使用的,增加兄弟组件时,控制区域会实时更新
52 | * @param {*} element
53 | */
54 | export function initElementHoverAction(element) {
55 | let currentBroInfo = null;
56 |
57 | const addBroIcon = document.querySelector('.add-bro');
58 |
59 | let isInBroIcon = false;
60 | addBroIcon.addEventListener('mouseover', event => {
61 | isInBroIcon = true;
62 | })
63 | addBroIcon.addEventListener('mouseout', event => {
64 | isInBroIcon = false;
65 | })
66 |
67 | element.addEventListener('mouseover', event => {
68 | event.stopPropagation();
69 | })
70 |
71 | return function () {
72 | // 初始化原型
73 | let newDropObj = Object.create(currentBroInfo.__rawVueInfo__.__proto__);
74 | // 拷贝内部属性
75 | Object.assign(newDropObj, JSON.parse(JSON.stringify(currentBroInfo.__rawVueInfo__)));
76 |
77 | // 有__key__键可以使在通过updateLinkTree更新结构时,内部的原型指向外部的原型
78 | newDropObj.__key__ = "__children";
79 |
80 | // 使新拖入的代码与原来的做脱离
81 | const newHtmlCode = replaceRowID(newDropObj, currentBroInfo.target.outerHTML);
82 | // 这里不能是任意的target,必须是已存在代码树,有引用链的节点
83 | currentBroInfo.target.parentNode.insertAdjacentHTML("beforeend", newHtmlCode);
84 |
85 | newDropObj.__proto__.parentCodeNode.__children.push(newDropObj);
86 |
87 | // 将所有子节点指向父节点
88 | linkRelationShip(newDropObj);
89 |
90 | // 更新到一个tree上面,维持引用
91 | updateLinkTree(newDropObj);
92 |
93 | // 删除所有的ID
94 | removeAllID(newDropObj);
95 | }
96 | }
--------------------------------------------------------------------------------
/src/libs/bundle-html2json-common.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //该文件用于解析HTML,输出为Object对象
4 |
5 | Object.defineProperty(exports, '__esModule', { value: true });
6 |
7 | const htmlparser2 = require("htmlparser2");
8 |
9 | function getNodeContent(node) {
10 | return node[Object.keys(node)[0]];
11 | }
12 |
13 | /**每个节点的表示方法为:
14 | {
15 | tagname: {
16 | key1: value1,
17 | key2: value2,
18 | __children: [
19 | {
20 |
21 | }
22 | ]
23 | }
24 | }*/
25 | function generateNewNode(tagName, attributes = {}) {
26 | // 构建新节点
27 | const newNode = {};
28 | newNode[tagName] = attributes;
29 | attributes.__children = [];
30 | return newNode;
31 | }
32 |
33 | function parseHtml(htmlData) {
34 | return new Promise((resolve, reject) => {
35 | // 根节点
36 | const root = generateNewNode('root');
37 | // 当前访问的节点
38 | let currentAccessObject = root;
39 | // 之前访问的节点数组
40 | let lastAccessStack = [root];
41 |
42 | // options docment: https://github.com/fb55/htmlparser2/wiki/Parser-options
43 | const parser = new htmlparser2.Parser({
44 | onopentag(tagname, attributes) {
45 | const newNode = generateNewNode(tagname, attributes);
46 | lastAccessStack.push(newNode);
47 | getNodeContent(currentAccessObject).__children.push(newNode);
48 | currentAccessObject = newNode;
49 | },
50 | ontext(text) {
51 | if (text.trim()) {
52 | getNodeContent(currentAccessObject).__text__ = text.trim();
53 | }
54 | },
55 | onclosetag(tagname) {
56 | lastAccessStack.pop();
57 | currentAccessObject = lastAccessStack[lastAccessStack.length - 1];
58 | },
59 | onend() {
60 | resolve(root);
61 | },
62 |
63 | onerror(error) {
64 | reject(error);
65 | }
66 | }, {
67 | lowerCaseAttributeNames: false,
68 | lowerCaseTags: false,
69 | });
70 | parser.write(
71 | htmlData
72 | );
73 |
74 | parser.end();
75 | })
76 | }
77 |
78 | async function html2Json(htmlData) {
79 | return await parseHtml(htmlData);
80 | }
81 |
82 | exports.html2Json = html2Json;
83 |
--------------------------------------------------------------------------------
/src/libs/bundle-html2json-esm.js:
--------------------------------------------------------------------------------
1 | //该文件用于解析HTML,输出为Object对象
2 | import { Parser } from "htmlparser2"
3 |
4 | function getNodeContent(node) {
5 | return node[Object.keys(node)[0]];
6 | }
7 |
8 | /**每个节点的表示方法为:
9 | {
10 | tagname: {
11 | key1: value1,
12 | key2: value2,
13 | __children: [
14 | {
15 |
16 | }
17 | ]
18 | }
19 | }*/
20 | function generateNewNode(tagName, attributes = {}) {
21 | // 构建新节点
22 | const newNode = {};
23 | newNode[tagName] = attributes;
24 | attributes.__children = [];
25 | return newNode;
26 | }
27 |
28 | function parseHtml(htmlData) {
29 | return new Promise((resolve, reject) => {
30 | // 根节点
31 | const root = generateNewNode('root');
32 | // 当前访问的节点
33 | let currentAccessObject = root;
34 | // 之前访问的节点数组
35 | let lastAccessStack = [root];
36 |
37 | // options docment: https://github.com/fb55/htmlparser2/wiki/Parser-options
38 | const parser = new Parser({
39 | onopentag(tagname, attributes) {
40 | const newNode = generateNewNode(tagname, attributes);
41 | lastAccessStack.push(newNode);
42 | getNodeContent(currentAccessObject).__children.push(newNode);
43 | currentAccessObject = newNode;
44 | },
45 | ontext(text) {
46 | if (text.trim()) {
47 | getNodeContent(currentAccessObject).__text__ = text.trim();
48 | }
49 | },
50 | onclosetag(tagname) {
51 | lastAccessStack.pop();
52 | currentAccessObject = lastAccessStack[lastAccessStack.length - 1];
53 | },
54 | onend() {
55 | resolve(root);
56 | },
57 |
58 | onerror(error) {
59 | reject(error);
60 | }
61 | }, {
62 | lowerCaseAttributeNames: false,
63 | lowerCaseTags: false,
64 | });
65 | parser.write(
66 | htmlData
67 | );
68 | parser.end();
69 | })
70 | }
71 |
72 | async function html2Json(htmlData) {
73 | return await parseHtml(htmlData);
74 | }
75 |
76 | export { html2Json };
77 |
--------------------------------------------------------------------------------
/src/libs/code-generator-factory.js:
--------------------------------------------------------------------------------
1 | // 代码生成对象工厂,每次初始化需要获取一个新的实例,所以工厂方法模式最为适用
2 | import { CodeGenerator } from "./bundle-core-esm";
3 | import { checkIsDataDirectives, checkIsMethodDirectives } from '@/libs/directiveCheck';
4 | import stringifyObject from '@/libs/stringify-object'
5 |
6 | export function createNewCodeGenerator() {
7 | return new CodeGenerator({
8 | convertDataResult: function (dataCodeArr) {
9 | let result = dataCodeArr;
10 | // 干扰数据结果
11 | if (dataCodeArr.length > 0) {
12 | result = dataCodeArr.map((item) => {
13 | const kav = item.split(":");
14 | const key = kav[0];
15 | // 这里获取的是原始data数据
16 | if (window.dataSourceMap[key] || window.dataSourceMap[key] == 0) {
17 | return `${key}: ${stringifyObject(window.dataSourceMap[key], {
18 | indent: " ",
19 | singleQuotes: false,
20 | })}`;
21 | } else {
22 | return item;
23 | }
24 | });
25 | }
26 |
27 | return result;
28 | },
29 | convertMethodResult(methodCodeArr) {
30 | let result = methodCodeArr;
31 | if (methodCodeArr && methodCodeArr.length > 0) {
32 | result = methodCodeArr.map(methodItem => {
33 | const kav = methodItem.split(":");
34 | const key = kav[0];
35 | // 这里获取的是原始data数据
36 | if (window.methodSourceMap && window.methodSourceMap[key]) {
37 | return `${key}: ${window.methodSourceMap[key]}`;
38 | } else {
39 | return methodItem;
40 | }
41 | })
42 | }
43 | return result;
44 | },
45 | preConvertStyleResult(selectorSet) {
46 | let result = '';
47 | const selectorValues = selectorSet.values();
48 | const selectorKeys = Object.keys(window.styleSourceMap);
49 |
50 | for (const selector of selectorValues) {
51 | // styleSourceMap中保留了所有的原始选择器,这里只处理class的
52 | const findResults = selectorKeys.filter(key => {
53 | return key === `.${selector}` || key.indexOf(`.${selector} `) >= 0 || key.indexOf(` .${selector}`) >= 0;
54 | })
55 |
56 | if (findResults && findResults.length > 0) {
57 | findResults.forEach(findResult => {
58 | result += `${findResult} { ${window.styleSourceMap[findResult]} }\n`;
59 | })
60 | selectorSet.delete(selector);
61 | }
62 | }
63 | return result;
64 | },
65 | checkIsDataDirectives,
66 | checkIsMethodDirectives,
67 | unSupportedKey: function (key, value) {
68 | // 对于这一类需要警示,因为可能是被遗漏的
69 | if (/^v-/g.test(key) || /^:+/g.test(key)) {
70 | console.warn(`可能遗漏了这些: ${key}: ${value}`);
71 | } else {
72 | console.info(`unsupport key: ${key}: ${value}`);
73 | }
74 | }
75 | });
76 | }
--------------------------------------------------------------------------------
/src/libs/directiveCheck.js:
--------------------------------------------------------------------------------
1 | export function checkIsDataDirectives(key) {
2 | return [
3 | ':prop',
4 | 'v-for',
5 | ':data',
6 | ':src',
7 | ':model',
8 | ':rules',
9 | ':total',
10 | ':current-page',
11 | 'v-model',
12 | 'v-if',
13 | ':reverse',
14 | ':show-file-list',
15 | ':file-list'].includes(key) || /^:+/g.test(key);
16 | }
17 |
18 | export function checkIsMethodDirectives(key) {
19 | return [
20 | ':before-close',
21 | ':on-preview',
22 | ':on-remove',
23 | ':before-remove',
24 | ':on-exceed',
25 | ':on-success',
26 | ':format',
27 | ':before-upload'].includes(key);
28 | }
--------------------------------------------------------------------------------
/src/libs/presetAttribute.js:
--------------------------------------------------------------------------------
1 | export default {
2 | "el-button": {
3 | "@click": "onButtonClick",
4 | size: "small",
5 | },
6 | "el-radio-group": {
7 | "v-model": "radio",
8 | },
9 | "el-radio": {
10 | ":label": 0,
11 | },
12 | "el-checkbox-group": {
13 | },
14 | "el-link": {
15 | "@click": "onClickLink",
16 | },
17 | "el-select": {
18 | size: "small",
19 | },
20 | "el-input": {
21 | "v-model": "input",
22 | placeholder: "请输入内容",
23 | size: "small",
24 | class: "input",
25 | },
26 | "el-pagination": {
27 | "@size-change": "handleSizeChange",
28 | "@current-change": "handleCurrentChange",
29 | ":current-page": "currentPage",
30 | ":page-sizes": "[10, 20, 50, 100]",
31 | ":page-size": "10",
32 | layout: "total, sizes, prev, pager, next, jumper",
33 | ":total": "234",
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/src/libs/singleIndexOutput.js:
--------------------------------------------------------------------------------
1 | import { parseComponent } from "vue-template-compiler/browser";
2 | import ejs from "ejs";
3 |
4 | const outputVueTemplate = `
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Page
13 |
16 | <% for(var i = 0; i < cssLibs.length; ++i) { %>
17 | <% } %>
18 |
19 |
20 |
21 | <%- templateHolder %>
22 |
23 |
24 | <% for(var i = 0; i < scriptLibs.length; ++i) { %>
25 | <% } %>
26 |
33 | `;
34 |
35 | const libAddressMap = {
36 | vue: {
37 | js: ["https://cdn.bootcdn.net/ajax/libs/vue/3.2.31/vue.global.min.js"],
38 | css: "",
39 | },
40 | ele: {
41 | js: ["https://cdn.bootcdn.net/ajax/libs/element-plus/2.1.0/index.full.min.js"],
42 | css: "https://cdn.bootcdn.net/ajax/libs/element-plus/2.1.0/theme-chalk/index.min.css",
43 | libName: "ElementPlus",
44 | },
45 | antd: {
46 | js: [
47 | "https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.8/dayjs.min.js",
48 | "https://cdn.bootcdn.net/ajax/libs/ant-design-vue/3.0.0-alpha.14/antd.min.js",
49 | ],
50 | css: "https://cdn.bootcdn.net/ajax/libs/ant-design-vue/3.0.0-alpha.14/antd.min.css",
51 | libName: "antd",
52 | },
53 | vant: {
54 | js: ["https://cdn.bootcdn.net/ajax/libs/vant/3.3.7/vant.min.js"],
55 | css: "https://cdn.bootcdn.net/ajax/libs/vant/3.3.7/index.min.css",
56 | libName: "vant",
57 | },
58 | };
59 |
60 | export default function (vueCode, dependenciesLibs, vue3 = true) {
61 | const { template, script, styles, customBlocks } = parseComponent(vueCode);
62 |
63 | let newScript = script.content.replace(/\s*export default\s*/, "");
64 |
65 | const tempDependenciesLibs = dependenciesLibs.slice();
66 | const tempLibAddressMap = vue3 ? libAddressMap: libAddressMapForVue2
67 |
68 | tempDependenciesLibs.unshift("vue");
69 |
70 | const output = ejs.render(outputVueTemplate, {
71 | cssLibs: tempDependenciesLibs.map((item) => tempLibAddressMap[item].css).filter((item) => !!item),
72 | scriptLibs: tempDependenciesLibs
73 | .map((item) => tempLibAddressMap[item].js)
74 | .flat()
75 | .filter((item) => !!item),
76 | vue3,
77 | vue3UseLib: tempDependenciesLibs
78 | .filter((item) => item != "vue")
79 | .map((item) => tempLibAddressMap[item].libName),
80 | style: styles[0].content,
81 | templateHolder: template.content,
82 | logicHolder: newScript,
83 | });
84 |
85 | return output;
86 | }
87 |
88 | const libAddressMapForVue2 = {
89 | vue: {
90 | js: ["https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"],
91 | css: "",
92 | },
93 | ele: {
94 | js: ["https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.7/index.min.js"],
95 | css: "https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.7/theme-chalk/index.min.css",
96 | },
97 | antd: {
98 | js: [
99 | "https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.min.js",
100 | "https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.js",
101 | ],
102 | css: "https://cdn.bootcdn.net/ajax/libs/ant-design-vue/1.7.8/antd.css",
103 | },
104 | vant: {
105 | js: ["https://cdn.bootcdn.net/ajax/libs/vant/2.12.44/vant.min.js"],
106 | css: "https://cdn.bootcdn.net/ajax/libs/vant/2.12.44/index.min.css",
107 | },
108 | };
109 |
--------------------------------------------------------------------------------
/src/libs/split-init.js:
--------------------------------------------------------------------------------
1 | import Split from 'split.js'
2 |
3 | export function splitInit() {
4 | let sizes = localStorage.getItem('split-sizes')
5 | let sizeCodes = localStorage.getItem('split-sizes-code')
6 |
7 | if (sizes) {
8 | sizes = JSON.parse(sizes)
9 | } else {
10 | sizes = [50, 50] // default sizes
11 | }
12 |
13 | if (sizeCodes) {
14 | sizeCodes = JSON.parse(sizeCodes)
15 | } else {
16 | sizeCodes = [70, 30] // default sizes
17 | }
18 |
19 | Split(['.base-component-container', '.main-container'], {
20 | sizes: sizes,
21 | gutterSize: 4,
22 | minSize: [400, 375],
23 | gutter: (index, direction) => {
24 | const gutter = document.createElement('div')
25 | gutter.className = `gutter gutter-${direction}`
26 | gutter.addEventListener("mousemove", () => {
27 | gutter.style.cursor = 'col-resize';
28 | })
29 | return gutter
30 | }, onDragEnd: function (sizes) {
31 | localStorage.setItem('split-sizes', JSON.stringify(sizes))
32 | },
33 | })
34 |
35 | // Split(['.preview-container', '.render-wrap'], {
36 | // sizes: sizeCodes,
37 | // gutterSize: 4,
38 | // direction: 'vertical',
39 | // minSize: [100, 100],
40 | // gutter: (index, direction) => {
41 | // const gutter = document.createElement('div')
42 | // gutter.className = `gutter gutter-${direction}`
43 | // gutter.addEventListener("mousemove", () => {
44 | // gutter.style.cursor = 'row-resize';
45 | // })
46 | // return gutter
47 | // }, onDragEnd: function (sizes) {
48 | // localStorage.setItem('split-sizes-code', JSON.stringify(sizes))
49 | // },
50 | // })
51 | }
--------------------------------------------------------------------------------
/src/libs/store.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'vuex'
2 |
3 | export const store = createStore({
4 | state() {
5 | return {
6 | currentEditComp: null,
7 | renderCount: 0
8 | }
9 | },
10 | mutations: {
11 | storeCurrentEditComp(state, newComp) {
12 | state.currentEditComp = newComp;
13 | },
14 | onDragEnd(state) {
15 | state.renderCount++;
16 | }
17 | }
18 | })
19 |
--------------------------------------------------------------------------------
/src/libs/stringify-object.js:
--------------------------------------------------------------------------------
1 | import isRegexp from 'is-regexp';
2 | import isObject from 'is-obj';
3 |
4 | const getOwnEnumPropSymbols = (object) => Object
5 | .getOwnPropertySymbols(object)
6 | .filter((keySymbol) => Object.prototype.propertyIsEnumerable.call(object, keySymbol));
7 |
8 | export default function stringifyObject(input, options, pad) {
9 | const seen = [];
10 |
11 | return (function stringify(input, options = {}, pad = '') {
12 | const indent = options.indent || '\t';
13 |
14 | let tokens;
15 | if (options.inlineCharacterLimit === undefined) {
16 | tokens = {
17 | newline: '\n',
18 | newlineOrSpace: '\n',
19 | pad,
20 | indent: pad + indent,
21 | };
22 | } else {
23 | tokens = {
24 | newline: '@@__STRINGIFY_OBJECT_NEW_LINE__@@',
25 | newlineOrSpace: '@@__STRINGIFY_OBJECT_NEW_LINE_OR_SPACE__@@',
26 | pad: '@@__STRINGIFY_OBJECT_PAD__@@',
27 | indent: '@@__STRINGIFY_OBJECT_INDENT__@@',
28 | };
29 | }
30 |
31 | const expandWhiteSpace = string => {
32 | if (options.inlineCharacterLimit === undefined) {
33 | return string;
34 | }
35 |
36 | const oneLined = string
37 | .replace(new RegExp(tokens.newline, 'g'), '')
38 | .replace(new RegExp(tokens.newlineOrSpace, 'g'), ' ')
39 | .replace(new RegExp(tokens.pad + '|' + tokens.indent, 'g'), '');
40 |
41 | if (oneLined.length <= options.inlineCharacterLimit) {
42 | return oneLined;
43 | }
44 |
45 | return string
46 | .replace(new RegExp(tokens.newline + '|' + tokens.newlineOrSpace, 'g'), '\n')
47 | .replace(new RegExp(tokens.pad, 'g'), pad)
48 | .replace(new RegExp(tokens.indent, 'g'), pad + indent);
49 | };
50 |
51 | if (seen.includes(input)) {
52 | return '"[Circular]"';
53 | }
54 |
55 | if (
56 | input === null
57 | || input === undefined
58 | || typeof input === 'number'
59 | || typeof input === 'boolean'
60 | || typeof input === 'function'
61 | || typeof input === 'symbol'
62 | || isRegexp(input)
63 | ) {
64 | return String(input);
65 | }
66 |
67 | if (input instanceof Date) {
68 | return `new Date('${input.toISOString()}')`;
69 | }
70 |
71 | if (Array.isArray(input)) {
72 | if (input.length === 0) {
73 | return '[]';
74 | }
75 |
76 | seen.push(input);
77 |
78 | const returnValue = '[' + tokens.newline + input.map((element, i) => {
79 | const eol = input.length - 1 === i ? tokens.newline : ',' + tokens.newlineOrSpace;
80 |
81 | let value = stringify(element, options, pad + indent);
82 | if (options.transform) {
83 | value = options.transform(input, i, value);
84 | }
85 |
86 | return tokens.indent + value + eol;
87 | }).join('') + tokens.pad + ']';
88 |
89 | seen.pop();
90 |
91 | return expandWhiteSpace(returnValue);
92 | }
93 |
94 | if (isObject(input)) {
95 | let objectKeys = [
96 | ...Object.keys(input),
97 | ...getOwnEnumPropSymbols(input),
98 | ];
99 |
100 | if (options.filter) {
101 | objectKeys = objectKeys.filter(element => options.filter(input, element));
102 | }
103 |
104 | if (objectKeys.length === 0) {
105 | return '{}';
106 | }
107 |
108 | seen.push(input);
109 |
110 | const returnValue = '{' + tokens.newline + objectKeys.map((element, i) => {
111 | const eol = objectKeys.length - 1 === i ? tokens.newline : ',' + tokens.newlineOrSpace;
112 | const isSymbol = typeof element === 'symbol';
113 | const isClassic = !isSymbol && /^[a-z$_][$\w]*$/i.test(element);
114 | const key = isSymbol || isClassic ? element : stringify(element, options);
115 |
116 | let value = stringify(input[element], options, pad + indent);
117 | if (options.transform) {
118 | value = options.transform(input, element, value);
119 | }
120 |
121 | return tokens.indent + String(key) + ': ' + value + eol;
122 | }).join('') + tokens.pad + '}';
123 |
124 | seen.pop();
125 |
126 | return expandWhiteSpace(returnValue);
127 | }
128 |
129 | input = String(input).replace(/[\r\n]/g, x => x === '\n' ? '\\n' : '\\r');
130 |
131 | if (options.singleQuotes === false) {
132 | input = input.replace(/"/g, '\\"');
133 | return `"${input}"`;
134 | }
135 |
136 | input = input.replace(/\\?'/g, '\\\'');
137 | return `'${input}'`;
138 | })(input, options, pad);
139 | }
140 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import ElementPlus from "element-plus";
3 | import {
4 | QuestionFilled,
5 | CirclePlus,
6 | DocumentCopy,
7 | Delete,
8 | Refresh,
9 | Minus,
10 | } from "@element-plus/icons-vue";
11 | import "element-plus/dist/index.css";
12 |
13 | import APP from "./App.vue";
14 | import loadCompontents from "@/libs/UIComponentInit.js";
15 |
16 | import Chart from './rawComponents/echart/init';
17 |
18 | /**
19 | * 创建实例基础方法
20 | * @param {*} renderComponent
21 | * @param {*} loadFinished
22 | * @returns
23 | */
24 | function loadTemplate(renderComponent, loadFinished = () => { }) {
25 | const app = createApp(renderComponent);
26 | app.use(ElementPlus);
27 | app.component('VChart', Chart);
28 | loadCompontents().then((modules) => {
29 | for (let index = 0; index < modules.length; index++) {
30 | const module = modules[index];
31 | app.use(module);
32 | loadFinished(app);
33 | }
34 | });
35 | return app;
36 | }
37 |
38 | /**
39 | * 同步创建实例
40 | * @param {*} renderComponent
41 | * @returns
42 | */
43 | function createBaseAppSync(renderComponent = {}) {
44 | return loadTemplate(renderComponent);
45 | }
46 |
47 | /**
48 | * 异步创建实例
49 | * @param {*} renderComponent
50 | * @returns
51 | */
52 | function createBaseAppAsync(renderComponent = {}) {
53 | return new Promise((resolve, reject) => {
54 | loadTemplate(renderComponent, (app) => {
55 | resolve(app);
56 | });
57 | });
58 | }
59 |
60 | const app = createBaseAppSync(APP);
61 |
62 | app.component("question-filled", QuestionFilled);
63 | app.component("circle-plus", CirclePlus);
64 | app.component("l-refresh", Refresh);
65 | app.component("l-delete", Delete);
66 | app.component("document-copy", DocumentCopy);
67 | app.component("l-minus", Minus);
68 |
69 | app.mount("#app");
70 |
71 | // 内部需要同样配置的全局Vue
72 | self.createBaseAppAsync = createBaseAppAsync;
--------------------------------------------------------------------------------
/src/map/data.index.js:
--------------------------------------------------------------------------------
1 | export default {"echartPieOption":{"title":{"text":"Traffic Sources","left":"center"},"legend":{"orient":"vertical","left":"left","data":["Direct","Email","Ad Networks","Video Ads","Search Engines"]},"series":[{"name":"Traffic Sources","type":"pie","radius":"55%","center":["50%","60%"],"data":[{"value":335,"name":"Direct"},{"value":310,"name":"Email"},{"value":234,"name":"Ad Networks"},{"value":135,"name":"Video Ads"},{"value":1548,"name":"Search Engines"}],"emphasis":{"itemStyle":{"shadowBlur":10,"shadowOffsetX":0,"shadowColor":"rgba(0, 0, 0, 0.5)"}}}]},"echartBarOption":{"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]},"circleUrl":"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png","squareUrl":"https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png","dialogVisible":false,"drawer":false,"direction":"rtl","count":0,"transferData":[{"key":0,"label":"备选项 0"},{"key":1,"label":"备选项 1"},{"key":2,"label":"备选项 2"},{"key":3,"label":"备选项 3"},{"key":4,"label":"备选项 4"}],"transferValue":[1,4],"textarea2":"","value1":["2016-10-10T00:40:00.000Z","2016-10-10T01:40:00.000Z"],"dateValue1":"","num":1,"imageUrl":"","color1":"#409EFF","checkList":["选中且禁用","复选框 A"],"radio":3,"input":"","fileList":[{"name":"food.jpeg","url":"https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100"},{"name":"food2.jpeg","url":"https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100"}],"options":[{"value":"选项1","label":"黄金糕"},{"value":"选项2","label":"双皮奶"},{"value":"选项3","label":"蚵仔煎"},{"value":"选项4","label":"龙须面"},{"value":"选项5","label":"北京烤鸭"}],"value":3,"value2":[],"options2":[{"value":"zhinan","label":"指南","children":[{"value":"shejiyuanze","label":"设计原则","children":[{"value":"yizhi","label":"一致"},{"value":"fankui","label":"反馈"},{"value":"xiaolv","label":"效率"},{"value":"kekong","label":"可控"}]},{"value":"daohang","label":"导航","children":[{"value":"cexiangdaohang","label":"侧向导航"},{"value":"dingbudaohang","label":"顶部导航"}]}]},{"value":"zujian","label":"组件","children":[{"value":"basic","label":"Basic","children":[{"value":"layout","label":"Layout 布局"},{"value":"color","label":"Color 色彩"},{"value":"typography","label":"Typography 字体"},{"value":"icon","label":"Icon 图标"},{"value":"button","label":"Button 按钮"}]},{"value":"form","label":"Form","children":[{"value":"radio","label":"Radio 单选框"},{"value":"checkbox","label":"Checkbox 多选框"},{"value":"input","label":"Input 输入框"},{"value":"input-number","label":"InputNumber 计数器"},{"value":"select","label":"Select 选择器"},{"value":"cascader","label":"Cascader 级联选择器"},{"value":"switch","label":"Switch 开关"},{"value":"slider","label":"Slider 滑块"},{"value":"time-picker","label":"TimePicker 时间选择器"},{"value":"date-picker","label":"DatePicker 日期选择器"},{"value":"datetime-picker","label":"DateTimePicker 日期时间选择器"},{"value":"upload","label":"Upload 上传"},{"value":"rate","label":"Rate 评分"},{"value":"form","label":"Form 表单"}]},{"value":"data","label":"Data","children":[{"value":"table","label":"Table 表格"},{"value":"tag","label":"Tag 标签"},{"value":"progress","label":"Progress 进度条"},{"value":"tree","label":"Tree 树形控件"},{"value":"pagination","label":"Pagination 分页"},{"value":"badge","label":"Badge 标记"}]},{"value":"notice","label":"Notice","children":[{"value":"alert","label":"Alert 警告"},{"value":"loading","label":"Loading 加载"},{"value":"message","label":"Message 消息提示"},{"value":"message-box","label":"MessageBox 弹框"},{"value":"notification","label":"Notification 通知"}]},{"value":"navigation","label":"Navigation","children":[{"value":"menu","label":"NavMenu 导航菜单"},{"value":"tabs","label":"Tabs 标签页"},{"value":"breadcrumb","label":"Breadcrumb 面包屑"},{"value":"dropdown","label":"Dropdown 下拉菜单"},{"value":"steps","label":"Steps 步骤条"}]},{"value":"others","label":"Others","children":[{"value":"dialog","label":"Dialog 对话框"},{"value":"tooltip","label":"Tooltip 文字提示"},{"value":"popover","label":"Popover 弹出框"},{"value":"card","label":"Card 卡片"},{"value":"carousel","label":"Carousel 走马灯"},{"value":"collapse","label":"Collapse 折叠面板"}]}]},{"value":"ziyuan","label":"资源","children":[{"value":"axure","label":"Axure Components"},{"value":"sketch","label":"Sketch Templates"},{"value":"jiaohu","label":"组件交互文档"}]}],"value3":true,"value4":28,"value5":"","value6":"","value7":1,"radio1":3,"ruleForm":{"name":"","region":"","date1":"","date2":"","delivery":false,"type":[],"resource":"","desc":""},"rules":{"name":[{"required":true,"message":"请输入活动名称","trigger":"blur"},{"min":3,"max":5,"message":"长度在 3 到 5 个字符","trigger":"blur"}],"region":[{"required":true,"message":"请选择活动区域","trigger":"change"}],"date1":[{"type":"date","required":true,"message":"请选择日期","trigger":"change"}],"date2":[{"type":"date","required":true,"message":"请选择时间","trigger":"change"}],"type":[{"type":"array","required":true,"message":"请至少选择一个活动性质","trigger":"change"}],"resource":[{"required":true,"message":"请选择活动资源","trigger":"change"}],"desc":[{"required":true,"message":"请填写活动形式","trigger":"blur"}]},"formInline":{"user":"","region":""},"url":"https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg","calendarValue":"2023-12-06T10:59:26.558Z","reverse":true,"activities":[{"content":"活动按期开始","timestamp":"2018-04-15"},{"content":"通过审核","timestamp":"2018-04-13"},{"content":"创建成功","timestamp":"2018-04-11"}],"collapseActiveNames":["1"],"active":1,"activeName":"second","activeIndex2":"1","data":[{"label":"一级 1","children":[{"label":"二级 1-1","children":[{"label":"三级 1-1-1"}]}]},{"label":"一级 2","children":[{"label":"二级 2-1","children":[{"label":"三级 2-1-1"}]},{"label":"二级 2-2","children":[{"label":"三级 2-2-1"}]}]},{"label":"一级 3","children":[{"label":"二级 3-1","children":[{"label":"三级 3-1-1"}]},{"label":"二级 3-2","children":[{"label":"三级 3-2-1"}]}]}],"defaultProps":{"children":"children","label":"label"},"url4":"https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg","currentPage":1,"tableData":[{"date":"2016-05-02","name":"王小虎","address":"上海市普陀区金沙江路 1518 弄"},{"date":"2016-05-04","name":"王小虎","address":"上海市普陀区金沙江路 1517 弄"},{"date":"2016-05-01","name":"王小虎","address":"上海市普陀区金沙江路 1519 弄"},{"date":"2016-05-03","name":"王小虎","address":"上海市普陀区金沙江路 1516 弄"}],"propList":[{"label":"姓名","prop":"name"},{"label":"日期","prop":"date"},{"label":"地址","prop":"address"},{"label":"动态组件","prop":"lc-component","component":"el-switch"}],"time1":1701860186580,"time2":1701601166580,"currentRate":0,"gradientColor":{"0%":"#3fecff","100%":"#6149f6"},"activeNames":["1"],"time":108000000,"list":[1,2,3,4,5,6,7,8,9,10,11,12,13,14],"loading":false,"finished":false,"stepValue":1,"username":"","password":"","slideValue":30,"slideArrValue":[10,60],"search":"","show":false,"columns":["杭州","宁波","温州","绍兴","湖州","嘉兴","金华","衢州"],"checked":true,"date":"","checked9":true,"minDate":"2019-12-31T16:00:00.000Z","maxDate":"2025-10-31T16:00:00.000Z","currentDate":"2023-12-06T10:59:26.615Z","tel":"","text":"","digit":"","number":"","result":[],"radio10":"1","checked12":false,"currentPage13":1,"activeKey":0,"active14":2,"areaList":{},"searchResult":[],"chosenAddressId":"1","list15":[{"lc_id":"1","name":"张三","tel":"13000000000","address":"浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室","isDefault":true},{"lc_id":"2","name":"李四","tel":"1310000000","address":"浙江省杭州市拱墅区莫干山路 50 号"}],"disabledList":[{"lc_id":"3","name":"王五","tel":"1320000000","address":"浙江省杭州市滨江区江南大道 15 号"}]}
--------------------------------------------------------------------------------
/src/map/load.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 加载外部代码资源
3 | */
4 | import styleData from "../map/style.index.js";
5 | import methodData from "../map/method.index.js";
6 | import dataData from "../map/data.index.js";
7 | import templateData from "../map/template.index.js";
8 |
9 | window.templateSourceMap = templateData;
10 | window.dataSourceMap = dataData;
11 | window.methodSourceMap = methodData;
12 | window.styleSourceMap = styleData;
13 |
--------------------------------------------------------------------------------
/src/map/method.index.js:
--------------------------------------------------------------------------------
1 | export default {"handleClose":"function (done) {\n this.$confirm('确认关闭\\uFF1F').then(_ => {\n done();\n }).catch(_ => {\n });\n}","handleDrawerClose":"function (done) {\n this.$confirm('确认关闭\\uFF1F').then(_ => {\n done();\n }).catch(_ => {\n });\n}","load":"function () {\n this.count += 2;\n}","handleChange":"function (value) {\n}","handleRemove":"function (file, fileList) {\n console.log(file, fileList);\n}","handlePreview":"function (file) {\n console.log(file);\n}","handleExceed":"function (files, fileList) {\n this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${ files.length } 个文件,共选择了 ${ files.length + fileList.length } 个文件`);\n}","beforeRemove":"function (file, fileList) {\n return this.$confirm(`确定移除 ${ file.name }?`);\n}","handleAvatarSuccess":"function (res, file) {\n this.imageUrl = URL.createObjectURL(file.raw);\n}","beforeAvatarUpload":"function (file) {\n}","next":"function () {\n}","goBack":"function () {\n console.log('go back');\n}","onSubmit":"function () {\n console.log('submit!');\n}","onReset":"function () {\n console.log('submit!');\n}","submitForm":"function () {\n console.log('submit!');\n}","resetForm":"function () {\n console.log('submit!');\n}","format":"function (percentage) {\n return percentage === 100 ? '满' : `${ percentage }%`;\n}","handleNodeClick":"function () {\n}","handleSelect":"function () {\n}","handleClick":"function () {\n}","goBack2":"function () {\n}","handleChange3":"function (val) {\n console.log(val);\n}","handleSizeChange":"function () {\n}","handleCurrentChange":"function () {\n}","onLoad":"function () {\n}","onSubmit6":"function (values) {\n console.log('submit', values);\n}","onConfirm":"function (value, index) {\n}","onChange":"function (picker, value, index) {\n}","onCancel":"function () {\n}","afterRead":"function (file) {\n console.log(file);\n}","formatDate":"function (date) {\n return `${ date.getMonth() + 1 }/${ date.getDate() }`;\n}","onConfirm8":"function (date) {\n this.show = false;\n this.date = this.formatDate(date);\n}","checkAll":"function () {\n this.$refs.checkboxGroup.toggleAll(true);\n}","toggleAll":"function () {\n this.$refs.checkboxGroup.toggleAll();\n}","onClickIcon":"function () {\n Toast('点击图标');\n}","onClickButton":"function () {\n Toast('点击按钮');\n}","onAdd":"function () {\n Toast('新增地址');\n}","onEdit":"function (item, index) {\n Toast('编辑地址:' + index);\n}","onClickLeft":"function () {\n Toast('返回');\n}","onClickRight":"function () {\n Toast('按钮');\n}","onSave":"function () {\n Toast('save');\n}","onDelete":"function () {\n Toast('delete');\n}","onChangeDetail":"function (val) {\n if (val) {\n this.searchResult = [{\n name: '黄龙万科中心',\n address: '杭州市西湖区'\n }];\n } else {\n this.searchResult = [];\n }\n}","onSubmit11":"function () {\n}","onClickEditAddress":"function () {\n}"}
--------------------------------------------------------------------------------
/src/map/style.index.js:
--------------------------------------------------------------------------------
1 | export default {".container":"",".echart-class":"padding: 20px; display: flex; flex-direction: column; gap: 10px;",".chart":"height: 300px; width: 100% !important; border: 1px dashed #c6c6c6; border-radius: 5px;",".row":"margin-bottom: 10px;",".icon":"margin-right: 10px; margin-left: 10px; font-size: 18px;",".el-header":"background-color: #b3c0d1; color: #333; text-align: center; line-height: 60px;",".el-footer":"background-color: #b3c0d1; color: #333; text-align: center; line-height: 60px;",".el-aside":"background-color: #d3dce6; color: #333; text-align: center; line-height: 200px;",".el-main":"background-color: #e9eef3; color: #333; text-align: center; line-height: 160px;","body > .el-container":"margin-bottom: 40px;",".el-container:nth-child(5) .el-aside":"line-height: 260px;",".el-container:nth-child(6) .el-aside":"line-height: 260px;",".el-container:nth-child(7) .el-aside":"line-height: 320px;",".avatar-uploader :v-deep(.el-upload)":"border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden;",".avatar-uploader .el-upload:hover":"border-color: #409eff;",".avatar-uploader-icon":"font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center;",".avatar":"width: 178px; height: 178px; display: block;",".demo-border":"border: 1px grey dashed; min-height: 1rem; border-radius: 5px;","[label-lc-mark]":"display: inline-block; width: 200px; border: 1px grey dashed; min-height: 1rem; border-radius: 5px;",".item":"margin-top: 10px; margin-right: 40px;",".el-carousel__item h3":"color: #475669; font-size: 14px; opacity: 0.75; line-height: 150px; margin: 0;",".el-carousel__item:nth-child(2n)":"background-color: #99a9bf;",".el-carousel__item:nth-child(2n + 1)":"background-color: #d3dce6;",".el-row":"margin-bottom: 20px;",".el-row:last-child":"margin-bottom: 0;",".el-col":"border-radius: 4px;",".bg-purple-dark":"background: #99a9bf;",".bg-purple":"background: #d3dce6 !important;",".bg-purple-light":"background: #e5e9f2;",".grid-content":"border-radius: 4px; min-height: 36px;",".row-bg":"padding: 10px 0; background-color: #f9fafc;",".icon1":"margin-left: 10px; margin-right: 10px;",".layout":"border: 1px solid #d7dde4; background: #f5f7f9; position: relative; border-radius: 4px; overflow: hidden;",".layout-logo":"width: 100px; height: 30px; background: #5b6270; border-radius: 3px; float: left; position: relative; top: 15px; left: 20px;",".layout-nav":"width: 420px; margin: 0 auto; margin-right: 20px;",".demonstration":"font-size: 12px; color: #1f2f3d; padding: 10px 0 0 0;",".title":"width: 120px;",".demonstration-raw":"padding: 10px 0;","[div-lc-mark]":"border: 1px grey dashed; min-height: 1rem; border-radius: 5px;","button + button":"margin-top: 10px;","#vant-button > *":"margin: 0 5px 5px 0;",".colon":"display: inline-block; margin: 0 4px; color: #ee0a24;",".block":"display: inline-block; width: 22px; color: #fff; font-size: 12px; text-align: center; background-color: #ee0a24;",".my-swipe .van-swipe-item":"color: #fff; font-size: 20px; line-height: 150px; text-align: center; background-color: #39a9ed;",".icon-c":"margin-left: 10px; margin-right: 10px;",":v-deep(.van-tabbar--fixed)":"position: initial;",":v-deep(.van-address-list__bottom)":"position: relative;",":v-deep(.van-submit-bar)":"position: initial;"}
--------------------------------------------------------------------------------
/src/rawComponents/antd/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Button 按钮
5 |
6 |
Primary Button
7 |
Default Button
8 |
Dashed Button
9 |
Text Button
10 |
Link Button
11 |
Primary
12 |
Default
13 |
14 |
15 |
16 |
ButtonGroup 按钮组
17 |
18 |
Primary
19 |
Default
20 |
Dashed
21 |
Danger
22 |
Link
23 |
24 |
25 |
26 |
27 |
49 |
51 |
--------------------------------------------------------------------------------
/src/rawComponents/antd/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
41 |
42 |
--------------------------------------------------------------------------------
/src/rawComponents/echart/chart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Tips: Vcc使用的是vue-echarts,因此实际使用时也需要将vue-echarts集成至项目中使用。
5 | 另外由于渲染器本身的原因,在将eChart拖入到面板之后,会有无法正常展示的问题。
6 | 代码本身是没有问题的,在预览模式下也可以正常渲染。
7 |
8 |
16 |
24 |
25 |
26 |
84 |
99 |
--------------------------------------------------------------------------------
/src/rawComponents/echart/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
26 |
27 |
42 |
--------------------------------------------------------------------------------
/src/rawComponents/echart/init.vue:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/src/rawComponents/element/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Button 默认按钮
5 |
默认按钮
6 |
主要按钮
7 |
成功按钮
8 |
信息按钮
9 |
警告按钮
10 |
危险按钮
11 |
12 |
13 |
Button 朴素按钮
14 |
朴素按钮
15 |
主要按钮
16 |
成功按钮
17 |
信息按钮
18 |
警告按钮
19 |
危险按钮
20 |
21 |
22 |
Button 圆角按钮
23 |
圆角按钮
24 |
主要按钮
25 |
成功按钮
26 |
信息按钮
27 |
警告按钮
28 |
危险按钮
29 |
30 |
31 |
Button 文字按钮
32 |
文字按钮
33 |
文字按钮
34 |
35 |
36 |
Button 按钮组
37 |
38 | 上一页
39 | 下一页
40 |
41 |
42 |
43 |
Link 文字链接
44 |
默认链接
45 |
主要链接
46 |
成功链接
47 |
警告链接
48 |
危险链接
49 |
信息链接
50 |
51 |
52 |
Tag 标签
53 |
标签一
54 |
标签二
55 |
标签三
56 |
标签四
57 |
标签五
58 |
59 |
60 |
Avatar 头像
61 |
62 |
63 |
64 |
65 |
66 |
76 |
85 |
--------------------------------------------------------------------------------
/src/rawComponents/element/container.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Container 布局容器
5 |
6 | Header
7 | Main
8 |
9 |
10 | Header
11 | Main
12 | Footer
13 |
14 |
15 | Aside
16 | Main
17 |
18 |
19 | Header
20 |
21 | Aside
22 | Main
23 |
24 |
25 |
26 | Header
27 |
28 | Aside
29 |
30 | Main
31 | Footer
32 |
33 |
34 |
35 |
36 | Aside
37 |
38 | Header
39 | Main
40 |
41 |
42 |
43 | Aside
44 |
45 | Header
46 | Main
47 | Footer
48 |
49 |
50 |
51 |
52 |
Skeleton 骨架屏
53 |
54 |
55 |
59 |
60 |
Result 结果
61 |
62 |
63 |
64 |
65 | 返回
66 |
67 |
68 |
69 |
70 |
71 |
72 | 返回
73 |
74 |
75 |
76 |
77 |
78 |
79 | 返回
80 |
81 |
82 |
83 |
84 |
85 |
86 | 返回
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
101 |
135 |
--------------------------------------------------------------------------------
/src/rawComponents/element/dialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Dialog 对话框
5 |
6 | 直接拖我看结果
7 |
8 | 这是一段信息
9 |
13 |
14 |
15 |
16 |
17 |
18 |
35 |
36 |
--------------------------------------------------------------------------------
/src/rawComponents/element/final.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Drawer 抽屉
5 |
6 |
7 | 从左往右开
8 | 从右往左开
9 | 从上往下开
10 | 从下往上开
11 |
12 | 点我打开
13 |
14 | 我来啦!
15 |
16 |
17 |
18 |
19 |
Popconfirm 气泡确认框
20 |
21 |
22 |
23 | 删除
24 |
25 |
26 |
27 |
28 |
29 |
Popover 弹出框
30 |
31 |
32 |
33 | click 激活
34 |
35 |
36 |
37 |
38 |
39 |
Tooltip 文字提示
40 |
41 |
42 | 上左
43 |
44 |
45 |
46 |
47 |
Transfer 穿梭框
48 |
49 |
50 |
51 |
52 |
53 |
54 |
93 |
94 |
--------------------------------------------------------------------------------
/src/rawComponents/element/image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Image 图片
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
--------------------------------------------------------------------------------
/src/rawComponents/element/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
57 |
59 |
89 |
--------------------------------------------------------------------------------
/src/rawComponents/element/layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Layout 布局
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Layout 分栏间隔布局
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Layout 混合布局
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
126 |
154 |
--------------------------------------------------------------------------------
/src/rawComponents/element/menu.vue:
--------------------------------------------------------------------------------
1 |
2 | Menu 菜单
3 |
4 |
5 |
6 |
7 |
8 |
9 | Navigator One
10 |
11 |
12 | item one
13 | item two
14 |
15 |
16 | item three
17 |
18 |
19 | item four
20 | item one
21 |
22 |
23 |
24 |
25 |
26 |
27 | Navigator Two
28 |
29 |
30 |
31 |
32 |
33 | Navigator Three
34 |
35 |
36 |
37 |
38 |
39 | Navigator Four
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/rawComponents/element/table.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Table 表格
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Table 动态表格👍
13 |
14 |
15 |
16 | {{
17 | row[propItem.prop]
18 | }}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
Pagination 分页
26 |
27 |
28 |
29 |
30 |
83 |
84 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Button 按钮:
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
24 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Icon 图标:
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
30 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
48 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Layout 布局:
6 |
7 |
26 |
27 |
28 |
52 |
53 |
54 |
55 | Home
56 | Components
57 | Layout
58 |
59 | Content
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/other.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
Timeline 时间轴:
9 |
10 | 发布1.0版本
11 | 发布2.0版本
12 | 严重故障
13 | 发布3.0版本
14 |
15 |
16 |
17 |
Time 相对时间:
18 |
19 |
20 |
21 |
22 |
Circle 进度环:
23 |
24 |
25 | 80%
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
50 |
51 |
--------------------------------------------------------------------------------
/src/rawComponents/iview/template.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/src/rawComponents/quasar/base.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Ajax请求状态栏:
5 |
6 |
7 |
8 |
9 |
10 |
J
11 |
J
12 |
13 |
14 |
15 |
16 |
17 |
59 |
60 |
--------------------------------------------------------------------------------
/src/rawComponents/quasar/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
按钮:
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
带图标:
16 |
17 |
18 |
19 |
20 |
21 |
圆形:
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
36 |
39 |
42 |
45 |
46 |
47 |
74 |
--------------------------------------------------------------------------------
/src/rawComponents/quasar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
38 |
43 |
--------------------------------------------------------------------------------
/src/rawComponents/raw/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | div:
7 | |
8 |
9 |
10 | |
11 |
12 |
13 |
14 | span:
15 | |
16 |
17 | Span Element
18 | |
19 |
20 |
21 |
22 | a(超链接):
23 | |
24 |
25 | W3School
26 | |
27 |
28 |
29 |
30 | abbr(标记缩写):
31 | |
32 |
33 | PRC
34 | |
35 |
36 |
37 |
38 | article:
39 | |
40 |
41 |
42 | Internet Explorer 9
43 | Windows Internet Explorer 9(简称 IE9)于 2011 年 3 月
44 | 14
45 | 日发布.....
46 |
47 | |
48 |
49 |
50 |
51 | aside:
52 | |
53 |
54 |
55 | Epcot Center
56 | |
57 |
58 |
59 |
60 | p:
61 | |
62 |
63 | 这是普通文本
64 | 这是粗体文本
65 | |
66 |
67 |
68 |
69 |
70 |
87 |
100 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Button 按钮
5 |
6 | 主要按钮
7 | 成功按钮
8 | 默认按钮
9 | 警告按钮
10 | 危险按钮
11 | 朴素按钮
12 | 方形按钮
13 | 圆形按钮
14 | 按钮
15 | 按钮
16 | 大号按钮
17 | 普通按钮
18 | 小型按钮
19 | 迷你按钮
20 | 块级元素
21 | 单色按钮
22 | 单色按钮
23 | 渐变色按钮
24 |
25 |
26 |
27 |
Cell 单元格
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
48 |
55 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/display.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Collapse 折叠面板
5 |
6 | 内容
7 | 内容
8 | 内容
9 |
10 |
11 |
12 |
CountDown 倒计时
13 |
14 |
15 |
16 |
17 | {{ timeData.hours }}
18 | :
19 | {{ timeData.minutes }}
20 | :
21 | {{ timeData.seconds }}
22 |
23 |
24 |
25 |
26 |
Divider 分割线
27 |
28 |
文字
29 |
文字
30 |
文字
31 |
文字
32 |
33 |
34 |
Empty 空状态
35 |
36 |
37 |
38 |
39 |
40 |
List 列表
41 |
42 |
43 |
44 |
45 |
46 |
NoticeBar 通知栏
47 |
48 |
49 |
50 |
51 |
52 |
Progress 进度条
53 |
54 |
55 |
56 |
57 |
58 |
Skeleton 骨架屏
59 |
60 |
61 |
62 |
63 |
Steps 步骤条
64 |
65 | 买家下单
66 | 商家接单
67 | 买家提货
68 | 交易完成
69 |
70 |
71 |
72 | 【城市】物流状态1
73 | 2016-07-12 12:40
74 |
75 |
76 | 【城市】物流状态2
77 | 2016-07-11 10:00
78 |
79 |
80 | 快件已发货
81 | 2016-07-10 09:30
82 |
83 |
84 |
85 |
86 |
Swipe 轮播
87 |
88 |
89 | 1
90 | 2
91 | 3
92 | 4
93 |
94 |
95 |
96 |
97 |
98 |
119 |
139 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Form 表单
5 |
6 |
7 |
8 |
9 | 提交
10 |
11 |
12 |
13 |
14 |
Picker 选择器
15 |
16 |
17 |
18 |
Rate 评分
19 |
20 |
21 |
22 |
23 |
24 |
Search 搜索
25 |
26 |
27 |
28 |
29 |
30 |
Slider 滑块
31 |
32 |
33 |
34 |
35 |
36 |
Stepper 步进器
37 |
38 |
39 |
43 |
44 |
Uploader 文件上传
45 |
46 |
47 |
48 |
49 |
78 |
79 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Icon 图标
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
28 |
32 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
43 |
46 |
47 |
77 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Field 输入框
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
DatetimePicker 时间选择 ❌
15 |
16 |
17 |
18 |
Checkbox 复选框
19 |
20 | 复选框 a
21 | 复选框 b
22 |
23 |
24 |
25 |
Checkbox 复选框(全选与反选)
26 |
27 |
28 | 复选框 a
29 | 复选框 b
30 | 复选框 c
31 |
32 |
全选
33 | 反选
34 |
35 |
36 |
37 |
Radio 单选框
38 |
39 |
40 | 单选框 1
41 | 单选框 2
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
96 |
97 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Grid 宫格
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
NavBar 导航栏
14 |
15 |
16 |
17 |
Pagination 分页
18 |
19 |
20 |
21 |
Sidebar 侧边导航
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Tab 标签页
30 |
31 | 内容 1
32 | 内容 2
33 | 内容 3
34 | 内容 4
35 |
36 |
37 |
38 |
ActionBar 动作栏
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
AddressEdit 地址编辑
50 |
51 |
52 |
56 |
57 |
ActionBar 行动栏
58 |
59 |
60 |
61 |
62 |
140 |
149 |
158 |
--------------------------------------------------------------------------------
/src/rawComponents/vant/template.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
NumberKeyboard 数字键盘
5 |
6 |
7 |
8 |
15 |
16 |
--------------------------------------------------------------------------------
/src/script/compile.js:
--------------------------------------------------------------------------------
1 | const { compiler } = require("./compileComponent.js");
2 |
3 | const glob = require("glob");
4 | const path = require("path");
5 | const process = require("process");
6 |
7 | const componentsPath = path.join(process.cwd(), "src/rawComponents");
8 |
9 | console.info(`当前正在读取${componentsPath}中的vue原始组件`);
10 |
11 | // options is optional
12 | glob(
13 | "**/*.vue",
14 | {
15 | cwd: componentsPath,
16 | absolute: true,
17 | ignore: ["**/element/index.vue", "**/vant/index.vue", "**/iview/index.vue", "**/antd/index.vue", "**/echart/index.vue", "**/echart/chart.vue"],
18 | },
19 | function (er, files) {
20 | console.info(`正在对${files.length}个文件进行编译...`);
21 | files.forEach((filePath) => {
22 | console.info(`正在编译${filePath}`);
23 | compiler(filePath);
24 | });
25 | }
26 | );
27 |
--------------------------------------------------------------------------------
/src/script/distClear.js:
--------------------------------------------------------------------------------
1 | const glob = require("glob");
2 | const file = require("fs");
3 |
4 | glob(
5 | "**/*.js",
6 | {
7 | cwd: "./dist/",
8 | absolute: true,
9 | ignore: ["**/*.min.*"],
10 | },
11 | function (er, files) {
12 | console.info(`正在清理多余文件...`);
13 | files.forEach((filePath) => {
14 | file.rm(filePath, function (er) {});
15 | });
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/src/test/compileVueOnline.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 解析
8 |
9 |
10 |
11 |
12 |
13 |
14 |
58 |
59 |
--------------------------------------------------------------------------------
/src/utils/common.js:
--------------------------------------------------------------------------------
1 | import isEqual from "lodash-es/isEqual";
2 | import { customAlphabet, nanoid } from 'nanoid';
3 |
4 | export function getRawComponentKey(__rawVueInfo__) {
5 | return Object.keys(__rawVueInfo__)[0];
6 | }
7 |
8 | export function getRawComponentContent(__rawVueInfo__) {
9 | return __rawVueInfo__[getRawComponentKey(__rawVueInfo__)];
10 | }
11 |
12 | /**
13 | * 获得一个数据节点的父节点
14 | * @param {*} __rawVueInfo__
15 | * @returns
16 | */
17 | export function findParentNode(__rawVueInfo__) {
18 | const targetDom = findTargetDom(__rawVueInfo__);
19 | if (targetDom) {
20 | const parentDom = findParentDom(targetDom.parentNode);
21 | return findVueInfo(parentDom);
22 | } else {
23 | throw new Error(`没有找到目标DOM节点,数据节点信息:`, __rawVueInfo__);
24 | }
25 | }
26 |
27 | /**
28 | * 将一个节点从其节点中移除
29 | * @param {*} __rawVueInfo__
30 | */
31 | export function deleteNodeFromParent(__rawVueInfo__) {
32 | const parentNode = findParentNode(__rawVueInfo__);
33 | const children = getRawComponentContent(parentNode).__children;
34 | const index = children.findIndex(item => isEquals(item, __rawVueInfo__));
35 | children.splice(index, 1);
36 | }
37 |
38 | /**
39 | * 获得一个节点对应的数据信息,这个函数不负责向上递归查找
40 | * @param {*} element
41 | * @returns
42 | */
43 | export function findVueInfo(element) {
44 | if (element) {
45 | const lcid = element.attributes.lc_id.nodeValue;
46 | // 获取源代码结构
47 | let rowCode = window.templateSourceMap[lcid];
48 |
49 | if (!rowCode) {
50 | // 如果不在templateSourceMap,则可能当前的操作是在渲染面板上,这部分数据存放在tree中
51 | rowCode = window.tree[lcid];
52 | }
53 | return rowCode;
54 | } else {
55 | return null;
56 | }
57 | }
58 |
59 | /**
60 | * 是组件库的组件
61 | * @param {*} __rawVueInfo__
62 | * @returns
63 | */
64 | export function isRawComponents(__rawVueInfo__) {
65 | const lcid = getVueInfoLcid(__rawVueInfo__);
66 | return !!window.templateSourceMap[lcid];
67 | }
68 |
69 | /**
70 | * 是已经被拖入面板的组件
71 | * @param {*} __rawVueInfo__
72 | * @returns
73 | */
74 | export function isActiveComponents(__rawVueInfo__) {
75 | const lcid = getVueInfoLcid(__rawVueInfo__);
76 | return !!window.tree[lcid];
77 | }
78 |
79 | /**
80 | * 校验两个数据节点是否相等。由于vue代理的存在,用简单===相比的方式已经失效
81 | * @param {*} o1
82 | * @param {*} o2
83 | */
84 | export function isEquals(o1, o2) {
85 | if (o1 && o2) {
86 | return getVueInfoLcid(o1) == getVueInfoLcid(o2);
87 | } else {
88 | return false;
89 | }
90 | }
91 |
92 | /**
93 | * 获得一个DOM节点的组件父DOM节点
94 | * @param {*} parentNode 要传入parentDom
95 | * @returns
96 | */
97 | export function findParentDom(parentNode) {
98 | if (parentNode.attributes && parentNode.attributes.lc_id) {
99 | return parentNode;
100 | } else if (parentNode.parentNode) {
101 | return findParentDom(parentNode.parentNode);
102 | } else {
103 | return null;
104 | }
105 | }
106 |
107 | /**
108 | * 获得一个数据节点的lc_id属性值
109 | * @param {*} __rawVueInfo__
110 | * @returns
111 | */
112 | export function getVueInfoLcid(__rawVueInfo__) {
113 | const lcid = getRawComponentContent(__rawVueInfo__).lc_id;
114 | return lcid;
115 | }
116 |
117 | /**
118 | * 获得一个数据节点的DOM节点
119 | * @param {*} __rawVueInfo__
120 | * @returns
121 | */
122 | export function findTargetDom(__rawVueInfo__) {
123 | const targetDom = document.querySelector(`[lc_id="${getVueInfoLcid(__rawVueInfo__)}"]`);
124 | return targetDom;
125 | }
126 |
127 | /**
128 | * 比较两个对象是否完全相等
129 | */
130 | export function compareTwoObjectIsEqual(o1, o2) {
131 | return isEqual(o1, o2);
132 | }
133 |
134 | export function isArray(arr) {
135 | return Object.prototype.toString.apply(arr) === "[object Array]";
136 | }
137 |
138 | export function isObject(obj) {
139 | return Object.prototype.toString.apply(obj) === "[object Object]";
140 | }
141 |
142 | /**
143 | * @description 生成唯一ID
144 | */
145 | export function createUniqueId() {
146 | const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10);
147 | return nanoid();
148 | }
149 |
150 | /**
151 | * 遍历对象,添加ID
152 | * @param {*} jsonObj
153 | */
154 | export function ergodic(jsonObj) {
155 | if (jsonObj) {
156 | for (const key in jsonObj) {
157 | if (jsonObj.hasOwnProperty(key)) {
158 | const element = jsonObj[key];
159 |
160 | if (isArray(element)) {
161 | element.forEach((item) => {
162 | if (isObject(item)) {
163 | ergodic(item);
164 | delete item.lc_id;
165 | }
166 | });
167 | } else if (isObject(element)) {
168 | ergodic(element);
169 | } else {
170 | }
171 | }
172 | }
173 |
174 | // 添加ID
175 | if (!jsonObj["lc_id"]) {
176 | jsonObj["lc_id"] = createUniqueId();
177 | }
178 | }
179 | }
180 |
181 | /**
182 | * 从解析后的Vue结构中找到关键的根节点
183 | * 根节点分包是:template/script/style
184 | *
185 | * @param {*} array
186 | * @param {*} propertyName
187 | * @returns
188 | */
189 | export function findAObject(array, propertyName) {
190 | const module = array.find(function (ele) {
191 | return ele[propertyName];
192 | });
193 | return module || null;
194 | }
--------------------------------------------------------------------------------
/src/utils/forCode.js:
--------------------------------------------------------------------------------
1 |
2 | import { isObject, isArray, getRawComponentKey, createUniqueId, findParentDom, findVueInfo } from '@/utils/common';
3 | import presetAttribute from "../libs/presetAttribute";
4 |
5 | // 将预生成的ID替换,否则当有两个组件挂在同一个树上时,后一个会将前一个的属性覆盖
6 | export function replaceRowID(codeObj, html) {
7 | // 生成一个唯一的ID,使Code和Dom一一对应,与原代码脱离关系
8 | let newHtml = html;
9 | function deep(obj) {
10 | if (isObject(obj)) {
11 | for (const key in obj) {
12 | if (obj.hasOwnProperty(key)) {
13 | const element = obj[key];
14 | if (key == "lc_id") {
15 | const oldID = obj[key];
16 | const newID = createUniqueId();
17 | newHtml = newHtml.replace(oldID, newID);
18 | obj[key] = newID;
19 | } else if (isObject(element)) {
20 | deep(element);
21 | } else if (isArray(element)) {
22 | element.forEach((item) => deep(item));
23 | }
24 | }
25 | }
26 | }
27 | }
28 | deep(codeObj);
29 | return newHtml;
30 | }
31 |
32 |
33 | /** 在这里维护一棵以ID为KEY的树 */
34 | export function updateLinkTree(codeObj) {
35 | if (!window.tree) {
36 | window.tree = {};
37 | }
38 |
39 | flatCodeObj(codeObj);
40 | }
41 |
42 | /**
43 | * 获取这个元素所对应的代码结构
44 | */
45 | export function findRawVueInfo(element) {
46 | const parentDom = findParentDom(element);
47 | return findVueInfo(parentDom);
48 | }
49 |
50 | export function flatCodeObj(codeObj) {
51 | function deep(object) {
52 | for (const key in object) {
53 | if (object.hasOwnProperty(key)) {
54 | const element = object[key];
55 |
56 | // 如果对__children的子属性遍历时,它内部的元素需要指向外层的节点作为父节点
57 | if (
58 | object.hasOwnProperty("__key__") &&
59 | object["__key__"] === "__children" &&
60 | isObject(element)
61 | ) {
62 | delete object["__key__"];
63 | }
64 |
65 | if (key === "lc_id" && object.hasOwnProperty("__key__")) {
66 | const outerKey = object["__key__"];
67 | const newObj = {
68 | [outerKey]: object
69 | };
70 |
71 | // 这个关系也需要链接
72 | newObj.__proto__ = object.__proto__;
73 | delete object.__key__;
74 | window.tree[element] = newObj;
75 | } else if (key === "__children") {
76 | object.__children.forEach((child) => {
77 | child["__key__"] = key;
78 | deep(child);
79 | });
80 | } else if (isObject(element)) {
81 | element["__key__"] = key;
82 | deep(element);
83 | }
84 | }
85 | }
86 | }
87 | deep(codeObj);
88 | }
89 |
90 | // 将所有子节点指向其父节点,父节点指向子节点容易,而子节点找到具体的父节点不容易
91 | export function linkRelationShip(unitRootCodeObj) {
92 | function deep(obj) {
93 | if (isObject(obj)) {
94 | for (const key in obj) {
95 | if (obj.hasOwnProperty(key)) {
96 | const element = obj[key];
97 | if (isObject(element)) {
98 | element.__proto__ = { parentCodeNode: obj };
99 | deep(element);
100 | } else if (isArray(element) && key === "__children") {
101 | element.forEach((item) => {
102 | item.__proto__ = { parentCodeNode: obj };
103 |
104 | deep(item);
105 | });
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
112 | deep(unitRootCodeObj);
113 | }
114 |
115 | // 使生成的代码不携带ID属性
116 | export function removeAllID(codeObj) {
117 | function deep(obj) {
118 | if (isObject(obj)) {
119 | for (const key in obj) {
120 | if (obj.hasOwnProperty(key)) {
121 | const element = obj[key];
122 | if (key == "lc_id" || key == "lc-mark") {
123 | delete obj[key];
124 | } else if (isObject(element)) {
125 | deep(element);
126 | } else if (isArray(element)) {
127 | element.forEach((item) => deep(item));
128 | }
129 | }
130 | }
131 | }
132 | }
133 |
134 | deep(codeObj);
135 | }
136 |
137 | export function generateRawInfo(target) {
138 | if (target.attributes.lc_id) {
139 | return findVueInfo(target);
140 | } else {
141 | // 这是一个普通的元素
142 | const temp = {
143 | [target.localName]: {
144 | __text__: target.innerText,
145 | class: target.className,
146 | }
147 | };
148 | return temp;
149 | }
150 | }
151 |
152 | /**
153 | * 这里需要将o2作为o1的子值 这里使用回调方法而不是用Promise的原因为需要严格保证外部的调用时序
154 | */
155 | export function merge(o1, o2, currentPointPositionAfterIndex = -1, onFinish = () => { }) {
156 | if (o1 && o2) {
157 | if (!o1["__children"]) {
158 | o1["__children"] = [];
159 | }
160 | // 更新结构关系,将插入到指定的位置
161 | if (currentPointPositionAfterIndex > -1) {
162 | o1["__children"].splice(currentPointPositionAfterIndex, 0, o2);
163 | } else {
164 | o1["__children"].push(o2);
165 | }
166 | onFinish();
167 |
168 | // 这里踩了一个坑,所有的对象的默认属性__proto__都指向同一个对象,会引起意外的问题
169 | o2.__proto__ = { parentCodeNode: o1 };
170 | }
171 | }
172 |
173 | // 特殊分隔符
174 | export function getSplitTag() {
175 | return "@#$!";
176 | }
177 |
178 | export function insertPresetAttribute(vueInfo) {
179 | const key = getRawComponentKey(vueInfo);
180 | const value = vueInfo[key];
181 | const presetAttr = presetAttribute[key];
182 | if (presetAttr) {
183 | for (const key in presetAttr) {
184 | if (presetAttr.hasOwnProperty(key)) {
185 | // 将原先的属性做新增或者替换操作
186 | const element = presetAttr[key];
187 | value[key] = element;
188 | }
189 | }
190 | }
191 | }
192 |
193 | /**
194 | * 寻找实际的可以代表整个复合组件Dom,这是个核心方法,根据某个元素查找实际的以Vue组件为单位的最小元素
195 | */
196 | export function findCodeElemNode(element) {
197 | return findParentDom(element);
198 | }
--------------------------------------------------------------------------------
/src/utils/initRawComponent.js:
--------------------------------------------------------------------------------
1 | import { generateRawInfo, getSplitTag } from './forCode';
2 |
3 |
4 | // 遍历DOM树,初始化lc-mark标记的元素
5 | export function deepLCEle(rootElement, onCountIncrease = () => { }) {
6 | // 对dragParent下所有含有lc-mark属性的Element实现可拖拽能力
7 | function deep(ele) {
8 | if (ele.attributes["lc-mark"]) {
9 | // 统计标记组件数量
10 | onCountIncrease();
11 | initElement(ele);
12 | }
13 |
14 | if (ele.children.length > 0) {
15 | const length = ele.children.length;
16 | for (let i = 0; i < length; i++) {
17 | deep(ele.children.item(i));
18 | }
19 | }
20 | }
21 |
22 | deep(rootElement);
23 | }
24 |
25 | // 对组件初始化,使组件可以拖拽
26 | export function initElement(element) {
27 | element.draggable = true;
28 | // 给每个组件添加拖拽事件
29 | element.addEventListener("dragstart", function (event) {
30 | event.dataTransfer.effectAllowed = "copy";
31 | const raw = generateRawInfo(element);
32 | const str = `${element.localName}${getSplitTag()}${element.innerText
33 | }${getSplitTag()}${0}${getSplitTag()}${element.style.cssText
34 | }${getSplitTag()}${JSON.stringify(raw)}`;
35 | event.dataTransfer.setData("text/plain", str);
36 |
37 | event.stopPropagation();
38 | });
39 |
40 | // 处理组件标记
41 | element.addEventListener("mouseenter", (event) => {
42 | const parentClassList = element.parentElement.classList;
43 | if (parentClassList && parentClassList.contains("mark-element-unit")) {
44 | parentClassList.remove("mark-element-unit");
45 | element.isRemoveParentStyle = true;
46 | }
47 |
48 | element.classList.add("mark-element-unit");
49 | event.stopPropagation();
50 | });
51 |
52 | element.addEventListener("mouseleave", (event) => {
53 | element.classList.remove("mark-element-unit");
54 | if (element.isRemoveParentStyle) {
55 | element.parentElement.classList.add("mark-element-unit");
56 | }
57 |
58 | event.stopPropagation();
59 | });
60 | }
--------------------------------------------------------------------------------
/src/utils/lineHelper.js:
--------------------------------------------------------------------------------
1 | let currentPostion = null,
2 | currentTarget = null,
3 | preSelectTarget = null; // 记录上一次鼠标所在位置
4 | const TOP = 1,
5 | MIDDLE = 2,
6 | BOTTOM = 3;
7 |
8 | import { findCodeElemNode, findRawVueInfo } from "@/utils/forCode";
9 | import { getRawComponentContent } from "@/utils/common";
10 |
11 | export function initContainerForLine(targetElement, _currentPointer = () => { }) {
12 | const crossX = document.querySelector(".x");
13 |
14 | const currentPointer = (...args) => {
15 | _currentPointer(...args);
16 | };
17 |
18 | targetElement.addEventListener("dragover", (event) => {
19 | event.preventDefault();
20 | drawLine(event);
21 | });
22 |
23 | targetElement.addEventListener("dragleave", (event) => {
24 | if (event.target === targetElement) {
25 | targetElement.classList.remove("in-element");
26 | crossX.style = "display: none;";
27 | } else {
28 | clearTargetOutline();
29 | }
30 | });
31 |
32 | /**
33 | * 获得一个元素在父元素中的索引
34 | * @param {*} element
35 | * @returns
36 | */
37 | function findElementIndex(element) {
38 | // 根据代码结构查找
39 | const parentElementNode = findCodeElemNode(element.parentElement);
40 | const lc_id = element.getAttribute("lc_id");
41 |
42 | if (parentElementNode) {
43 | const parentRawInfo = findRawVueInfo(parentElementNode);
44 |
45 | if (parentRawInfo) {
46 | const attributes = getRawComponentContent(parentRawInfo);
47 | if (attributes) {
48 | const childrenArray = attributes.__children;
49 |
50 | const index = childrenArray.findIndex((item) => {
51 | return getRawComponentContent(item).lc_id == lc_id;
52 | });
53 | return index;
54 | }
55 | }
56 | }
57 | return -1;
58 | }
59 |
60 | function clearTargetOutline() {
61 | if (preSelectTarget) {
62 | preSelectTarget.classList.remove("in-element");
63 | }
64 | }
65 |
66 | function drawLine(event) {
67 | const realTarget = event.target;
68 |
69 | // 2021年03月26日15:56:35 新的逻辑是:只有上下定位辅助线,不再计算左右辅助线
70 | const directionObj = judgeTopOrBottom(realTarget, event.clientX, event.clientY);
71 | const position = getElCoordinate(realTarget);
72 |
73 | // 如果鼠标点在目标的上部分则绘制上部分辅助线
74 | if (directionObj.top && targetElement !== realTarget) {
75 | if (currentPostion === TOP && currentTarget === realTarget) {
76 | return;
77 | }
78 | currentPostion = TOP;
79 | currentTarget = realTarget;
80 |
81 | clearTargetOutline();
82 |
83 | crossX.style = `top:${position.top}px;width:${position.width}px;left:${position.left}px;display:block;`;
84 |
85 | currentPointer(realTarget.parentElement, findElementIndex(realTarget));
86 | } else if (directionObj.bottom && targetElement !== realTarget) {
87 | // 如果鼠标点在目标的下部分,则绘制下部分辅助线
88 | if (currentPostion === BOTTOM && currentTarget === realTarget) {
89 | return;
90 | }
91 | currentPostion = BOTTOM;
92 | currentTarget = realTarget;
93 |
94 | clearTargetOutline();
95 |
96 | crossX.style = `top:${position.bottom}px;width:${position.width}px;left:${position.left}px;display:block;`;
97 |
98 | currentPointer(realTarget.parentElement, findElementIndex(realTarget) + 1);
99 | } else {
100 | currentPostion = MIDDLE;
101 | currentTarget = realTarget;
102 |
103 | realTarget.classList.add("in-element");
104 | preSelectTarget = realTarget;
105 |
106 | crossX.style = `display:none;`;
107 |
108 | currentPointer(realTarget, -1);
109 | }
110 | }
111 | }
112 |
113 | // 获取一个元素在屏幕上的坐标点
114 | function getElCoordinate(e) {
115 | const rect = e.getBoundingClientRect();
116 | return rect;
117 | }
118 |
119 | // 判断一个点是否在该元素内
120 | function judgeEleIsContentPoint(e, x, y) {
121 | const position = getElCoordinate(e);
122 |
123 | return x >= position.left && x <= position.right && y >= position.top && y <= position.bottom;
124 | }
125 |
126 | /**
127 | * 判断上还是下
128 | * @param {*} e
129 | * @param {*} x
130 | * @param {*} y
131 | * @returns
132 | */
133 | function judgeTopOrBottom(e, x, y) {
134 | const position = getElCoordinate(e);
135 |
136 | const cutDistance = Math.round((position.bottom - position.top) / 3);
137 |
138 | return {
139 | top: y < position.top + cutDistance,
140 | middle: y >= position.top + cutDistance && y <= position.top + cutDistance * 2,
141 | bottom: y > position.top + cutDistance * 2,
142 | };
143 | }
144 |
145 | // 判断点在元素的方向
146 | function direction(e, x, y) {
147 | const position = getElCoordinate(e);
148 |
149 | // 基本方向判定
150 | const direction = {
151 | left: x < position.left ? position.left - x : 0,
152 | right: x > position.right ? x - position.right : 0,
153 | top: y < position.top ? position.top - y : 0,
154 | bottom: y > position.bottom ? y - position.bottom : 0,
155 | };
156 |
157 | // 判定方向更靠近哪个
158 | let count = 0;
159 | let directionStrArray = [];
160 | for (const key in direction) {
161 | const element = direction[key];
162 | if (element) {
163 | directionStrArray.push(key);
164 | count++;
165 | }
166 | }
167 | if (count === 2) {
168 | // 进一步判定更靠近哪个方向
169 | const num1 = direction[directionStrArray[0]];
170 | const num2 = direction[directionStrArray[1]];
171 |
172 | direction[directionStrArray[0]] = num1 > num2;
173 | direction[directionStrArray[1]] = num2 > num1;
174 | }
175 |
176 | direction.position = position;
177 |
178 | return direction;
179 | }
180 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import vue from "@vitejs/plugin-vue";
3 |
4 | import path from "path";
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [
9 | vue(),
10 | ],
11 | resolve: {
12 | alias: [
13 | {
14 | find: "@",
15 | replacement: path.resolve(__dirname, "src"),
16 | },
17 | {
18 | find: "vue",
19 | replacement: "vue/dist/vue.esm-bundler.js"
20 | }
21 | ],
22 | extensions: [".vue", ".js"],
23 | },
24 | server: {
25 | fs: {
26 | // 可以为项目根目录的上一级提供服务
27 | allow: [".."],
28 | },
29 | },
30 | });
31 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = {
4 | css: { extract: false },
5 | chainWebpack: (config) => {
6 | // config.resolve.alias.set("vue$", "vue/dist/vue.esm.js");
7 | // config.resolve.alias.set('vue', '@vue/compat/dist/vue.esm-browser.prod.js')
8 | },
9 |
10 | publicPath: process.env.PUBLIC_PATH,
11 | productionSourceMap: false,
12 |
13 | pluginOptions: {
14 | quasar: {
15 | importStrategy: "kebab",
16 | rtlSupport: false,
17 | },
18 | },
19 |
20 | transpileDependencies: ["quasar"],
21 | };
22 |
--------------------------------------------------------------------------------