@cicctencent/low-code-editor 0.0.0-stage → 1.1.9-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/api-extractor.json +36 -0
- package/dist/ActivityBar/index.vue.d.ts +29 -0
- package/dist/ComponentTree/components/List.vue.d.ts +29 -0
- package/dist/ComponentTree/index.vue.d.ts +32 -0
- package/dist/DataSource/index.vue.d.ts +34 -0
- package/dist/HeaderBar/components/BarTooltip.vue.d.ts +37 -0
- package/dist/HeaderBar/index.vue.d.ts +49 -0
- package/dist/NavTree/index.vue.d.ts +48 -0
- package/dist/SchemaConfig/index.vue.d.ts +36 -0
- package/dist/SidebarPane/index.vue.d.ts +38 -0
- package/dist/components/ComponentTreeFavorites/components/List.vue.d.ts +41 -0
- package/dist/components/ComponentTreeFavorites/components/Tips.vue.d.ts +16 -0
- package/dist/components/ComponentTreeFavorites/index.vue.d.ts +44 -0
- package/dist/components/ImageGuide/index.vue.d.ts +45 -0
- package/dist/components.d.ts +7 -0
- package/dist/composables/useComponentTree.d.ts +13 -0
- package/dist/config/ace.config.d.ts +3 -0
- package/dist/domain/EditorDomain.d.ts +89 -0
- package/dist/editor.d.ts +286 -0
- package/dist/editor.vue.d.ts +104 -0
- package/dist/formily/ActivityBar/index.d.ts +4 -0
- package/dist/formily/ComponentTree/index.d.ts +4 -0
- package/dist/formily/DataBinding/index.d.ts +4 -0
- package/dist/formily/DataBinding/index.vue.d.ts +26 -0
- package/dist/formily/DataSource/index.d.ts +4 -0
- package/dist/formily/MenuTable/PageList.vue.d.ts +28 -0
- package/dist/formily/MenuTable/index.d.ts +4 -0
- package/dist/formily/MenuTable/index.vue.d.ts +30 -0
- package/dist/formily/MenuTable/useMenuTable.d.ts +227 -0
- package/dist/formily/NavTree/index.d.ts +4 -0
- package/dist/formily/SidebarPane/index.d.ts +4 -0
- package/dist/formily/index.d.ts +2 -0
- package/dist/index.css +1167 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.js +34171 -0
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/index.d.ts +27 -0
- package/package.json +58 -4
- package/rollup.config.js +3 -0
- package/src/ActivityBar/index.vue +71 -0
- package/src/ComponentTree/components/List.vue +310 -0
- package/src/ComponentTree/index.vue +77 -0
- package/src/DataSource/index.vue +640 -0
- package/src/HeaderBar/components/BarDialog.vue +43 -0
- package/src/HeaderBar/components/BarTooltip.vue +33 -0
- package/src/HeaderBar/index.vue +513 -0
- package/src/NavTree/index.vue +345 -0
- package/src/SchemaConfig/index.vue +150 -0
- package/src/SidebarPane/index.vue +199 -0
- package/src/SynBarComponent/index.vue +31 -0
- package/src/assets/font/NotoSansSC.ttf +0 -0
- package/src/assets/img/header/edit.png +0 -0
- package/src/assets/img/header/link.png +0 -0
- package/src/assets/img/header/link.svg +9 -0
- package/src/assets/img/header/preview.png +0 -0
- package/src/assets/img/header/preview.svg +9 -0
- package/src/assets/img/header/redo.svg +1 -0
- package/src/assets/img/header/undo.svg +1 -0
- package/src/assets/img/tool/all-select.svg +9 -0
- package/src/assets/img/tool/all.svg +9 -0
- package/src/assets/img/tool/collect.svg +6 -0
- package/src/assets/img/tool/collection-select.svg +3 -0
- package/src/assets/img/tool/collection-tag.svg +5 -0
- package/src/assets/img/tool/info.svg +4 -0
- package/src/assets/img/tool/union.svg +4 -0
- package/src/components/ComponentTreeFavorites/components/List.vue +452 -0
- package/src/components/ComponentTreeFavorites/components/Tips.vue +108 -0
- package/src/components/ComponentTreeFavorites/index.vue +180 -0
- package/src/components/ImageGuide/index.vue +100 -0
- package/src/components.ts +23 -0
- package/src/composables/useComponentTree.ts +38 -0
- package/src/config/ace.config.ts +22 -0
- package/src/domain/EditorDomain.ts +265 -0
- package/src/editor.ts +645 -0
- package/src/editor.vue +380 -0
- package/src/formily/ActivityBar/index.ts +23 -0
- package/src/formily/ActivityBar/index.vue +121 -0
- package/src/formily/ComponentTree/index.ts +22 -0
- package/src/formily/DataBinding/index.ts +22 -0
- package/src/formily/DataBinding/index.vue +285 -0
- package/src/formily/DataSource/index.ts +22 -0
- package/src/formily/MenuTable/PageList.vue +132 -0
- package/src/formily/MenuTable/index.ts +19 -0
- package/src/formily/MenuTable/index.vue +218 -0
- package/src/formily/MenuTable/useMenuTable.ts +147 -0
- package/src/formily/NavTree/index.ts +20 -0
- package/src/formily/SidebarPane/index.ts +23 -0
- package/src/formily/SidebarPane/index.vue +174 -0
- package/src/formily/index.ts +12 -0
- package/src/index.ts +29 -0
- package/src/typings/vue-shim.d.ts +13 -0
- package/src/utils/base64.ts +5 -0
- package/src/utils/index.ts +57 -0
- package/test/case.ts +67 -0
- package/test/utils/index.test.ts +27 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="data-binding">
|
|
3
|
+
<VueForm :schema="config" :model-value="formData" />
|
|
4
|
+
<div class="setter-actions" @click="onClickIcon">
|
|
5
|
+
<span class="title">
|
|
6
|
+
<b class="title-icon">
|
|
7
|
+
<svg
|
|
8
|
+
fill="currentColor"
|
|
9
|
+
preserveAspectRatio="xMidYMid meet"
|
|
10
|
+
width="20"
|
|
11
|
+
height="20"
|
|
12
|
+
viewBox="0 0 1024 1024"
|
|
13
|
+
data-spm-anchor-id="a2q5o.26736379.0.i29.1c952a4bJg2W1k"
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d="M596.32 263.392c18.048 6.56 27.328 26.496 20.8 44.512l-151.04 414.912a34.72 34.72 0 1 1-65.28-23.744l151.04-414.912a34.72 34.72 0 0 1 44.48-20.768zM220.64 192H273.6v55.488H233.024c-26.112 0-38.464 14.4-38.464 44.544v134.304c0 42.496-19.936 71.264-59.104 85.664 39.168 16.448 59.104 44.544 59.104 85.664v134.976c0 28.8 12.352 43.84 38.464 43.84H273.6V832H220.672c-30.24 0-53.6-10.272-70.08-29.44-15.136-17.856-22.72-42.496-22.72-72.64v-128.832c0-19.872-4.096-34.24-12.352-43.2-9.6-10.944-26.784-16.416-51.52-17.792v-56.192c24.736-1.376 41.92-7.52 51.52-17.824 8.256-9.6 12.384-24 12.384-43.168V294.784c0-30.848 7.552-55.488 22.688-73.312C167.04 201.6 190.4 192 220.672 192z m529.792 0h52.896c30.24 0 53.6 9.6 70.08 29.44 15.136 17.856 22.72 42.496 22.72 73.344v128.128c0 19.2 4.096 34.24 13.024 43.84 8.96 9.6 26.112 15.776 50.848 17.152v56.192c-24.736 1.376-41.92 6.848-51.52 17.824-8.256 8.896-12.384 23.296-12.384 43.168v128.8c0 30.176-7.552 54.816-22.688 72.64-16.48 19.2-39.84 29.472-70.08 29.472h-52.896v-55.488h40.544c25.408 0 38.464-15.104 38.464-43.84v-135.04c0-41.088 19.232-69.184 59.104-85.632-39.872-14.4-59.104-43.168-59.104-85.664V292.032c0-30.144-13.056-44.544-38.464-44.544H750.4V192z"
|
|
17
|
+
></path>
|
|
18
|
+
</svg>
|
|
19
|
+
</b>
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
<el-dialog
|
|
23
|
+
v-model="dialogVisible"
|
|
24
|
+
title="变量绑定"
|
|
25
|
+
width="80%"
|
|
26
|
+
append-to-body
|
|
27
|
+
destroy-on-close
|
|
28
|
+
>
|
|
29
|
+
<div class="variable-dialog-content">
|
|
30
|
+
<div class="variable-selector">
|
|
31
|
+
<p class="variable-title">变量列表</p>
|
|
32
|
+
<div class="variable-selector-inner">
|
|
33
|
+
<ul class="variable-selector-category">
|
|
34
|
+
<li class="active">数据源</li>
|
|
35
|
+
</ul>
|
|
36
|
+
<div class="variable-selector-items-container">
|
|
37
|
+
<div class="selector-search-control">
|
|
38
|
+
<el-input
|
|
39
|
+
v-model="keyword"
|
|
40
|
+
@input="onInputChange"
|
|
41
|
+
placeholder="搜索"
|
|
42
|
+
:suffix-icon="Search"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
<ul class="variable-selector-items">
|
|
46
|
+
<li
|
|
47
|
+
v-for="item in itemList"
|
|
48
|
+
:key="item.value"
|
|
49
|
+
@click="onItemClick(item)"
|
|
50
|
+
>
|
|
51
|
+
{{ item.label }}
|
|
52
|
+
</li>
|
|
53
|
+
</ul>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="variable-content">
|
|
58
|
+
<p class="variable-title">变量</p>
|
|
59
|
+
<div class="code-control">
|
|
60
|
+
<v-ace-editor
|
|
61
|
+
v-model:value="content"
|
|
62
|
+
:options="{ showPrintMargin: false }"
|
|
63
|
+
theme="chrome"
|
|
64
|
+
lang="typescript"
|
|
65
|
+
style="height: 320px"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<template #footer>
|
|
71
|
+
<span class="dialog-footer">
|
|
72
|
+
<el-button @click="onCancel">取消</el-button>
|
|
73
|
+
<el-button type="primary" @click="onConfirm"> 确定 </el-button>
|
|
74
|
+
</span>
|
|
75
|
+
</template>
|
|
76
|
+
</el-dialog>
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<script lang="ts" setup>
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
import { PropType, computed, reactive, ref, watch, defineProps, defineEmits } from 'vue';
|
|
83
|
+
import '../../config/ace.config';
|
|
84
|
+
import { VAceEditor } from 'vue3-ace-editor';
|
|
85
|
+
import { VueForm } from '@jt/formily';
|
|
86
|
+
import { Search } from '@jt/icons-vue';
|
|
87
|
+
import { DataSource } from '@jt/low-code-core';
|
|
88
|
+
const props = defineProps({
|
|
89
|
+
schema: Object,
|
|
90
|
+
modelValue: String,
|
|
91
|
+
sourceList: {
|
|
92
|
+
type: Array as PropType<DataSource[]>,
|
|
93
|
+
default: () => [],
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const emits = defineEmits(['update:modelValue', 'getBindingValue']);
|
|
97
|
+
|
|
98
|
+
const dialogVisible = ref(false);
|
|
99
|
+
// 绑定的内容
|
|
100
|
+
const content = ref('');
|
|
101
|
+
// schema唯一表单项的key
|
|
102
|
+
const key = 'value';
|
|
103
|
+
// 表单数据
|
|
104
|
+
const formData = reactive<any>({});
|
|
105
|
+
// 搜索数据源名称关键字
|
|
106
|
+
const keyword = ref('');
|
|
107
|
+
// 展示的列表
|
|
108
|
+
const itemList = ref([]);
|
|
109
|
+
// 完整的列表
|
|
110
|
+
const stateList = computed(() => {
|
|
111
|
+
const defaultList = [
|
|
112
|
+
{
|
|
113
|
+
label: 'state.form',
|
|
114
|
+
value: '$form',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: 'state.urlParams',
|
|
118
|
+
value: '$state.urlParams',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
if (Array.isArray(props.sourceList)) {
|
|
122
|
+
return [
|
|
123
|
+
...defaultList,
|
|
124
|
+
...props.sourceList.map(v => ({
|
|
125
|
+
label: `state.${v.name}`,
|
|
126
|
+
value: `$state.${v.name}`,
|
|
127
|
+
})),
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
return defaultList;
|
|
131
|
+
});
|
|
132
|
+
// 表单schema配置
|
|
133
|
+
const config = computed(() => ({
|
|
134
|
+
[key]: {
|
|
135
|
+
...props.schema,
|
|
136
|
+
},
|
|
137
|
+
}));
|
|
138
|
+
watch(
|
|
139
|
+
() => props.modelValue,
|
|
140
|
+
val => {
|
|
141
|
+
content.value = val;
|
|
142
|
+
},
|
|
143
|
+
{ immediate: true }
|
|
144
|
+
);
|
|
145
|
+
watch(
|
|
146
|
+
stateList,
|
|
147
|
+
list => {
|
|
148
|
+
itemList.value = list;
|
|
149
|
+
},
|
|
150
|
+
{ immediate: true, deep: true }
|
|
151
|
+
);
|
|
152
|
+
/* 点击图标触发的事件 */
|
|
153
|
+
const onClickIcon = () => {
|
|
154
|
+
dialogVisible.value = true;
|
|
155
|
+
};
|
|
156
|
+
/* 点击对话框确定按钮 */
|
|
157
|
+
const onConfirm = () => {
|
|
158
|
+
dialogVisible.value = false;
|
|
159
|
+
// 抛出事件更新值
|
|
160
|
+
emits('update:modelValue', content.value);
|
|
161
|
+
// 抛出事件 由模拟器返回真实数据
|
|
162
|
+
emits('getBindingValue', value => {
|
|
163
|
+
// FIXME: formData变化 展示数据不变化
|
|
164
|
+
formData[key] = value;
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
/* 选中state */
|
|
168
|
+
const onItemClick = item => {
|
|
169
|
+
content.value = item.value;
|
|
170
|
+
};
|
|
171
|
+
/* 点击对话框取消按钮 */
|
|
172
|
+
const onCancel = () => {
|
|
173
|
+
dialogVisible.value = false;
|
|
174
|
+
};
|
|
175
|
+
/* 关键字输入框输入值变化 */
|
|
176
|
+
const onInputChange = val => {
|
|
177
|
+
itemList.value = stateList.value.filter(v => v.label.includes(val));
|
|
178
|
+
};
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<style lang="scss" scoped>
|
|
182
|
+
.data-binding {
|
|
183
|
+
position: relative;
|
|
184
|
+
.setter-actions {
|
|
185
|
+
position: absolute;
|
|
186
|
+
right: 0;
|
|
187
|
+
top: 0;
|
|
188
|
+
transform: none;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
.title {
|
|
193
|
+
background: #006cff;
|
|
194
|
+
color: #fff !important;
|
|
195
|
+
border-radius: 3px;
|
|
196
|
+
.title-icon {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
.only-icon {
|
|
202
|
+
background: #fff;
|
|
203
|
+
color: #8f9bb3;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
&:deep(.formily-element-plus-form-item-control-content-component) {
|
|
207
|
+
justify-content: flex-start;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
.variable-dialog-content {
|
|
211
|
+
display: flex;
|
|
212
|
+
box-sizing: border-box;
|
|
213
|
+
.variable-selector {
|
|
214
|
+
width: 270px;
|
|
215
|
+
flex-shrink: 0;
|
|
216
|
+
flex-grow: 0;
|
|
217
|
+
.variable-selector-inner {
|
|
218
|
+
display: flex;
|
|
219
|
+
border: 1px solid rgba(31, 56, 88, 0.3);
|
|
220
|
+
border-radius: 3px;
|
|
221
|
+
height: calc(100% - 28px);
|
|
222
|
+
.variable-selector-category {
|
|
223
|
+
width: 110px;
|
|
224
|
+
flex-shrink: 0;
|
|
225
|
+
flex-grow: 0;
|
|
226
|
+
border-right: 1px solid rgba(31, 56, 88, 0.2);
|
|
227
|
+
li {
|
|
228
|
+
height: 28px;
|
|
229
|
+
line-height: 28px;
|
|
230
|
+
padding: 0 12px;
|
|
231
|
+
cursor: pointer;
|
|
232
|
+
position: relative;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
text-overflow: ellipsis;
|
|
235
|
+
white-space: nowrap;
|
|
236
|
+
}
|
|
237
|
+
.active {
|
|
238
|
+
background: rgba(31, 56, 88, 0.06);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
.variable-selector-items-container {
|
|
242
|
+
width: 157px;
|
|
243
|
+
.selector-search-control {
|
|
244
|
+
margin: 10px;
|
|
245
|
+
height: 30px;
|
|
246
|
+
}
|
|
247
|
+
.variable-selector-items {
|
|
248
|
+
overflow-y: auto;
|
|
249
|
+
overflow-x: hidden;
|
|
250
|
+
height: 270px;
|
|
251
|
+
li {
|
|
252
|
+
height: 28px;
|
|
253
|
+
line-height: 28px;
|
|
254
|
+
padding: 0 12px;
|
|
255
|
+
cursor: pointer;
|
|
256
|
+
position: relative;
|
|
257
|
+
overflow: hidden;
|
|
258
|
+
text-overflow: ellipsis;
|
|
259
|
+
white-space: nowrap;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
ul {
|
|
264
|
+
margin: 0;
|
|
265
|
+
padding: 0;
|
|
266
|
+
list-style-type: none;
|
|
267
|
+
li:hover {
|
|
268
|
+
background: rgba(31, 56, 88, 0.04);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
.variable-content {
|
|
274
|
+
margin-left: 12px;
|
|
275
|
+
flex-grow: 1;
|
|
276
|
+
}
|
|
277
|
+
.variable-title {
|
|
278
|
+
font-weight: 700;
|
|
279
|
+
margin-bottom: 8px;
|
|
280
|
+
color: rgba(0, 0, 0, 0.8);
|
|
281
|
+
display: flex;
|
|
282
|
+
align-items: center;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { connect, mapProps } from '@formily/vue';
|
|
2
|
+
import { transformComponent } from '@jt/formily';
|
|
3
|
+
import JtDataSource from '../../dataSource/index.vue';
|
|
4
|
+
|
|
5
|
+
export type JtDataSourceProps = typeof JtDataSource;
|
|
6
|
+
|
|
7
|
+
const TransformJtDataSource = transformComponent<JtDataSourceProps>(
|
|
8
|
+
JtDataSource,
|
|
9
|
+
{
|
|
10
|
+
change: 'update:modelValue',
|
|
11
|
+
}
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export const ActivityBar = connect(
|
|
15
|
+
TransformJtDataSource,
|
|
16
|
+
mapProps({
|
|
17
|
+
value: 'modelValue',
|
|
18
|
+
readOnly: 'readonly',
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default ActivityBar;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="page-list">
|
|
3
|
+
<div class="input" style="background-color: #f7f7f7">
|
|
4
|
+
<el-input
|
|
5
|
+
placeholder="搜索..."
|
|
6
|
+
:prefix-icon="Search"
|
|
7
|
+
v-model="searchValue"
|
|
8
|
+
clearable
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="title" style="background-color: #eeeeee">
|
|
12
|
+
{{ project.projectName }}(项目名称)
|
|
13
|
+
</div>
|
|
14
|
+
<ul class="name-list">
|
|
15
|
+
<template :key="page.id" v-for="page in pages">
|
|
16
|
+
<li class="page-item" @click="bindPage(page)">
|
|
17
|
+
{{ page.title }}
|
|
18
|
+
</li>
|
|
19
|
+
</template>
|
|
20
|
+
</ul>
|
|
21
|
+
<div v-if="modelValue" class="show-area" style="background-color: #f7f7f7">
|
|
22
|
+
<span>绑定页面:{{ pageName }}</span>
|
|
23
|
+
<span class="remove-icon" @click="unbindPage">
|
|
24
|
+
<el-icon><CircleClose /></el-icon>
|
|
25
|
+
<span style="padding-left: 5px">移除</span>
|
|
26
|
+
</span>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script lang="ts" setup>
|
|
32
|
+
// @ts-nocheck
|
|
33
|
+
import {
|
|
34
|
+
computed,
|
|
35
|
+
ref,
|
|
36
|
+
watch,
|
|
37
|
+
withDefaults,
|
|
38
|
+
defineProps,
|
|
39
|
+
defineEmits,
|
|
40
|
+
} from 'vue';
|
|
41
|
+
import { Search, CircleClose } from '@jt/icons-vue';
|
|
42
|
+
|
|
43
|
+
const props = withDefaults(
|
|
44
|
+
defineProps<{
|
|
45
|
+
modelValue: number;
|
|
46
|
+
project: any;
|
|
47
|
+
}>(),
|
|
48
|
+
{
|
|
49
|
+
modelValue: 0,
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const emits = defineEmits(['update:modelValue']);
|
|
54
|
+
|
|
55
|
+
// 输入的搜索关键字
|
|
56
|
+
const searchValue = ref('');
|
|
57
|
+
|
|
58
|
+
// 当前绑定的页面名
|
|
59
|
+
const pageName = ref('');
|
|
60
|
+
|
|
61
|
+
// 当前展示的页面列表(不包括关键字过滤掉的)
|
|
62
|
+
const pages = computed(() =>
|
|
63
|
+
props.project.pages
|
|
64
|
+
? props.project.pages
|
|
65
|
+
.map(page => ({ title: page.title, id: page.id }))
|
|
66
|
+
.filter(p => p.title.includes(searchValue.value))
|
|
67
|
+
: []
|
|
68
|
+
);
|
|
69
|
+
watch(
|
|
70
|
+
() => props.modelValue,
|
|
71
|
+
id => {
|
|
72
|
+
pageName.value = pages.value.find(page => page.id === id)?.title ?? '';
|
|
73
|
+
},
|
|
74
|
+
{ immediate: true }
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
/* 点击列表项绑定页面或者覆盖上一个绑定的页面 */
|
|
78
|
+
const bindPage = page => {
|
|
79
|
+
emits('update:modelValue', page.id);
|
|
80
|
+
pageName.value = page.title;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/* 点击移除图标解绑页面 */
|
|
84
|
+
const unbindPage = () => {
|
|
85
|
+
emits('update:modelValue', 0);
|
|
86
|
+
pageName.value = '';
|
|
87
|
+
};
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<style lang="scss" scoped>
|
|
91
|
+
.page-list {
|
|
92
|
+
flex: 1;
|
|
93
|
+
margin-bottom: 18px;
|
|
94
|
+
border: 1px solid #6b7280;
|
|
95
|
+
border-radius: 5px;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
.input,
|
|
98
|
+
.title {
|
|
99
|
+
padding: 10px 15px 10px 15px;
|
|
100
|
+
}
|
|
101
|
+
.name-list {
|
|
102
|
+
list-style: none;
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
overflow: auto;
|
|
106
|
+
height: 150px;
|
|
107
|
+
li {
|
|
108
|
+
padding-left: 20px;
|
|
109
|
+
padding-right: 20px;
|
|
110
|
+
line-height: 30px;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
.show-area {
|
|
115
|
+
padding: 10px 15px 10px 15px;
|
|
116
|
+
display: flex;
|
|
117
|
+
justify-content: space-between;
|
|
118
|
+
.remove-icon {
|
|
119
|
+
display: flex;
|
|
120
|
+
justify-content: space-between;
|
|
121
|
+
align-items: center;
|
|
122
|
+
cursor: pointer;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
.page-item {
|
|
127
|
+
transition: all 0.3s ease-in-out;
|
|
128
|
+
}
|
|
129
|
+
.page-item:hover {
|
|
130
|
+
background-color: #f5f7fa;
|
|
131
|
+
}
|
|
132
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { connect, mapProps } from '@formily/vue';
|
|
2
|
+
import { transformComponent } from '@jt/formily';
|
|
3
|
+
import JtMenuTable from './index.vue';
|
|
4
|
+
|
|
5
|
+
export type JtMenuTableProps = typeof JtMenuTable;
|
|
6
|
+
|
|
7
|
+
const TransformJtMenuTable = transformComponent<JtMenuTableProps>(JtMenuTable, {
|
|
8
|
+
change: 'update:modelValue',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const MenuTable = connect(
|
|
12
|
+
TransformJtMenuTable,
|
|
13
|
+
mapProps({
|
|
14
|
+
value: 'modelValue',
|
|
15
|
+
readOnly: 'readonly',
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export default MenuTable;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-table
|
|
3
|
+
:data="menuTree"
|
|
4
|
+
style="width: 100%"
|
|
5
|
+
row-key="name"
|
|
6
|
+
default-expand-all
|
|
7
|
+
>
|
|
8
|
+
<el-table-column prop="name" label="名称" width="180" />
|
|
9
|
+
<el-table-column label="图标" width="100">
|
|
10
|
+
<template #default="scope">
|
|
11
|
+
<div v-if="!scope.row.icon">无</div>
|
|
12
|
+
<div v-else>
|
|
13
|
+
<el-icon>
|
|
14
|
+
<component :is="ElIcons[scope.row.icon]" />
|
|
15
|
+
</el-icon>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
</el-table-column>
|
|
19
|
+
<el-table-column label="导航类型" width="180">
|
|
20
|
+
<template #default="scope">
|
|
21
|
+
{{ scope.row.target === '_self' ? '本窗口打开' : '新窗口打开' }}
|
|
22
|
+
</template>
|
|
23
|
+
</el-table-column>
|
|
24
|
+
<el-table-column label="绑定界面" width="180" show-overflow-tooltip>
|
|
25
|
+
<template #default="scope">
|
|
26
|
+
<span v-if="scope.row.children.length">父级菜单</span>
|
|
27
|
+
<template v-else>
|
|
28
|
+
<span
|
|
29
|
+
v-if="scope.row.pageId && !scope.row.url"
|
|
30
|
+
class="binded-page"
|
|
31
|
+
:style="{ color: '#cc9c66' }"
|
|
32
|
+
>
|
|
33
|
+
<el-icon><Document /></el-icon>
|
|
34
|
+
<span>{{ pageNameMap[scope.row.pageId] }}</span>
|
|
35
|
+
</span>
|
|
36
|
+
<span v-else-if="scope.row.url" class="binded-page">
|
|
37
|
+
<el-icon><Link /></el-icon>
|
|
38
|
+
<el-link :href="scope.row.url" target="_blank">{{
|
|
39
|
+
scope.row.url
|
|
40
|
+
}}</el-link>
|
|
41
|
+
</span>
|
|
42
|
+
<span v-else :style="{ color: '#dd5547' }">未绑定</span>
|
|
43
|
+
</template>
|
|
44
|
+
</template>
|
|
45
|
+
</el-table-column>
|
|
46
|
+
<el-table-column label="操作" width="300" fixed="right">
|
|
47
|
+
<template #default="scope">
|
|
48
|
+
<el-button
|
|
49
|
+
size="small"
|
|
50
|
+
type="primary"
|
|
51
|
+
text
|
|
52
|
+
@click="addPeer(scope.$index, scope.row)"
|
|
53
|
+
>
|
|
54
|
+
添加同级
|
|
55
|
+
</el-button>
|
|
56
|
+
<el-button
|
|
57
|
+
:disabled="scope.row.pageId || scope.row.url"
|
|
58
|
+
size="small"
|
|
59
|
+
type="primary"
|
|
60
|
+
text
|
|
61
|
+
@click="addSubordinate(scope.$index, scope.row)"
|
|
62
|
+
>
|
|
63
|
+
添加下级
|
|
64
|
+
</el-button>
|
|
65
|
+
<el-button
|
|
66
|
+
size="small"
|
|
67
|
+
type="primary"
|
|
68
|
+
text
|
|
69
|
+
@click="handleEdit(scope.$index, scope.row)"
|
|
70
|
+
>
|
|
71
|
+
编辑
|
|
72
|
+
</el-button>
|
|
73
|
+
<el-button
|
|
74
|
+
size="small"
|
|
75
|
+
type="danger"
|
|
76
|
+
text
|
|
77
|
+
@click="handleDelete(scope.$index, scope.row)"
|
|
78
|
+
>
|
|
79
|
+
删除
|
|
80
|
+
</el-button>
|
|
81
|
+
</template>
|
|
82
|
+
</el-table-column>
|
|
83
|
+
</el-table>
|
|
84
|
+
<el-dialog
|
|
85
|
+
v-model="editModalOptions.visible"
|
|
86
|
+
:title="editModalOptions.title"
|
|
87
|
+
:width="editModalOptions.width"
|
|
88
|
+
append-to-body
|
|
89
|
+
>
|
|
90
|
+
<div class="menu-form">
|
|
91
|
+
<el-form :model="menuForm" ref="menuFormRef" label-width="100px">
|
|
92
|
+
<el-form-item
|
|
93
|
+
label="菜单名称:"
|
|
94
|
+
prop="name"
|
|
95
|
+
:rules="[{ required: true, message: '请填写菜单名称' }]"
|
|
96
|
+
>
|
|
97
|
+
<el-input
|
|
98
|
+
placeholder="请输入"
|
|
99
|
+
v-model="menuForm.name"
|
|
100
|
+
clearable
|
|
101
|
+
></el-input>
|
|
102
|
+
</el-form-item>
|
|
103
|
+
<el-form-item label="菜单图标:" prop="icon">
|
|
104
|
+
<el-select v-model="menuForm.icon" placeholder="无">
|
|
105
|
+
<el-option
|
|
106
|
+
style="
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
align-items: center;
|
|
110
|
+
"
|
|
111
|
+
v-for="(item, key) in ElIcons"
|
|
112
|
+
:key="key"
|
|
113
|
+
:value="key"
|
|
114
|
+
>
|
|
115
|
+
<span>{{ key }}</span>
|
|
116
|
+
<el-icon><component :is="item" /></el-icon>
|
|
117
|
+
</el-option>
|
|
118
|
+
</el-select>
|
|
119
|
+
</el-form-item>
|
|
120
|
+
<el-form-item label="菜单内容:" prop="content">
|
|
121
|
+
<el-radio-group v-model="menuForm.content">
|
|
122
|
+
<el-radio label="pageId" value="pageId"> 项目中的页面 </el-radio>
|
|
123
|
+
<el-radio label="url" value="url">URL</el-radio>
|
|
124
|
+
</el-radio-group>
|
|
125
|
+
</el-form-item>
|
|
126
|
+
<el-form-item
|
|
127
|
+
v-if="menuForm.content === 'pageId'"
|
|
128
|
+
label-width="0px"
|
|
129
|
+
prop="pageId"
|
|
130
|
+
>
|
|
131
|
+
<PageList v-model="menuForm.pageId" :project="project" />
|
|
132
|
+
</el-form-item>
|
|
133
|
+
<template v-else>
|
|
134
|
+
<el-form-item label="打开方式:" prop="target">
|
|
135
|
+
<el-radio-group v-model="menuForm.target">
|
|
136
|
+
<el-radio label="_blank" value="_blank"> 新窗口打开 </el-radio>
|
|
137
|
+
<el-radio label="_self" value="_self"> 本窗口打开 </el-radio>
|
|
138
|
+
</el-radio-group>
|
|
139
|
+
</el-form-item>
|
|
140
|
+
<el-form-item
|
|
141
|
+
label="URL:"
|
|
142
|
+
prop="url"
|
|
143
|
+
:rules="[
|
|
144
|
+
{
|
|
145
|
+
type: 'url',
|
|
146
|
+
required: true,
|
|
147
|
+
message: '请输入URL',
|
|
148
|
+
},
|
|
149
|
+
]"
|
|
150
|
+
>
|
|
151
|
+
<el-input
|
|
152
|
+
placeholder="请输入"
|
|
153
|
+
v-model="menuForm.url"
|
|
154
|
+
clearable
|
|
155
|
+
></el-input>
|
|
156
|
+
</el-form-item>
|
|
157
|
+
</template>
|
|
158
|
+
</el-form>
|
|
159
|
+
</div>
|
|
160
|
+
<template #footer>
|
|
161
|
+
<span class="dialog-footer">
|
|
162
|
+
<el-button @click="cancel"> 取消 </el-button>
|
|
163
|
+
<el-button type="primary" @click="confirm"> 确认 </el-button>
|
|
164
|
+
</span>
|
|
165
|
+
</template>
|
|
166
|
+
</el-dialog>
|
|
167
|
+
</template>
|
|
168
|
+
|
|
169
|
+
<script lang="ts" setup>
|
|
170
|
+
// @ts-nocheck
|
|
171
|
+
import { defineProps, defineEmits } from 'vue';
|
|
172
|
+
import * as ElIcons from '@jt/icons-vue';
|
|
173
|
+
import PageList from './PageList.vue';
|
|
174
|
+
import useMenuTable from './useMenuTable';
|
|
175
|
+
const props = defineProps({
|
|
176
|
+
modelValue: {
|
|
177
|
+
type: Array,
|
|
178
|
+
default: () => [
|
|
179
|
+
{
|
|
180
|
+
name: '默认菜单',
|
|
181
|
+
icon: '',
|
|
182
|
+
pageId: 1,
|
|
183
|
+
children: [],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
project: Object,
|
|
188
|
+
});
|
|
189
|
+
const emits = defineEmits(['update:modelValue']);
|
|
190
|
+
const {
|
|
191
|
+
editModalOptions,
|
|
192
|
+
menuForm,
|
|
193
|
+
menuFormRef,
|
|
194
|
+
menuTree,
|
|
195
|
+
pageNameMap,
|
|
196
|
+
confirm,
|
|
197
|
+
cancel,
|
|
198
|
+
addPeer,
|
|
199
|
+
addSubordinate,
|
|
200
|
+
handleEdit,
|
|
201
|
+
handleDelete,
|
|
202
|
+
} = useMenuTable(props, emits);
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<style lang="scss" scoped>
|
|
206
|
+
.binded-page {
|
|
207
|
+
display: 'flex';
|
|
208
|
+
align-items: 'center';
|
|
209
|
+
}
|
|
210
|
+
.menu-form {
|
|
211
|
+
margin-bottom: 17px;
|
|
212
|
+
padding-top: 44px;
|
|
213
|
+
padding-bottom: 26px;
|
|
214
|
+
border: 1px solid #e8e9eb;
|
|
215
|
+
border-left: none;
|
|
216
|
+
border-right: none;
|
|
217
|
+
}
|
|
218
|
+
</style>
|