├── .gitignore
├── .prettierrc
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── img
├── alipay.jpg
├── qq-group.jpg
├── view1.jpg
├── view2.jpg
└── wechatpay.jpg
├── index.html
├── package.json
├── public
└── favicon.ico
├── src
├── App.vue
├── assets
│ ├── icons
│ │ └── svg
│ │ │ ├── assignment.svg
│ │ │ ├── decision.svg
│ │ │ ├── deepLearning.svg
│ │ │ ├── endParallel.svg
│ │ │ ├── logo.svg
│ │ │ ├── machineLearning.svg
│ │ │ ├── start.svg
│ │ │ └── startParallel.svg
│ ├── images
│ │ ├── logo-circle.jpg
│ │ └── logo.jpg
│ ├── styles
│ │ ├── element-ui-theme.scss
│ │ ├── element-ui.scss
│ │ ├── index.scss
│ │ ├── mixin.scss
│ │ └── variables.module.scss
│ └── vue.svg
├── components
│ └── SvgIcon
│ │ └── index.vue
├── main.ts
├── style.css
├── utils
│ ├── elementIcons.ts
│ ├── index.ts
│ └── options.ts
├── views
│ └── design
│ │ ├── LFComponents
│ │ ├── Control.vue
│ │ ├── NodePanel.vue
│ │ ├── top.vue
│ │ └── view-json.vue
│ │ ├── PropertySetting
│ │ └── PropertyDialog.vue
│ │ ├── index.vue
│ │ ├── registerEdge
│ │ ├── myBezier.vue
│ │ └── registerBezier.ts
│ │ ├── registerNode
│ │ ├── assignment
│ │ │ ├── assignment.ts
│ │ │ ├── assignment.vue
│ │ │ └── assignmentProperty.vue
│ │ ├── decision
│ │ │ ├── decision.ts
│ │ │ ├── decision.vue
│ │ │ └── decisionProperty.vue
│ │ ├── deepLearning
│ │ │ ├── deepLearning.ts
│ │ │ ├── deepLearning.vue
│ │ │ └── deepLearningProperty.vue
│ │ ├── endParallel
│ │ │ ├── endParallel.ts
│ │ │ ├── endParallel.vue
│ │ │ └── endParallelProperty.vue
│ │ ├── machineLearning
│ │ │ ├── machineLearning.ts
│ │ │ ├── machineLearning.vue
│ │ │ └── machineLearningProperty.vue
│ │ ├── start
│ │ │ ├── start.ts
│ │ │ ├── start.vue
│ │ │ └── startProperty.vue
│ │ └── startParallel
│ │ │ ├── startParallel.ts
│ │ │ ├── startParallel.vue
│ │ │ └── startParallelProperty.vue
│ │ └── template.vue
└── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | package-lock.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "printWidth": 200
6 | }
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 SHOW
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 |
2 |

3 |
4 |
5 |
6 | ## logicflow-vue3
7 |
8 | 🎉 logicflow+vue3+elementplus的流程编排。vue2版本在vue2分支。
9 |
10 |
11 |

12 |
13 |
14 |

15 |
16 |
17 |
18 |
19 | ## 功能亮点
20 |
21 | - 左侧树形,支持无数层
22 |
23 | - 自定义节点图标+文字,样式美观
24 |
25 |
26 |
27 | ## 运行步骤
28 |
29 | 使用node18
30 |
31 | ```
32 | npm config set registry https://registry.npmmirror.com/
33 | npm install
34 | npm run dev
35 | ```
36 |
37 |
38 |
39 | ## 项目集成
40 |
41 | - 下载logicflow依赖
42 |
43 | "@logicflow/core": "^1.2.26",
44 |
45 | "@logicflow/extension": "^1.2.26"
46 |
47 | - 把src/views/design目录复制到自己项目里即可
48 |
49 |
50 |
51 | ## 鸣谢
52 |
53 | - https://github.com/didi/LogicFlow
54 |
55 | - https://site.logic-flow.cn/examples
56 |
57 |
58 |
59 | ## 项目链接
60 |
61 | - https://github.com/xoobom/logicflow-vue3
62 |
63 |
64 |
65 |
66 | ## 沟通交流
67 |
68 | ### QQ群
69 |
70 | 群号:692252235
71 |
72 |
73 |

74 |
75 |
76 |
77 |
78 |
79 | ## 捐赠
80 |
81 | 如果您觉得这个项目帮助到了您,您可以捐赠 :moneybag: 表示鼓励 :coffee:
82 |
83 |

84 |

85 |
--------------------------------------------------------------------------------
/img/alipay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/img/alipay.jpg
--------------------------------------------------------------------------------
/img/qq-group.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/img/qq-group.jpg
--------------------------------------------------------------------------------
/img/view1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/img/view1.jpg
--------------------------------------------------------------------------------
/img/view2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/img/view2.jpg
--------------------------------------------------------------------------------
/img/wechatpay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/img/wechatpay.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | logicflow-vue3
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "logicflow-vue3",
3 | "private": true,
4 | "version": "1.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@element-plus/icons-vue": "^2.3.1",
13 | "@logicflow/core": "^2.0.10",
14 | "@logicflow/extension": "^2.0.10",
15 | "element-plus": "^2.7.0",
16 | "vue": "^3.4.21",
17 | "vue-json-pretty": "^2.4.0"
18 | },
19 | "devDependencies": {
20 | "@vitejs/plugin-vue": "^5.0.4",
21 | "fast-glob": "^3.3.2",
22 | "sass": "1.75.0",
23 | "typescript": "^5.2.2",
24 | "vite": "^5.2.0",
25 | "vite-plugin-svg-icons": "^2.0.1",
26 | "vue-tsc": "^2.0.6"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/assignment.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/decision.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/deepLearning.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/endParallel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/machineLearning.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/start.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/svg/startParallel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/logo-circle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/src/assets/images/logo-circle.jpg
--------------------------------------------------------------------------------
/src/assets/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/src/assets/images/logo.jpg
--------------------------------------------------------------------------------
/src/assets/styles/element-ui-theme.scss:
--------------------------------------------------------------------------------
1 | :root:root {
2 | --el-color-primary: #700efa;
3 | --el-color-primary-light-5: #7933e3;
4 | --el-color-primary-light-3: #4b21c4;
5 | --el-menu-item-height: 37px;
6 | --el-menu-sub-item-height: 37px;
7 | --el-border-radius-base: 6px;
8 | // --el-fill-color: #fff; //翻页背景
9 | }
10 |
--------------------------------------------------------------------------------
/src/assets/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | .el-form--inline .el-form-item {
2 | margin-right: 15px !important; //表单右边间距
3 | }
4 |
5 | .el-table {
6 | // border-radius: $appRadius !important;
7 | .el-table__header-wrapper,
8 | .el-table__fixed-header-wrapper {
9 | th {
10 | background-color: $tableHeaderBgColor !important;
11 | text-align: center; //表头居中
12 | }
13 | }
14 | td {
15 | text-align: center !important; //表格内容居中
16 | }
17 | // 多选列的表头--去掉半选状态
18 | .el-checkbox__input.is-indeterminate .el-checkbox__inner {
19 | background-color: unset !important;
20 | border-color: #dcdfe6 !important;
21 | }
22 | }
23 |
24 | //表格高度
25 | .el-table__header {
26 | .el-table__cell {
27 | padding: 10px 0 !important;
28 | }
29 | }
30 |
31 | .el-table__body {
32 | .el-table__cell {
33 | padding: 12px 0 !important;
34 | }
35 | }
36 |
37 | // 表格中斑马纹行的颜色
38 | .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
39 | background-color: $tableStripedColor !important;
40 | }
41 |
42 | .el-pagination {
43 | justify-content: center; //分页居中
44 | }
45 | .el-overlay-dialog {
46 | display: flex;
47 | justify-content: center;
48 | align-items: center;
49 | }
50 | .el-dialog {
51 | margin: auto !important;
52 | border-radius: $appRadiusLg !important;
53 |
54 | .el-dialog__close {
55 | font-size: 22px;
56 | position: relative;
57 | top: 5px;
58 | }
59 | }
60 |
61 | .el-dialog__footer {
62 | text-align: center !important; //弹窗底部居中
63 | }
64 | .el-dialog__header {
65 | border-bottom: 1px solid #e6e6e6;
66 | padding-bottom: 12px;
67 | }
68 |
69 | .el-dialog__body {
70 | padding: 15px 20px !important;
71 | }
72 |
73 | //表单间距
74 | .el-form-item {
75 | margin-bottom: 24px !important;
76 | }
77 |
78 | //表单错误提示
79 | .el-form-item--default .el-form-item__error {
80 | padding-top: 6px !important;
81 | }
82 |
83 | // danger按钮点击之后颜色不变淡
84 | .el-button--danger.is-link:focus {
85 | color: #f56c6c !important;
86 | }
87 | .el-button--danger.is-link:hover {
88 | color: #fab6b6 !important;
89 | }
90 |
91 | // primary按钮禁用时,颜色应变淡
92 | .el-button--primary.is-link.is-disabled {
93 | opacity: 0.6;
94 | }
95 |
96 | // 按钮active时的效果去掉
97 | .btn-has-bg .el-button--primary:active,
98 | .btn-has-bg,
99 | el-button--primary:focus {
100 | border-color: #700efa !important;
101 | background-color: #700efa !important;
102 | }
103 |
104 | // 解决:el-select+multiple+filterable、tag宽度+input宽度正好到达临界值、select的高度就会无限高低切换
105 | .el-select {
106 | .select-trigger {
107 | .el-select__input {
108 | min-width: 5px;
109 | margin-left: 10px;
110 | }
111 | }
112 | }
113 |
114 | .el-message-box {
115 | border-radius: $appRadiusLg;
116 | }
117 |
118 | //消息-标题、按钮居中
119 | .el-message-box__title,
120 | .el-message-box__btns {
121 | text-align: center !important;
122 | display: flex;
123 | justify-content: center;
124 | }
125 |
126 | .el-message-box__btns {
127 | padding-top: 15px;
128 | padding-bottom: 5px !important;
129 | }
130 |
131 | //消息-内容居中
132 | .el-message-box__content {
133 | display: flex;
134 | align-items: center;
135 | justify-content: center;
136 | }
137 |
138 | //页头
139 | .el-page-header__content {
140 | font-size: 15px !important;
141 | }
--------------------------------------------------------------------------------
/src/assets/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './variables.module.scss';
2 | @import './element-ui.scss';
3 |
4 | html,
5 | body,
6 | #app {
7 | height: 100%;
8 | margin: 0px;
9 | padding: 0px;
10 | }
11 |
12 | body {
13 | height: 100%;
14 | margin: 0;
15 | -moz-osx-font-smoothing: grayscale;
16 | -webkit-font-smoothing: antialiased;
17 | text-rendering: optimizeLegibility;
18 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
19 | // 为全局的文字设置默认的字体大小
20 | // font-size: 12px;
21 | }
22 |
23 | label {
24 | font-weight: 700;
25 | }
26 |
27 | html {
28 | height: 100%;
29 | box-sizing: border-box;
30 | // 方便rem和px之间进行转化 此时1px = 0.1rem
31 | // font-size: 10px;
32 | }
33 |
34 | #app {
35 | height: 100%;
36 | }
37 |
38 | *,
39 | *:before,
40 | *:after {
41 | box-sizing: inherit;
42 | }
43 |
44 | .no-padding {
45 | padding: 0px !important;
46 | }
47 |
48 | .padding-content {
49 | padding: 4px 0;
50 | }
51 |
52 | a:focus,
53 | a:active {
54 | outline: none;
55 | }
56 |
57 | a,
58 | a:focus,
59 | a:hover {
60 | cursor: pointer;
61 | color: inherit;
62 | text-decoration: none;
63 | }
64 |
65 | div:focus {
66 | outline: none;
67 | }
68 |
69 | .fr {
70 | float: right;
71 | }
72 |
73 | .fl {
74 | float: left;
75 | }
76 |
77 | .pr-5 {
78 | padding-right: 5px;
79 | }
80 |
81 | .pl-5 {
82 | padding-left: 5px;
83 | }
84 |
85 | .block {
86 | display: block;
87 | }
88 |
89 | .pointer {
90 | cursor: pointer;
91 | }
92 |
93 | .inlineBlock {
94 | display: block;
95 | }
96 |
97 | .clearfix {
98 | &:after {
99 | visibility: hidden;
100 | display: block;
101 | font-size: 0;
102 | content: ' ';
103 | clear: both;
104 | height: 0;
105 | }
106 | }
107 |
108 | aside {
109 | background: #eef1f6;
110 | padding: 8px 24px;
111 | margin-bottom: 20px;
112 | border-radius: 2px;
113 | display: block;
114 | line-height: 32px;
115 | font-size: 16px;
116 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
117 | color: #2c3e50;
118 | -webkit-font-smoothing: antialiased;
119 | -moz-osx-font-smoothing: grayscale;
120 |
121 | a {
122 | color: #337ab7;
123 | cursor: pointer;
124 |
125 | &:hover {
126 | color: rgb(32, 160, 255);
127 | }
128 | }
129 | }
130 |
131 | //main-container全局样式
132 | .app-container {
133 | border: 1px solid #ebeef5;
134 | background-color: #fff;
135 | transition: 0.3s;
136 | padding: 15px 16px;
137 | border-radius: 12px;
138 | }
139 |
140 | .components-container {
141 | margin: 30px 50px;
142 | position: relative;
143 | }
144 |
145 | .text-center {
146 | text-align: center;
147 | }
148 |
149 | .sub-navbar {
150 | height: 50px;
151 | line-height: 50px;
152 | position: relative;
153 | width: 100%;
154 | text-align: right;
155 | padding-right: 20px;
156 | transition: 600ms ease position;
157 | background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
158 |
159 | .subtitle {
160 | font-size: 20px;
161 | color: #fff;
162 | }
163 |
164 | &.draft {
165 | background: #d0d0d0;
166 | }
167 |
168 | &.deleted {
169 | background: #d0d0d0;
170 | }
171 | }
172 |
173 | .link-type,
174 | .link-type:focus {
175 | color: #337ab7;
176 | cursor: pointer;
177 |
178 | &:hover {
179 | color: rgb(32, 160, 255);
180 | }
181 | }
182 |
183 | .filter-container {
184 | padding-bottom: 10px;
185 |
186 | .filter-item {
187 | display: inline-block;
188 | vertical-align: middle;
189 | margin-bottom: 10px;
190 | }
191 | }
192 |
193 | //refine vue-multiselect plugin
194 | .multiselect {
195 | line-height: 16px;
196 | }
197 |
198 | .multiselect--active {
199 | z-index: 1000 !important;
200 | }
201 |
202 | //分页
203 | .el-pagination {
204 | margin-top: 15px;
205 | text-align: right;
206 | }
207 |
208 | // 微信二维码样式定制
209 | .weichat-container {
210 | height: 300px;
211 | iframe {
212 | width: 100%;
213 | height: 100%;
214 | }
215 | }
216 |
217 | .mt15 {
218 | margin-top: 15px;
219 | }
220 |
221 | .mb15 {
222 | margin-bottom: 15px;
223 | }
224 |
225 | .ml15 {
226 | margin-left: 15px;
227 | }
228 |
229 | .mr15 {
230 | margin-right: 15px;
231 | }
232 |
233 | .mt10 {
234 | margin-top: 10px;
235 | }
236 |
237 | .mb10 {
238 | margin-bottom: 10px;
239 | }
240 |
241 | .ml10 {
242 | margin-left: 10px;
243 | }
244 |
245 | .mr10 {
246 | margin-right: 10px;
247 | }
248 |
249 | .center {
250 | text-align: center;
251 | justify-content: center;
252 | display: flex;
253 | }
254 |
255 | .right {
256 | text-align: right;
257 | }
258 |
259 | //两边靠
260 | .flex-between {
261 | display: flex;
262 | justify-content: space-between;
263 | }
264 |
265 | .flex-center {
266 | display: flex;
267 | justify-content: space-between;
268 | align-items: center;
269 | }
270 |
271 | .color-primary {
272 | color: #700efa;
273 | }
274 |
275 | //json
276 | .json-container {
277 | position: relative;
278 | }
279 |
280 | //json右上角复制
281 | .json-copy {
282 | position: absolute;
283 | right: 20px;
284 | top: 0;
285 | font-size: 16px;
286 | cursor: pointer;
287 | }
288 |
--------------------------------------------------------------------------------
/src/assets/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | // 多行文本超出显示省略号
2 | @mixin n-ellipsis($n) {
3 | overflow: hidden;
4 | display: -webkit-box;
5 | -webkit-line-clamp: $n;
6 | -webkit-box-orient: vertical;
7 | text-overflow: ellipsis;
8 | word-break: break-all;
9 | }
10 |
11 | // 单行文本显示超出省略号
12 | @mixin ellipsis($w) {
13 | overflow: hidden;
14 | text-overflow: ellipsis;
15 | white-space: nowrap;
16 | width: $w;
17 | display: inline-block;
18 | }
19 |
--------------------------------------------------------------------------------
/src/assets/styles/variables.module.scss:
--------------------------------------------------------------------------------
1 | $base-menu-color: #fff;
2 | $base-menu-color-active: #000;
3 | $base-menu-color-hover: #000;
4 | $base-menu-background: #4921c3;
5 | $base-logo-title-color: #fff;
6 |
7 | $base-menu-light-color: rgba(0, 0, 0, 0.7);
8 | $base-menu-light-background: #ffffff;
9 | $base-logo-light-title-color: #001529;
10 |
11 | $base-sub-menu-background: #fff;
12 | $base-sub-menu-hover: #fff;
13 |
14 | $top-bar-bg-color: #4921c3;
15 | $top-bar-height: 45px;
16 |
17 | $--color-primary: #4921c3;
18 | $--color-success: #67c23a;
19 | $--color-warning: #e6a23c;
20 | $--color-danger: #f56c6c;
21 | $--color-info: #939cb7;
22 |
23 | $base-sidebar-width: 180px;
24 |
25 | $tableStripedColor: #f7f8fa; //表格斑马条纹背景色
26 | $tableHeaderBgColor: #f1f4f7; //表格表头背景
27 | $appRadius: 6px;
28 | $appRadiusLg: 8px;
29 |
30 | $dragPanelBgColor: #f0f4fb; //拖拽面板背景色
31 | $dragNodeBorderColor: #e6f7ff; //拖拽节点边框色
32 |
33 | :export {
34 | menuColor: $base-menu-color;
35 | menuLightColor: $base-menu-light-color;
36 | menuColorActive: $base-menu-color-active;
37 | menuBackground: $base-menu-background;
38 | menuLightBackground: $base-menu-light-background;
39 | subMenuBackground: $base-sub-menu-background;
40 | subMenuHover: $base-sub-menu-hover;
41 | sideBarWidth: $base-sidebar-width;
42 | logoTitleColor: $base-logo-title-color;
43 | logoLightTitleColor: $base-logo-light-title-color;
44 | primaryColor: $--color-primary;
45 | successColor: $--color-success;
46 | dangerColor: $--color-danger;
47 | infoColor: $--color-info;
48 | warningColor: $--color-warning;
49 | dragPanelBgColor: $dragPanelBgColor;
50 | dragNodeBorderColor: $dragNodeBorderColor;
51 | }
--------------------------------------------------------------------------------
/src/assets/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
44 |
45 |
62 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import './style.css';
3 | import App from './App.vue';
4 | import '@/assets/styles/index.scss'; // global css
5 | import ElementPlus from 'element-plus';
6 | import 'element-plus/dist/index.css';
7 | import '@/assets/styles/element-ui-theme.scss';
8 | import 'virtual:svg-icons-register'; // svg图标
9 | import elementIcons from '@/utils/elementIcons'; //@element-plus/icons-vue
10 |
11 | const app = createApp(App);
12 |
13 | app.use(elementIcons);
14 | app.use(ElementPlus, {
15 | size: 'default',
16 | });
17 | app.mount('#app');
18 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xoobom/logicflow-vue3/ef8cfcac396672e1a8ff183077c8495a3e2a498d/src/style.css
--------------------------------------------------------------------------------
/src/utils/elementIcons.ts:
--------------------------------------------------------------------------------
1 | import * as components from '@element-plus/icons-vue';
2 |
3 | export default {
4 | install: (app: any) => {
5 | for (const key in components) {
6 | const componentConfig = components[key];
7 | app.component(componentConfig.name, componentConfig);
8 | }
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { ElMessage } from "element-plus";
2 |
3 | /**
4 | * @param {string} input value
5 | * @returns {number} output value
6 | */
7 | export function byteLength(str) {
8 | // returns the byte length of an utf8 string
9 | let s = str.length;
10 | for (var i = str.length - 1; i >= 0; i--) {
11 | const code = str.charCodeAt(i);
12 | if (code > 0x7f && code <= 0x7ff) s++;
13 | else if (code > 0x7ff && code <= 0xffff) s += 2;
14 | if (code >= 0xdc00 && code <= 0xdfff) i--;
15 | }
16 | return s;
17 | }
18 |
19 | /**
20 | * @param {Array} actual
21 | * @returns {Array}
22 | */
23 | export function cleanArray(actual) {
24 | const newArray = [];
25 | for (let i = 0; i < actual.length; i++) {
26 | if (actual[i]) {
27 | newArray.push(actual[i]);
28 | }
29 | }
30 | return newArray;
31 | }
32 |
33 | /**
34 | * @param {Object} json
35 | * @returns {Array}
36 | */
37 | export function param(json) {
38 | if (!json) return "";
39 | return cleanArray(
40 | Object.keys(json).map((key) => {
41 | if (json[key] === undefined) return "";
42 | return encodeURIComponent(key) + "=" + encodeURIComponent(json[key]);
43 | })
44 | ).join("&");
45 | }
46 |
47 | /**
48 | * @param {string} url
49 | * @returns {Object}
50 | */
51 | export function param2Obj(url) {
52 | const search = decodeURIComponent(url.split("?")[1]).replace(/\+/g, " ");
53 | if (!search) {
54 | return {};
55 | }
56 | const obj = {};
57 | const searchArr = search.split("&");
58 | searchArr.forEach((v) => {
59 | const index = v.indexOf("=");
60 | if (index !== -1) {
61 | const name = v.substring(0, index);
62 | const val = v.substring(index + 1, v.length);
63 | obj[name] = val;
64 | }
65 | });
66 | return obj;
67 | }
68 |
69 | /**
70 | * @param {string} val
71 | * @returns {string}
72 | */
73 | export function html2Text(val) {
74 | const div = document.createElement("div");
75 | div.innerHTML = val;
76 | return div.textContent || div.innerText;
77 | }
78 |
79 | /**
80 | * Merges two objects, giving the last one precedence
81 | * @param {Object} target
82 | * @param {(Object|Array)} source
83 | * @returns {Object}
84 | */
85 | export function objectMerge(target, source) {
86 | if (typeof target !== "object") {
87 | target = {};
88 | }
89 | if (Array.isArray(source)) {
90 | return source.slice();
91 | }
92 | Object.keys(source).forEach((property) => {
93 | const sourceProperty = source[property];
94 | if (typeof sourceProperty === "object") {
95 | target[property] = objectMerge(target[property], sourceProperty);
96 | } else {
97 | target[property] = sourceProperty;
98 | }
99 | });
100 | return target;
101 | }
102 |
103 | /**
104 | * @param {HTMLElement} element
105 | * @param {string} className
106 | */
107 | export function toggleClass(element, className) {
108 | if (!element || !className) {
109 | return;
110 | }
111 | let classString = element.className;
112 | const nameIndex = classString.indexOf(className);
113 | if (nameIndex === -1) {
114 | classString += "" + className;
115 | } else {
116 | classString =
117 | classString.substr(0, nameIndex) +
118 | classString.substr(nameIndex + className.length);
119 | }
120 | element.className = classString;
121 | }
122 |
123 | /**
124 | * @param {string} type
125 | * @returns {Date}
126 | */
127 | export function getTime(type) {
128 | if (type === "start") {
129 | return new Date().getTime() - 3600 * 1000 * 24 * 90;
130 | } else {
131 | return new Date(new Date().toDateString());
132 | }
133 | }
134 |
135 | /**
136 | * @param {Function} func
137 | * @param {number} wait
138 | * @param {boolean} immediate
139 | * @return {*}
140 | */
141 | export function debounce(func, wait, immediate) {
142 | let timeout, args, context, timestamp, result;
143 |
144 | const later = function () {
145 | // 据上一次触发时间间隔
146 | const last = +new Date() - timestamp;
147 |
148 | // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
149 | if (last < wait && last > 0) {
150 | timeout = setTimeout(later, wait - last);
151 | } else {
152 | timeout = null;
153 | // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
154 | if (!immediate) {
155 | result = func.apply(context, args);
156 | if (!timeout) context = args = null;
157 | }
158 | }
159 | };
160 |
161 | return function (...args) {
162 | context = this;
163 | timestamp = +new Date();
164 | const callNow = immediate && !timeout;
165 | // 如果延时不存在,重新设定延时
166 | if (!timeout) timeout = setTimeout(later, wait);
167 | if (callNow) {
168 | result = func.apply(context, args);
169 | context = args = null;
170 | }
171 |
172 | return result;
173 | };
174 | }
175 |
176 | /**
177 | * This is just a simple version of deep copy
178 | * Has a lot of edge cases bug
179 | * If you want to use a perfect deep copy, use lodash's _.cloneDeep
180 | * @param {Object} source
181 | * @returns {Object}
182 | */
183 | export function deepClone(source) {
184 | if (!source && typeof source !== "object") {
185 | throw new Error("error arguments", "deepClone");
186 | }
187 | const targetObj = source.constructor === Array ? [] : {};
188 | Object.keys(source).forEach((keys) => {
189 | if (source[keys] && typeof source[keys] === "object") {
190 | targetObj[keys] = deepClone(source[keys]);
191 | } else {
192 | targetObj[keys] = source[keys];
193 | }
194 | });
195 | return targetObj;
196 | }
197 |
198 | /**
199 | * @param {Array} arr
200 | * @returns {Array}
201 | */
202 | export function uniqueArr(arr) {
203 | return Array.from(new Set(arr));
204 | }
205 |
206 | /**
207 | * @returns {string}
208 | */
209 | export function createUniqueString() {
210 | const timestamp = +new Date() + "";
211 | const randomNum = parseInt((1 + Math.random()) * 65536) + "";
212 | return (+(randomNum + timestamp)).toString(32);
213 | }
214 |
215 | /**
216 | * Check if an element has a class
217 | * @param {HTMLElement} elm
218 | * @param {string} cls
219 | * @returns {boolean}
220 | */
221 | export function hasClass(ele, cls) {
222 | return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
223 | }
224 |
225 | /**
226 | * Add class to element
227 | * @param {HTMLElement} elm
228 | * @param {string} cls
229 | */
230 | export function addClass(ele, cls) {
231 | if (!hasClass(ele, cls)) ele.className += " " + cls;
232 | }
233 |
234 | /**
235 | * Remove class from element
236 | * @param {HTMLElement} elm
237 | * @param {string} cls
238 | */
239 | export function removeClass(ele, cls) {
240 | if (hasClass(ele, cls)) {
241 | const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
242 | ele.className = ele.className.replace(reg, " ");
243 | }
244 | }
245 |
246 | export function makeMap(str, expectsLowerCase) {
247 | const map = Object.create(null);
248 | const list = str.split(",");
249 | for (let i = 0; i < list.length; i++) {
250 | map[list[i]] = true;
251 | }
252 | return expectsLowerCase ? (val) => map[val.toLowerCase()] : (val) => map[val];
253 | }
254 |
255 | export const exportDefault = "export default ";
256 |
257 | export const beautifierConf = {
258 | html: {
259 | indent_size: "2",
260 | indent_char: " ",
261 | max_preserve_newlines: "-1",
262 | preserve_newlines: false,
263 | keep_array_indentation: false,
264 | break_chained_methods: false,
265 | indent_scripts: "separate",
266 | brace_style: "end-expand",
267 | space_before_conditional: true,
268 | unescape_strings: false,
269 | jslint_happy: false,
270 | end_with_newline: true,
271 | wrap_line_length: "110",
272 | indent_inner_html: true,
273 | comma_first: false,
274 | e4x: true,
275 | indent_empty_lines: true,
276 | },
277 | js: {
278 | indent_size: "2",
279 | indent_char: " ",
280 | max_preserve_newlines: "-1",
281 | preserve_newlines: false,
282 | keep_array_indentation: false,
283 | break_chained_methods: false,
284 | indent_scripts: "normal",
285 | brace_style: "end-expand",
286 | space_before_conditional: true,
287 | unescape_strings: false,
288 | jslint_happy: true,
289 | end_with_newline: true,
290 | wrap_line_length: "110",
291 | indent_inner_html: true,
292 | comma_first: false,
293 | e4x: true,
294 | indent_empty_lines: true,
295 | },
296 | };
297 |
298 | // 首字母大小
299 | export function titleCase(str) {
300 | return str.replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
301 | }
302 |
303 | // 下划转驼峰
304 | export function camelCase(str) {
305 | return str.replace(/_[a-z]/g, (str1) => str1.substr(-1).toUpperCase());
306 | }
307 |
308 | export function isNumberStr(str) {
309 | return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str);
310 | }
311 |
312 | export function obj2Param(obj) {
313 | return Object.keys(obj)
314 | .filter((i) => (obj[i] === false ? String(obj[i]) : obj[i]))
315 | .map((n) =>
316 | Array.isArray(obj[n]) ? arrayParams(n, obj[n]) : [n, obj[n]].join("=")
317 | )
318 | .join("&");
319 | }
320 |
321 | // 生成19位唯一数字标识符,来自chatgpt
322 | export const randomNumber = () => {
323 | var timestamp = Date.now().toString(); // 获取当前时间戳
324 | var random = Math.floor(Math.random() * 9000000000000000) + 1000000000000000; // 生成15位随机数
325 | var identifier = timestamp + random.toString(); // 结合时间戳和随机数
326 | if (identifier.length > 19) {
327 | identifier = identifier.substr(0, 19); // 如果标识符超过19位,则截取前19位
328 | }
329 | if (identifier.charAt(0) === "0") {
330 | identifier = "1" + identifier.substr(1); // 如果开头是0,则将第一个字符替换为1
331 | }
332 | return identifier;
333 | };
334 |
335 | //logicflow-获取节点输出几条线。graph是json数组,node是节点数据
336 | export const getNodeOutputCount = (graph, nodeData) => {
337 | // 找到所有节点
338 | const nodes = graph.nodes;
339 | // 找到所有边
340 | const edges = graph.edges;
341 | // 初始化节点输出线条数量
342 | let count = 0;
343 | // 遍历所有节点
344 | for (const node of nodes) {
345 | // 判断节点类型是否为
346 | if (node.id === nodeData.id) {
347 | // 遍历所有边,查找与该节点相关的边
348 | for (const edge of edges) {
349 | if (edge.sourceNodeId === node.id) {
350 | // 如果找到了与该节点相连的边,增加计数
351 | count++;
352 | }
353 | }
354 | }
355 | }
356 | return count; // 节点输出线条数量
357 | };
358 |
359 | //logicflow-获取节点输入几条线。graph是json数组,node是节点数据
360 | export const getNodeInputCount = (graph, nodeData) => {
361 | // 找到所有节点
362 | const nodes = graph.nodes;
363 | // 找到所有边
364 | const edges = graph.edges;
365 | // 初始化节点输出线条数量
366 | let count = 0;
367 | // 遍历所有节点
368 | for (const node of nodes) {
369 | // 判断节点类型是否为
370 | if (node.id === nodeData.id) {
371 | // 遍历所有边,查找与该节点相关的边
372 | for (const edge of edges) {
373 | if (edge.targetNodeId === node.id) {
374 | // 如果找到了与该节点相连的边,增加计数
375 | count++;
376 | }
377 | }
378 | }
379 | }
380 | return count; // 节点输出线条数量
381 | };
382 |
383 | //判断数组是否为空或者包含空。true为空
384 | export function arrHasEmpty(arr) {
385 | let arrTemp = arr.filter(Boolean);
386 | if (arr.length == 0) {
387 | return true;
388 | } else {
389 | if (arr.length != arrTemp.length) {
390 | return true;
391 | } else {
392 | return false;
393 | }
394 | }
395 | }
396 |
397 | //判断数组是否有值重复
398 | export const arrHasDuplicate = (arr) => {
399 | return new Set(arr).size !== arr.length;
400 | };
401 |
402 | //服务编排-校验边
403 | export const validEdgeType = (lf) => {
404 | let nodeTypes = [];
405 | lf.getGraphData().nodes.forEach((i) => {
406 | nodeTypes.push(i.type);
407 | });
408 | //校验决策条件是否为空。返回true校验通过false校验不通过
409 | if (nodeTypes.includes("decision")) {
410 | let arr = [];
411 | lf.getGraphData().edges.forEach((i) => {
412 | if (i.properties.type == "decision") {
413 | arr.push(i.properties.edgeType);
414 | }
415 | });
416 | return !arrHasEmpty(arr);
417 | } else {
418 | return true;
419 | }
420 | };
421 |
422 | //通过源节点获取所有输出的边
423 | export const getEdgesFormSourceNodeId = (graphData, sourceNodeId) => {
424 | let edges = [];
425 | graphData.edges.forEach((i) => {
426 | if (i.sourceNodeId == sourceNodeId) {
427 | edges.push(i);
428 | }
429 | });
430 | return edges;
431 | };
432 |
433 | //复制
434 | export const copyFunc = (data) => {
435 | if (data) {
436 | let oInput = document.createElement("input");
437 | oInput.value = data;
438 | document.body.appendChild(oInput);
439 | oInput.select();
440 | ElMessage.success("复制成功");
441 | document.execCommand("Copy");
442 | document.body.removeChild(oInput);
443 | }
444 | };
445 |
446 | /**
447 | * @description:根据数组将value变成中文
448 | * @param {*} value value值
449 | * @param {*} arr 枚举数据
450 | * @param {*} typeValue 英文字段
451 | * @param {*} typeLabel 中文字段
452 | * @return {*}
453 | */
454 | export const getLabelByValue = (value, arr, typeValue, typeLabel) => {
455 | let label = "";
456 | arr.forEach((i) => {
457 | if (i[typeValue] == value) {
458 | label = i[typeLabel];
459 | }
460 | });
461 | return label;
462 | };
463 |
464 | /**
465 | * @description:根据数组通过value拿type
466 | * @param {*} value value值
467 | * @param {*} arr 枚举数据
468 | * @param {*} typeValue 英文字段
469 | * @return {*}
470 | */
471 | export const getTypeByValue = (value, arr, typeValue) => {
472 | let type = "";
473 | arr.forEach((i) => {
474 | if (i[typeValue] == value) {
475 | type = i["type"];
476 | }
477 | });
478 | return type;
479 | };
480 |
--------------------------------------------------------------------------------
/src/utils/options.ts:
--------------------------------------------------------------------------------
1 | //所有枚举
2 |
3 | //服务编排-图元类型
4 | export const pixelOption = [
5 | {
6 | value: 'start',
7 | label: '开始',
8 | },
9 | {
10 | value: 'assignment',
11 | label: '节点1-1',
12 | },
13 | {
14 | value: 'decision',
15 | label: '节点1-2',
16 | },
17 | {
18 | value: 'startParallel',
19 | label: '节点1-3',
20 | },
21 | {
22 | value: 'endParallel',
23 | label: '节点1-4',
24 | },
25 | {
26 | value: 'machineLearning',
27 | label: '节点2-1',
28 | },
29 | {
30 | value: 'deepLearning',
31 | label: '节点2-2',
32 | },
33 | {
34 | value: 'myBezier',
35 | label: '连线',
36 | },
37 | ];
--------------------------------------------------------------------------------
/src/views/design/LFComponents/Control.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 |
31 |
32 |
90 |
112 |
--------------------------------------------------------------------------------
/src/views/design/LFComponents/NodePanel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ node.label }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
190 |
276 |
--------------------------------------------------------------------------------
/src/views/design/LFComponents/top.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 查看JSON
6 | 保存
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
62 |
63 |
78 |
--------------------------------------------------------------------------------
/src/views/design/LFComponents/view-json.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
58 |
59 |
--------------------------------------------------------------------------------
/src/views/design/PropertySetting/PropertyDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ getLabelByValue(nodeData.type, pixelOption, 'value', 'label') }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
68 |
69 |
--------------------------------------------------------------------------------
/src/views/design/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
473 |
567 |
--------------------------------------------------------------------------------
/src/views/design/registerEdge/myBezier.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 取消
12 | 确定
13 |
14 |
15 |
16 |
64 |
65 |
--------------------------------------------------------------------------------
/src/views/design/registerEdge/registerBezier.ts:
--------------------------------------------------------------------------------
1 | // 贝塞尔曲线
2 | import { BezierEdge, BezierEdgeModel } from '@logicflow/core';
3 | import { randomNumber } from '@/utils/index';
4 | class BezierModel extends BezierEdgeModel {
5 | createId() {
6 | return randomNumber();
7 | }
8 | constructor(data, graphModel) {
9 | super(data, graphModel);
10 | this.menu = [
11 | {
12 | text: '配置',
13 | callback: (res) => {
14 | graphModel.eventCenter.emit('edge:app-config', res); //发出事件
15 | },
16 | },
17 | {
18 | text: '删除',
19 | callback(res) {
20 | graphModel.deleteEdgeById(res.id);
21 | },
22 | },
23 | ];
24 | }
25 | }
26 |
27 | export default {
28 | type: 'myBezier',
29 | view: BezierEdge,
30 | model: BezierModel,
31 | };
32 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/assignment/assignment.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import assignment from './assignment.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('assignment', ({ HtmlNode, HtmlNodeModel }) => {
8 | class RuleNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(assignment, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 |
28 | class RuleModel extends HtmlNodeModel {
29 | createId() {
30 | return randomNumber(); //id用随机数数字
31 | }
32 | constructor(data, graphModel) {
33 | super(data, graphModel);
34 | // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
35 | this.menu = [
36 | {
37 | text: '删除',
38 | callback(node) {
39 | lf.deleteNode(node.id);
40 | },
41 | },
42 | {
43 | text: '复制',
44 | callback(node) {
45 | lf.cloneNode(node.id);
46 | },
47 | },
48 | ];
49 | }
50 | getDefaultAnchor() {
51 | const { id, x, y, width, height } = this;
52 | const anchors = [];
53 | anchors.push({
54 | x,
55 | y: y - height / 2,
56 | id: `${id}_incomming`,
57 | type: 'incomming',
58 | });
59 | anchors.push({
60 | x,
61 | y: y + height / 2,
62 | id: `${id}_outgoing`,
63 | type: 'outgoing',
64 | });
65 | return anchors;
66 | }
67 | initNodeData(data) {
68 | super.initNodeData(data);
69 | const width = 140;
70 | const height = 40;
71 | this.width = width;
72 | this.height = height;
73 | this.radius = 50;
74 | this.targetRules = [
75 | // {
76 | // message: `【${nodeNameZh}】只允许一个输入`,
77 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
78 | // const edges = this.graphModel.getNodeIncomingEdge(targetNode.id);
79 | // if (edges.length >= 1) {
80 | // return false;
81 | // } else {
82 | // return true;
83 | // }
84 | // },
85 | // },
86 | // {
87 | // message: '输入不允许连接输入',
88 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
89 | // return sourceAnchor.type === 'outgoing';
90 | // },
91 | // },
92 | ];
93 | this.sourceRules = [
94 | // {
95 | // message: `【${nodeNameZh}】只允许1个输出`,
96 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
97 | // const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id);
98 | // if (edges.length >= 1) return false;
99 | // return true;
100 | // },
101 | // },
102 | // {
103 | // message: '输出不允许连接输出',
104 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
105 | // return targetAnchor.type === 'incomming';
106 | // },
107 | // },
108 | ];
109 | }
110 | }
111 | return {
112 | view: RuleNode,
113 | model: RuleModel,
114 | };
115 | });
116 | }
117 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/assignment/assignment.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
38 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/assignment/assignmentProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/decision/decision.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import decision from './decision.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('decision', ({ HtmlNode, HtmlNodeModel }) => {
8 | class RuleNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(decision, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 |
28 | class RuleModel extends HtmlNodeModel {
29 | createId() {
30 | return randomNumber(); //id用随机数数字
31 | }
32 | constructor(data, graphModel) {
33 | super(data, graphModel);
34 | // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
35 | this.menu = [
36 | {
37 | text: '删除',
38 | callback(node) {
39 | lf.deleteNode(node.id);
40 | },
41 | },
42 | {
43 | text: '复制',
44 | callback(node) {
45 | lf.cloneNode(node.id);
46 | },
47 | },
48 | ];
49 | }
50 | getDefaultAnchor() {
51 | const { id, x, y, width, height } = this;
52 | const anchors = [];
53 | anchors.push({
54 | x,
55 | y: y - height / 2,
56 | id: `${id}_incomming`,
57 | type: 'incomming',
58 | });
59 | anchors.push({
60 | x,
61 | y: y + height / 2,
62 | id: `${id}_outgoing`,
63 | type: 'outgoing',
64 | });
65 | return anchors;
66 | }
67 | initNodeData(data) {
68 | super.initNodeData(data);
69 | const width = 140;
70 | const height = 40;
71 | this.width = width;
72 | this.height = height;
73 | this.radius = 50;
74 | this.targetRules = [
75 | // {
76 | // message: `【${nodeNameZh}】只允许一个输入`,
77 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
78 | // const edges = this.graphModel.getNodeIncomingEdge(targetNode.id);
79 | // if (edges.length >= 1) {
80 | // return false;
81 | // } else {
82 | // return true;
83 | // }
84 | // },
85 | // },
86 | // {
87 | // message: '输入不允许连接输入',
88 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
89 | // return sourceAnchor.type === 'outgoing';
90 | // },
91 | // },
92 | ];
93 | this.sourceRules = [
94 | // {
95 | // message: '输出不允许连接输出',
96 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
97 | // return targetAnchor.type === 'incomming';
98 | // },
99 | // },
100 | ];
101 | }
102 | }
103 | return {
104 | view: RuleNode,
105 | model: RuleModel,
106 | };
107 | });
108 | }
109 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/decision/decision.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
37 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/decision/decisionProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/deepLearning/deepLearning.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import deepLearning from './deepLearning.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('deepLearning', ({ HtmlNode, HtmlNodeModel }) => {
8 | class RuleNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(deepLearning, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 | class RuleModel extends HtmlNodeModel {
28 | createId() {
29 | return randomNumber(); //id用随机数数字
30 | }
31 | constructor(data, graphModel) {
32 | super(data, graphModel);
33 | // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
34 | this.menu = [
35 | {
36 | text: '删除',
37 | callback(node) {
38 | lf.deleteNode(node.id);
39 | },
40 | },
41 | {
42 | text: '复制',
43 | callback(node) {
44 | lf.cloneNode(node.id);
45 | },
46 | },
47 | ];
48 | }
49 | getDefaultAnchor() {
50 | const { id, x, y, width, height } = this;
51 | const anchors = [];
52 | anchors.push({
53 | x,
54 | y: y - height / 2,
55 | id: `${id}_incomming`,
56 | type: 'incomming',
57 | });
58 | anchors.push({
59 | x,
60 | y: y + height / 2,
61 | id: `${id}_outgoing`,
62 | type: 'outgoing',
63 | });
64 | return anchors;
65 | }
66 | initNodeData(data) {
67 | super.initNodeData(data);
68 | const width = 140;
69 | const height = 40;
70 | this.width = width;
71 | this.height = height;
72 | this.radius = 50;
73 | this.targetRules = [
74 | // {
75 | // message: `【${nodeNameZh}】只允许一个输入`,
76 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
77 | // const edges = this.graphModel.getNodeIncomingEdge(targetNode.id);
78 | // if (edges.length >= 1) {
79 | // return false;
80 | // } else {
81 | // return true;
82 | // }
83 | // },
84 | // },
85 | // {
86 | // message: '输入不允许连接输入',
87 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
88 | // return sourceAnchor.type === 'outgoing';
89 | // },
90 | // },
91 | ];
92 |
93 | this.sourceRules = [
94 | // {
95 | // message: `【${nodeNameZh}】只允许1个输出`,
96 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
97 | // const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id);
98 | // if (edges.length >= 1) return false;
99 | // return true;
100 | // },
101 | // },
102 | // {
103 | // message: '输出不允许连接输出',
104 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
105 | // return targetAnchor.type === 'incomming';
106 | // },
107 | // },
108 | ];
109 | }
110 | }
111 | return {
112 | view: RuleNode,
113 | model: RuleModel,
114 | };
115 | });
116 | }
117 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/deepLearning/deepLearning.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/deepLearning/deepLearningProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/endParallel/endParallel.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import endParallel from './endParallel.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('endParallel', ({ HtmlNode, HtmlNodeModel }) => {
8 | class RuleNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(endParallel, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 | class RuleModel extends HtmlNodeModel {
28 | createId() {
29 | return randomNumber(); //id用随机数数字
30 | }
31 | constructor(data, graphModel) {
32 | super(data, graphModel);
33 | // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
34 | this.menu = [
35 | {
36 | text: '删除',
37 | callback(node) {
38 | lf.deleteNode(node.id);
39 | },
40 | },
41 | {
42 | text: '复制',
43 | callback(node) {
44 | lf.cloneNode(node.id);
45 | },
46 | },
47 | ];
48 | }
49 | getDefaultAnchor() {
50 | const { id, x, y, width, height } = this;
51 | const anchors = [];
52 | anchors.push({
53 | x,
54 | y: y - height / 2,
55 | id: `${id}_incomming`,
56 | type: 'incomming',
57 | });
58 | anchors.push({
59 | x,
60 | y: y + height / 2,
61 | id: `${id}_outgoing`,
62 | type: 'outgoing',
63 | });
64 | return anchors;
65 | }
66 | initNodeData(data) {
67 | super.initNodeData(data);
68 | const width = 140;
69 | const height = 40;
70 | this.width = width;
71 | this.height = height;
72 | this.radius = 50;
73 | this.targetRules = [
74 | // {
75 | // message: '输入不允许连接输入',
76 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
77 | // return sourceAnchor.type === 'outgoing';
78 | // },
79 | // },
80 | ];
81 | this.sourceRules = [
82 | // {
83 | // message: `【${nodeNameZh}】只允许1个输出`,
84 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
85 | // const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id);
86 | // if (edges.length >= 1) return false;
87 | // return true;
88 | // },
89 | // },
90 | // {
91 | // message: '输出不允许连接输出',
92 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
93 | // return targetAnchor.type === 'incomming';
94 | // },
95 | // },
96 | ];
97 | }
98 | }
99 | return {
100 | view: RuleNode,
101 | model: RuleModel,
102 | };
103 | });
104 | }
105 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/endParallel/endParallel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/endParallel/endParallelProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/machineLearning/machineLearning.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import machineLearning from './machineLearning.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('machineLearning', ({ HtmlNode, HtmlNodeModel }) => {
8 | class RuleNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(machineLearning, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 | class RuleModel extends HtmlNodeModel {
28 | createId() {
29 | return randomNumber(); //id用随机数数字
30 | }
31 | constructor(data, graphModel) {
32 | super(data, graphModel);
33 | // 右键菜单自由配置,也可以通过边的properties或者其他属性条件更换不同菜单
34 | this.menu = [
35 | {
36 | text: '删除',
37 | callback(node) {
38 | lf.deleteNode(node.id);
39 | },
40 | },
41 | {
42 | text: '复制',
43 | callback(node) {
44 | lf.cloneNode(node.id);
45 | },
46 | },
47 | ];
48 | }
49 | getDefaultAnchor() {
50 | const { id, x, y, width, height } = this;
51 | const anchors = [];
52 | anchors.push({
53 | x,
54 | y: y - height / 2,
55 | id: `${id}_incomming`,
56 | type: 'incomming',
57 | });
58 | anchors.push({
59 | x,
60 | y: y + height / 2,
61 | id: `${id}_outgoing`,
62 | type: 'outgoing',
63 | });
64 | return anchors;
65 | }
66 | initNodeData(data) {
67 | super.initNodeData(data);
68 | const width = 140;
69 | const height = 40;
70 | this.width = width;
71 | this.height = height;
72 | this.radius = 50;
73 | this.targetRules = [
74 | // {
75 | // message: `【${nodeNameZh}】只允许一个输入`,
76 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
77 | // const edges = this.graphModel.getNodeIncomingEdge(targetNode.id);
78 | // if (edges.length >= 1) {
79 | // return false;
80 | // } else {
81 | // return true;
82 | // }
83 | // },
84 | // },
85 | // {
86 | // message: '输入不允许连接输入',
87 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
88 | // return sourceAnchor.type === 'outgoing';
89 | // },
90 | // },
91 | ];
92 |
93 | this.sourceRules = [
94 | // {
95 | // message: `【${nodeNameZh}】只允许1个输出`,
96 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
97 | // const edges = this.graphModel.getNodeOutgoingEdge(sourceNode.id);
98 | // if (edges.length >= 1) return false;
99 | // return true;
100 | // },
101 | // },
102 | // {
103 | // message: '输出不允许连接输出',
104 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
105 | // return targetAnchor.type === 'incomming';
106 | // },
107 | // },
108 | ];
109 | }
110 | }
111 | return {
112 | view: RuleNode,
113 | model: RuleModel,
114 | };
115 | });
116 | }
117 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/machineLearning/machineLearning.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/machineLearning/machineLearningProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/start/start.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import start from './start.vue';
3 | import { randomNumber } from '@/utils/index';
4 |
5 | export default function registerConnect(lf) {
6 | lf.register('start', ({ HtmlNode, HtmlNodeModel }) => {
7 | class RuleNode extends HtmlNode {
8 | setHtml(rootEl) {
9 | const { model } = this.props;
10 | const el = document.createElement('div');
11 | rootEl.innerHTML = '';
12 | rootEl.appendChild(el);
13 |
14 | // Vue 3 使用 createApp 来创建应用实例
15 | const app = createApp({
16 | render: () =>
17 | h(start, {
18 | properties: model.properties,
19 | }),
20 | });
21 |
22 | // 挂载 Vue 应用到元素上
23 | app.mount(el);
24 | }
25 | }
26 | class RuleModel extends HtmlNodeModel {
27 | createId() {
28 | return randomNumber();
29 | }
30 | constructor(data, graphModel) {
31 | super(data, graphModel);
32 | this.menu = [];
33 | }
34 | getDefaultAnchor() {
35 | const { id, x, y, width, height } = this;
36 | const anchors = [];
37 | anchors.push({
38 | x,
39 | y: y + height / 2,
40 | id: `${id}_outgoing`,
41 | type: 'outgoing',
42 | });
43 | return anchors;
44 | }
45 | initNodeData(data) {
46 | super.initNodeData(data);
47 | const width = 140;
48 | const height = 40;
49 | this.width = width;
50 | this.height = height;
51 | this.radius = 50;
52 | }
53 | }
54 | return {
55 | view: RuleNode,
56 | model: RuleModel,
57 | };
58 | });
59 | }
60 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/start/start.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
37 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/start/startProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/startParallel/startParallel.ts:
--------------------------------------------------------------------------------
1 | import { createApp, h } from 'vue';
2 | import startParallel from './startParallel.vue';
3 | import { randomNumber } from '@/utils/index';
4 | let nodeNameZh = '';
5 |
6 | export default function registerConnect(lf) {
7 | lf.register('startParallel', ({ HtmlNode, HtmlNodeModel }) => {
8 | class myNode extends HtmlNode {
9 | setHtml(rootEl) {
10 | const { model } = this.props;
11 | const el = document.createElement('div');
12 | rootEl.innerHTML = '';
13 | rootEl.appendChild(el);
14 |
15 | // Vue 3 使用 createApp 来创建应用实例
16 | const app = createApp({
17 | render: () =>
18 | h(startParallel, {
19 | properties: model.properties,
20 | }),
21 | });
22 |
23 | // 挂载 Vue 应用到元素上
24 | app.mount(el);
25 | }
26 | }
27 | class myModel extends HtmlNodeModel {
28 | createId() {
29 | return randomNumber(); //id用随机数数字
30 | }
31 | constructor(data, graphModel) {
32 | super(data, graphModel);
33 | this.menu = [
34 | {
35 | text: '删除',
36 | callback(node) {
37 | lf.deleteNode(node.id);
38 | },
39 | },
40 | {
41 | text: '复制',
42 | callback(node) {
43 | lf.cloneNode(node.id);
44 | },
45 | },
46 | ];
47 | }
48 | getDefaultAnchor() {
49 | const { id, x, y, width, height } = this;
50 | const anchors = [];
51 | anchors.push({
52 | x,
53 | y: y - height / 2,
54 | id: `${id}_incomming`,
55 | type: 'incomming',
56 | });
57 | anchors.push({
58 | x,
59 | y: y + height / 2,
60 | id: `${id}_outgoing`,
61 | type: 'outgoing',
62 | });
63 | return anchors;
64 | }
65 | initNodeData(data) {
66 | super.initNodeData(data);
67 | const width = 140;
68 | const height = 40;
69 | this.width = width;
70 | this.height = height;
71 | this.radius = 50;
72 | this.targetRules = [
73 | // {
74 | // message: `【${nodeNameZh}】只允许一个输入`,
75 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
76 | // const edges = this.graphModel.getNodeIncomingEdge(targetNode.id);
77 | // if (edges.length >= 1) {
78 | // return false;
79 | // } else {
80 | // return true;
81 | // }
82 | // },
83 | // },
84 | // {
85 | // message: '输入不允许连接输入',
86 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
87 | // return sourceAnchor.type === 'outgoing';
88 | // },
89 | // },
90 | ];
91 | this.sourceRules = [
92 | // {
93 | // message: '输出不允许连接输出',
94 | // validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {
95 | // return targetAnchor.type === 'incomming';
96 | // },
97 | // },
98 | ];
99 | }
100 | }
101 | return {
102 | view: myNode,
103 | model: myModel,
104 | };
105 | });
106 | }
107 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/startParallel/startParallel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/src/views/design/registerNode/startParallel/startParallelProperty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 取消
13 | 确定
14 |
15 |
16 |
17 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/design/template.vue:
--------------------------------------------------------------------------------
1 |
2 | 模版页面
3 |
4 |
9 |
11 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | //修复找不到.vue类型声明
4 | declare module '*.vue' {
5 | import type { DefineComponent } from 'vue';
6 | const vueComponent: DefineComponent<{}, {}, any>;
7 | export default vueComponent;
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "preserve",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import vue from '@vitejs/plugin-vue';
3 | import path from 'path';
4 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [
9 | vue(),
10 | createSvgIconsPlugin({
11 | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
12 | symbolId: 'icon-[dir]-[name]',
13 | }),
14 | ],
15 | resolve: {
16 | alias: {
17 | '@': path.resolve(__dirname, './src'),
18 | },
19 | extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
20 | },
21 | // 设置scss的api类型为modern-compiler
22 | css: {
23 | preprocessorOptions: {
24 | scss: {
25 | api: 'modern-compiler'
26 | }
27 | }
28 | },
29 | });
30 |
--------------------------------------------------------------------------------