@neatui/nuxt 0.1.0 → 1.0.1
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/BUILD.md +128 -0
- package/IMPORT_GUIDE.md +142 -0
- package/README.md +98 -1
- package/SSR_COMPATIBILITY.md +201 -0
- package/USAGE.md +291 -0
- package/nuxt.config.example.ts +37 -0
- package/package.json +34 -11
- package/src/components/basic/IDraggable.vue +87 -65
- package/src/components/basic/IFollowView.vue +32 -23
- package/src/components/basic/IRouterView.vue +38 -23
- package/src/components/basic/IScrollView.vue +11 -7
- package/src/components/basic/Icon.vue +17 -17
- package/src/components/basic/LayerView/Layer.vue +33 -106
- package/src/components/basic/follow.ts +133 -0
- package/src/components/display/Calendar.vue +14 -14
- package/src/components/display/CalendarReg.vue +14 -14
- package/src/components/display/Image.vue +8 -8
- package/src/components/display/PhotoEditor.vue +36 -36
- package/src/components/display/PhotoViewer.vue +8 -8
- package/src/components/display/Tree.vue +6 -6
- package/src/components/display/TreeView.vue +4 -4
- package/src/components/display/index.ts +2 -2
- package/src/components/form/Cascader.vue +19 -19
- package/src/components/form/Checkbox.vue +64 -0
- package/src/components/form/DatePicker.vue +6 -7
- package/src/components/form/DateRangePicker@v3.vue +4 -4
- package/src/components/form/DateRangeView@v3.vue +18 -19
- package/src/components/form/DateView.vue +14 -14
- package/src/components/form/DateView@v2.vue +14 -14
- package/src/components/form/DateView@v3.vue +331 -318
- package/src/components/form/ImgUpload.vue +7 -7
- package/src/components/form/Input@v3.vue +11 -11
- package/src/components/form/MoreSelect@v3.vue +87 -17
- package/src/components/form/MoreSelectList.vue +8 -8
- package/src/components/form/MoreSelectPanel@v3.vue +3 -3
- package/src/components/form/MoreSelectPicker.vue +7 -7
- package/src/components/form/MoreSelectTags.vue +8 -8
- package/src/components/form/PageMoreSelect.vue +14 -14
- package/src/components/form/PageSelect.vue +16 -16
- package/src/components/form/SearchMoreSelect.vue +12 -12
- package/src/components/form/SearchSelect@v3.vue +3 -3
- package/src/components/form/Select@v3.vue +229 -23
- package/src/components/form/SelectList.vue +8 -8
- package/src/components/form/SelectPicker.vue +6 -6
- package/src/components/form/SelectTags.vue +7 -7
- package/src/components/form/SelectTree/SelectTree@v1.vue +5 -5
- package/src/components/form/Switch.vue +38 -103
- package/src/components/form/TextArea.vue +18 -18
- package/src/components/form/Textarea@v2.vue +275 -0
- package/src/components/form/TimeView.vue +14 -14
- package/src/components/form/Upload.vue +806 -297
- package/src/components/form/date.ts +321 -0
- package/src/components/form/index.ts +7 -5
- package/src/components/form/number.ts +3 -0
- package/src/components/form/type.ts +224 -0
- package/src/components/icon/OrderIcon.vue +113 -0
- package/src/components/loader/FormLoader/FormLoader@v2.vue +193 -195
- package/src/components/loader/FormLoader/FormLoader@v3.vue.backup +372 -291
- package/src/components/loader/FormLoader/FormRender@v3.vue.backup +4 -0
- package/src/components/loader/FormLoader/NodeLoader.vue +85 -0
- package/src/components/loader/FormLoader@v1/FormLoader.vue +1 -1
- package/src/components/loader/FormLoader@v1/FormRender.vue +49 -24
- package/src/components/loader/LayerLoader/LayerLoader.vue +318 -0
- package/src/components/loader/LayerLoader/index.ts +2 -0
- package/src/components/loader/LayerLoader/style.scss +77 -0
- package/src/components/loader/LimitLoader/LimitLoader@v3.vue +39 -28
- package/src/components/loader/MoveLoader/MoveLoader.vue +628 -0
- package/src/components/loader/MoveLoader/index.ts +2 -0
- package/src/components/loader/MoveLoader/style.scss +77 -0
- package/src/components/loader/TableLoader/TableLoader.vue +227 -195
- package/src/components/loader/TableLoader/TableRender.vue +141 -0
- package/src/components/loader/TableLoader/index.ts +47 -0
- package/src/components/loader/index.ts +3 -2
- package/src/components/tools/Pagination@a.vue +17 -18
- package/src/components/tools/Pagination@b.vue +16 -16
- package/src/index.ts +2 -1
- package/src/module.ts +169 -0
- package/src/runtime/types.d.ts +36 -0
- package/src/store/{myui.ts → frame.ts} +4 -4
- package/src/utils/theme.ts +14 -0
- package/tsconfig.json +1 -1
- package/src/components/loader/FormLoader/index.ts +0 -2
- package/src/components/loader/LimitLoader/LimitLoader.vue.backup +0 -131
- package/src/components/loader/LimitLoader/LimitLoader@v2.vue.backup +0 -174
- package/src/components/loader/TableLoader/TableColView.vue +0 -115
@@ -0,0 +1,85 @@
|
|
1
|
+
<template>
|
2
|
+
<template v-for="(item, index) in $nodes" :key="item.paths">
|
3
|
+
<div class="fekit-node-loader" :data-level="level" :data-index="index" :data-paths="item.paths" v-bind="item.attrs" v-on="item.$events" v-html="item.value"></div>
|
4
|
+
<NodeLoader v-if="item.child" :nodes="item.child" :level="$level" :index="index" :paths="item.paths" />
|
5
|
+
</template>
|
6
|
+
</template>
|
7
|
+
<script setup lang="ts">
|
8
|
+
import { computed } from 'vue';
|
9
|
+
import NodeLoader from './NodeLoader.vue';
|
10
|
+
|
11
|
+
interface NodeProps {
|
12
|
+
label?: string | ((data: any) => string);
|
13
|
+
field?: string | ((data: any) => string);
|
14
|
+
model?: string | ((data: any) => string);
|
15
|
+
attrs?: Record<string, any> | ((data: any) => Record<string, any>);
|
16
|
+
clear?: boolean | ((data: any) => boolean);
|
17
|
+
media?: string | ((data: any) => string);
|
18
|
+
value?: any | ((data: any) => any);
|
19
|
+
event?: Record<string, any> | ((data: any) => Record<string, any>);
|
20
|
+
child?: NodeProps[];
|
21
|
+
}
|
22
|
+
|
23
|
+
interface Props {
|
24
|
+
nodes: NodeProps[];
|
25
|
+
level?: number;
|
26
|
+
index?: number;
|
27
|
+
paths?: string;
|
28
|
+
}
|
29
|
+
const props = withDefaults(defineProps<Props>(), {
|
30
|
+
nodes: () => [],
|
31
|
+
level: 0,
|
32
|
+
index: 0,
|
33
|
+
paths: '',
|
34
|
+
});
|
35
|
+
|
36
|
+
const $nodes = computed(() => {
|
37
|
+
return props.nodes.map((item, index) => {
|
38
|
+
const paths = props.paths ? `${props.paths}-${index}` : `${index}`;
|
39
|
+
const context = { node: item, level: props.level, index, paths } as const;
|
40
|
+
|
41
|
+
let attrs: any = item.attrs;
|
42
|
+
if (typeof attrs === 'function') {
|
43
|
+
try {
|
44
|
+
attrs = attrs(context);
|
45
|
+
} catch {
|
46
|
+
/* noop */
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
let value: any = (item as any).value;
|
51
|
+
if (typeof value === 'function') {
|
52
|
+
try {
|
53
|
+
value = value(context);
|
54
|
+
} catch {
|
55
|
+
/* noop */
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
let ev: any = (item as any).event;
|
60
|
+
if (typeof ev === 'function') {
|
61
|
+
try {
|
62
|
+
ev = ev(context);
|
63
|
+
} catch {
|
64
|
+
ev = undefined;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
const $events: Record<string, any> = {};
|
69
|
+
if (ev && typeof ev === 'object') {
|
70
|
+
for (const key in ev) {
|
71
|
+
const fn = ev[key];
|
72
|
+
if (typeof fn === 'function') {
|
73
|
+
$events[key] = (...args: any[]) => fn(context, ...args);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return { ...item, index, paths, attrs, value, $events } as any;
|
79
|
+
});
|
80
|
+
});
|
81
|
+
|
82
|
+
const $level = computed(() => {
|
83
|
+
return props.level + 1;
|
84
|
+
});
|
85
|
+
</script>
|
@@ -170,7 +170,7 @@
|
|
170
170
|
<div v-for="(item, idx) in node.other.suffix" v-html="item.value" v-bind="item.attrs" :key="idx"></div>
|
171
171
|
</div>
|
172
172
|
</div>
|
173
|
-
<div v-if="$slots.submit" class="full
|
173
|
+
<div v-if="$slots.submit" class="w-full" ui-flex="row lm">
|
174
174
|
<div class="flex-fixed" :style="labelcss(node)"> </div>
|
175
175
|
<div class="flex-block"><slot name="submit"></slot></div>
|
176
176
|
</div>
|
@@ -2,12 +2,19 @@
|
|
2
2
|
<div class="flex-block pr">
|
3
3
|
<template v-if="mode === 'view'">
|
4
4
|
<slot :name="`${node.field}-value`" :data="data" :node="node" :bind.sync="bind">
|
5
|
-
<div class="nt-ss"><div v-html="value" style="padding-top: 2px
|
5
|
+
<div class="nt-ss"><div v-html="value" style="padding-top: 2px"></div></div>
|
6
6
|
</slot>
|
7
7
|
</template>
|
8
8
|
<template v-else>
|
9
|
-
<el-input v-if="node.model[0] === 'Input'" v-model="value" class="full
|
10
|
-
<el-input
|
9
|
+
<el-input v-if="node.model[0] === 'Input'" v-model="value" class="w-full" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted" />
|
10
|
+
<el-input
|
11
|
+
v-if="node.model[0] === 'Textarea'"
|
12
|
+
v-model="value"
|
13
|
+
class="w-full"
|
14
|
+
v-bind="{ ...(node.model[1] || {}, { type: 'textarea', rows: 3 }) }"
|
15
|
+
@input="setInteracted"
|
16
|
+
@blur="setInteracted"
|
17
|
+
/>
|
11
18
|
<el-select v-else-if="node.model[0] === 'Select'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted">
|
12
19
|
<el-option v-for="item in node.enums" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
13
20
|
</el-select>
|
@@ -19,17 +26,35 @@
|
|
19
26
|
</el-checkbox-group>
|
20
27
|
<el-checkbox v-else-if="node.model[0] === 'Checkbox'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted"> </el-checkbox>
|
21
28
|
<el-switch v-else-if="node.model[0] === 'Switch'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted"> </el-switch>
|
22
|
-
<el-time-select v-else-if="node.model[0] === 'TimeSelect'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted">
|
23
|
-
|
24
|
-
<el-
|
25
|
-
|
26
|
-
<el-
|
29
|
+
<el-time-select v-else-if="node.model[0] === 'TimeSelect'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted">
|
30
|
+
</el-time-select>
|
31
|
+
<el-time-picker v-else-if="node.model[0] === 'TimePicker'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted">
|
32
|
+
</el-time-picker>
|
33
|
+
<el-date-picker v-else-if="node.model[0] === 'DatePicker'" v-model="value" v-bind="{ ...(node.model[1] || {}) }" @input="setInteracted" @blur="setInteracted">
|
34
|
+
</el-date-picker>
|
35
|
+
<el-date-picker
|
36
|
+
v-else-if="node.model[0] === 'DateTimePicker'"
|
37
|
+
v-model="value"
|
38
|
+
type="datetime"
|
39
|
+
v-bind="{ ...(node.model[1] || {}) }"
|
40
|
+
@input="setInteracted"
|
41
|
+
@blur="setInteracted"
|
42
|
+
>
|
43
|
+
</el-date-picker>
|
44
|
+
<el-cascader
|
45
|
+
v-else-if="node.model[0] === 'Cascader'"
|
46
|
+
v-model="value"
|
47
|
+
:options="node.enums"
|
48
|
+
v-bind="{ ...node.model[1] }"
|
49
|
+
@input="setInteracted"
|
50
|
+
@blur="setInteracted"
|
51
|
+
></el-cascader>
|
27
52
|
<gallery-value v-else-if="node.model[0] === 'SelectImg'" :form="data" :node="node" v-bind="{ ...node.model[1] }"></gallery-value>
|
28
53
|
<!-- 自定义组件插槽 -->
|
29
54
|
<slot v-else-if="node.model[0]" :name="node.model[0]" :data="data" :node="node" :bind.sync="bind"></slot>
|
30
55
|
<!-- 自定义字段插槽 -->
|
31
56
|
<slot v-else :name="`${node.field}-value`" :data="data" :node="node" :bind.sync="bind">
|
32
|
-
<div class="nt-ss"><div v-html="value" style="padding-top: 2px
|
57
|
+
<div class="nt-ss"><div v-html="value" style="padding-top: 2px"></div></div>
|
33
58
|
</slot>
|
34
59
|
<p v-show="error.verify === 'no'" class="form-loader-verify" :data-verify-type="error.co">{{ error.tips }}</p>
|
35
60
|
</template>
|
@@ -44,28 +69,28 @@
|
|
44
69
|
props: {
|
45
70
|
mode: {
|
46
71
|
type: String,
|
47
|
-
default: 'edit'
|
72
|
+
default: 'edit',
|
48
73
|
},
|
49
74
|
node: {
|
50
75
|
type: Object,
|
51
|
-
default: () => ({})
|
76
|
+
default: () => ({}),
|
52
77
|
},
|
53
78
|
data: {
|
54
79
|
type: Object,
|
55
|
-
default: () => ({})
|
80
|
+
default: () => ({}),
|
56
81
|
},
|
57
82
|
interacted: {
|
58
83
|
type: Boolean,
|
59
|
-
default: false
|
60
|
-
}
|
84
|
+
default: false,
|
85
|
+
},
|
61
86
|
},
|
62
87
|
data() {
|
63
88
|
return {
|
64
89
|
_interacted: false,
|
65
90
|
bind: {
|
66
|
-
value: null
|
91
|
+
value: null,
|
67
92
|
},
|
68
|
-
error: {}
|
93
|
+
error: {},
|
69
94
|
};
|
70
95
|
},
|
71
96
|
created() {},
|
@@ -99,8 +124,8 @@
|
|
99
124
|
this.$set(this.data, this.node.field, value);
|
100
125
|
}
|
101
126
|
this.validate();
|
102
|
-
}
|
103
|
-
}
|
127
|
+
},
|
128
|
+
},
|
104
129
|
},
|
105
130
|
methods: {
|
106
131
|
async validate() {
|
@@ -166,7 +191,7 @@
|
|
166
191
|
this.validate();
|
167
192
|
}, 50);
|
168
193
|
}
|
169
|
-
}
|
194
|
+
},
|
170
195
|
},
|
171
196
|
watch: {
|
172
197
|
bind: {
|
@@ -183,22 +208,22 @@
|
|
183
208
|
this.$set(this.data, this.node.field, value);
|
184
209
|
this.$set(this.bind, 'value', value);
|
185
210
|
},
|
186
|
-
deep: true
|
211
|
+
deep: true,
|
187
212
|
},
|
188
213
|
interacted: {
|
189
214
|
handler(val) {
|
190
215
|
this._interacted = val;
|
191
216
|
this.validate();
|
192
217
|
},
|
193
|
-
deep: true
|
218
|
+
deep: true,
|
194
219
|
},
|
195
220
|
value: {
|
196
221
|
handler() {
|
197
222
|
this.validate();
|
198
223
|
},
|
199
|
-
deep: true
|
200
|
-
}
|
201
|
-
}
|
224
|
+
deep: true,
|
225
|
+
},
|
226
|
+
},
|
202
227
|
};
|
203
228
|
</script>
|
204
229
|
<style>
|
@@ -0,0 +1,318 @@
|
|
1
|
+
<template>
|
2
|
+
<div id="layer-loader-root" :path="path" :class="`${!path ? '' : 'pr nl-ms'}`" :ui-lines="path ? line : null">
|
3
|
+
<template v-for="(item, idx) in data" :key="idx">
|
4
|
+
<div
|
5
|
+
ui-flex="row xm"
|
6
|
+
class="fekit-layer-loader-item ux-hover nx-ss r-ss pr z1"
|
7
|
+
:data-move="item._move"
|
8
|
+
:class="`${store.active === paths(idx) ? (store.error ? 'bg-risk+o-ss co-risk' : 'bg-main+o-ss co-main fekit-layer-node-active') : ''}${item?.attrs?.hide ? ' o-mm' : ''}`"
|
9
|
+
@mousedown="fMoveSta($event, data, idx, paths(idx))"
|
10
|
+
@mousemove="fMoveIng($event, item, paths(idx), false, item.child?.length && !item.__folding)"
|
11
|
+
@mouseout="fMoveOut($event, paths(idx))"
|
12
|
+
@mouseup="fMoveEnd($event, data, item, idx, paths(idx))"
|
13
|
+
>
|
14
|
+
<div class="hover:co-main" :ui-arrow="`${item.child ? '1' : '0'}`" :ui-folding="item.__folding" @click="item.__folding = item.child && !item.__folding"></div>
|
15
|
+
<div :id="`LID${path.replace(/\./g, '')}-${idx}`" :path="paths(idx)" ui-flex="row lt" class="flex-block ny-ss" @click="store.active = paths(idx)">
|
16
|
+
<slot name="prefix" :item="item" :path="paths(idx)">
|
17
|
+
<i :class="`ml-ss mr-xs o-ms icon icon-model-${!path ? 'label' : item.model || (item.child ? 'area' : 'null')}`"></i>
|
18
|
+
</slot>
|
19
|
+
<slot name="item" :item="item" :path="paths(idx)">
|
20
|
+
<span class="ux-none" ui-omit="1">
|
21
|
+
{{ (item.model === 'text' || (!item.model && !item.child) ? item.value : null) || item.label || word(label(item.model)) || '‐‐' }}{{ item.__folding ? ' ⋯' : '' }}
|
22
|
+
</span>
|
23
|
+
</slot>
|
24
|
+
<slot name="suffix" :item="item" :path="paths(idx)"></slot>
|
25
|
+
</div>
|
26
|
+
<slot name="tools" :data="data" :item="item" :path="paths(idx)">
|
27
|
+
<div ui-flex="row rm" class="layer-loader-tool parent-hover:show fs-xs" :data-model="item.model">
|
28
|
+
<div ui-tips="@a co:well ux:hover">
|
29
|
+
<button ui-btn="@a none s :square">
|
30
|
+
<i class="icon icon-add"></i>
|
31
|
+
</button>
|
32
|
+
<div ui-tips-box="bl">
|
33
|
+
<div class="n-sm nowrap fs-ss">
|
34
|
+
<ul class="nx-sm-sub ny-ss-sub">
|
35
|
+
<li class="r-sm ux-hover" @click="fAddArea(item, data, path, idx)">用容器包裹</li>
|
36
|
+
<li class="b-solid bk-line bb-xs n-no my-ss" v-if="isBoxNode(item.model)"></li>
|
37
|
+
<li class="r-sm ux-hover" v-if="isBoxNode(item.model)" @click="fAddNode(item, data, path, idx)">添加到内部</li>
|
38
|
+
<li class="b-solid bk-line bb-xs n-no my-ss"></li>
|
39
|
+
<li class="r-sm ux-hover" @click="fAddNode(item, data, path, idx, 'prev')">添加到前面</li>
|
40
|
+
<li class="r-sm ux-hover" @click="fAddNode(item, data, path, idx, 'next')">添加到后面</li>
|
41
|
+
<li class="b-solid bk-line bb-xs n-no my-ss"></li>
|
42
|
+
<li class="r-sm ux-hover" @click="fAddNode(item, data, path, idx, 'prev', 1)">复制到前面</li>
|
43
|
+
<li class="r-sm ux-hover" @click="fAddNode(item, data, path, idx, 'next', 1)">复制到后面</li>
|
44
|
+
</ul>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
<button ui-btn="@a none s :square" @click="fDelNode(item, data, idx)"><i class="icon icon-delete"></i></button>
|
49
|
+
</div>
|
50
|
+
<button ui-btn="@a none s :square" class="layer-loader-tool fs-xs" :class="item?.attrs?.hide ? '' : 'parent-hover:show'" @click="fhideNode(item)">
|
51
|
+
<i class="icon" :class="item?.attrs?.hide ? 'icon-hide' : 'icon-view'"></i>
|
52
|
+
</button>
|
53
|
+
</slot>
|
54
|
+
</div>
|
55
|
+
<LayerLoader v-if="item.child && !item.__folding" :data="item.child" :path="paths(idx)" :class="`${item?.attrs?.hide ? ' o-mm' : ''}`">
|
56
|
+
<template #prefix="{ item = {}, path = '' }: any = {}"><slot name="prefix" :item="item" :path="path"></slot></template>
|
57
|
+
<template #item="{ item = {}, path = '' }: any = {}"><slot name="item" :item="item" :path="path"></slot></template>
|
58
|
+
<template #suffix="{ item = {}, path = '' }: any = {}"><slot name="suffix" :item="item" :path="path"></slot></template>
|
59
|
+
<template #tools="{ item = {}, path = '' }: any = {}"><slot name="tools" :item="item" :path="path"></slot></template>
|
60
|
+
</LayerLoader>
|
61
|
+
<div
|
62
|
+
v-if="item.child?.length && !item.__folding"
|
63
|
+
ui-flex="row lm"
|
64
|
+
class="pr z1"
|
65
|
+
style="height: 0.5em"
|
66
|
+
:data-label="(item.model === 'text' || (!item.model && !item.child) ? item.value : null) || item.label || item.model || '‐‐'"
|
67
|
+
:data-move="item._move"
|
68
|
+
@mousemove="fMoveIng($event, item, paths(idx), true)"
|
69
|
+
@mouseout="fMoveOut($event, paths(idx))"
|
70
|
+
@mouseup="fMoveEnd($event, data, item, idx, paths(idx))"
|
71
|
+
></div>
|
72
|
+
</template>
|
73
|
+
</div>
|
74
|
+
</template>
|
75
|
+
<script setup lang="ts">
|
76
|
+
import { nextTick, reactive, ref, watch, onMounted } from 'vue';
|
77
|
+
import LayerLoader from './LayerLoader.vue';
|
78
|
+
import { useBasicStore, useWorksStore, storeToRefs } from '@/stores';
|
79
|
+
import { deepcopy } from '@fekit/utils';
|
80
|
+
import scrollto from '@fekit/scrollto';
|
81
|
+
|
82
|
+
// 事件定义
|
83
|
+
const store: any = useWorksStore();
|
84
|
+
const base: any = useBasicStore();
|
85
|
+
const { word } = base;
|
86
|
+
|
87
|
+
// 类型定义
|
88
|
+
interface Props {
|
89
|
+
data?: Array<any>;
|
90
|
+
path?: string;
|
91
|
+
line?: boolean;
|
92
|
+
}
|
93
|
+
// 外部入参
|
94
|
+
const props: any = withDefaults(defineProps<Props>(), {
|
95
|
+
data: () => [],
|
96
|
+
path: '',
|
97
|
+
line: true,
|
98
|
+
});
|
99
|
+
|
100
|
+
// 内部状态
|
101
|
+
const state: any = reactive({});
|
102
|
+
const tools: any = ref(null);
|
103
|
+
|
104
|
+
const ph = ref(0);
|
105
|
+
onMounted(() => {
|
106
|
+
const side = document.getElementById('side-layerloader');
|
107
|
+
ph.value = side?.clientHeight || 0;
|
108
|
+
});
|
109
|
+
|
110
|
+
// 计算路径
|
111
|
+
const paths = (idx: number) => {
|
112
|
+
return props.path ? `${props.path}-${idx}` : `${idx}`;
|
113
|
+
};
|
114
|
+
|
115
|
+
function unfoldActivePath(data: any, activePath: any) {
|
116
|
+
if (!activePath) return;
|
117
|
+
const indices = activePath.split('-').map(Number);
|
118
|
+
let cur = data;
|
119
|
+
for (let i = 0; i < indices.length; i++) {
|
120
|
+
const idx = indices[i];
|
121
|
+
if (!cur || !cur[idx]) break;
|
122
|
+
cur[idx].__folding = false;
|
123
|
+
cur = cur[idx].child;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
if (!props.path) {
|
128
|
+
watch(
|
129
|
+
() => store.active,
|
130
|
+
(newActive) => {
|
131
|
+
unfoldActivePath(props.data, newActive);
|
132
|
+
|
133
|
+
nextTick(() => {
|
134
|
+
const el = document.getElementById(`LID${newActive.replace(/\./g, '')}`);
|
135
|
+
if (el && !store.isMouseInLayer) {
|
136
|
+
scrollto({
|
137
|
+
el: '#side-layerloader',
|
138
|
+
to: {
|
139
|
+
y: el,
|
140
|
+
},
|
141
|
+
offset: {
|
142
|
+
y: -(ph.value / 2.5),
|
143
|
+
},
|
144
|
+
time: 600,
|
145
|
+
});
|
146
|
+
}
|
147
|
+
});
|
148
|
+
},
|
149
|
+
);
|
150
|
+
}
|
151
|
+
|
152
|
+
// 查找枚举
|
153
|
+
const label = (item: any) => {
|
154
|
+
return base.enums?.sys_models?.find((e: any) => e.value === item)?.label || item;
|
155
|
+
};
|
156
|
+
|
157
|
+
// 是容器节点
|
158
|
+
const isBoxNode = (model: string) => {
|
159
|
+
return !['data', 'text', 'icon', 'code', 'draw'].includes(model);
|
160
|
+
};
|
161
|
+
|
162
|
+
// 添加节点
|
163
|
+
const fAddNode = async (item: any = {}, _data: any = [], _path: any = '', _idx: number = 0, pos: any = null, copy: any = 0) => {
|
164
|
+
if (pos) {
|
165
|
+
if (pos === 'prev') {
|
166
|
+
// 加到前面
|
167
|
+
_data.splice(
|
168
|
+
_idx,
|
169
|
+
0,
|
170
|
+
copy
|
171
|
+
? deepcopy(item)
|
172
|
+
: {
|
173
|
+
label: '',
|
174
|
+
model: '',
|
175
|
+
value: '',
|
176
|
+
attrs: {},
|
177
|
+
},
|
178
|
+
);
|
179
|
+
store.active = `${_path}-${_idx}`;
|
180
|
+
} else if (pos === 'next') {
|
181
|
+
// 加到后面
|
182
|
+
_data.splice(
|
183
|
+
_idx + 1,
|
184
|
+
0,
|
185
|
+
copy
|
186
|
+
? deepcopy(item)
|
187
|
+
: {
|
188
|
+
label: '',
|
189
|
+
model: '',
|
190
|
+
value: '',
|
191
|
+
attrs: {},
|
192
|
+
},
|
193
|
+
);
|
194
|
+
store.active = `${_path}-${_idx + 1}`;
|
195
|
+
}
|
196
|
+
} else {
|
197
|
+
if (!item.child) {
|
198
|
+
item.child = [];
|
199
|
+
}
|
200
|
+
await item.child.push({
|
201
|
+
label: '',
|
202
|
+
model: '',
|
203
|
+
value: '',
|
204
|
+
attrs: {},
|
205
|
+
});
|
206
|
+
store.active = `${_path}-${_idx}-${item.child.length - 1}`;
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
// 包裹容器
|
211
|
+
const fAddArea = (item: any = {}, data: any = [], path: any = '', idx: number = 0, pos: any = null, copy: any = 0) => {
|
212
|
+
// 1. 构造新的 area 节点
|
213
|
+
const areaNode = {
|
214
|
+
model: 'area',
|
215
|
+
attrs: {},
|
216
|
+
child: [item],
|
217
|
+
param: {},
|
218
|
+
};
|
219
|
+
// 2. 替换 data 中的当前节点
|
220
|
+
data.splice(idx, 1, areaNode);
|
221
|
+
// 3. 设置 active 到新节点
|
222
|
+
store.active = path ? `${path}-${idx}` : `${idx}`;
|
223
|
+
};
|
224
|
+
|
225
|
+
// 删除节点
|
226
|
+
const fDelNode = (_item: any = {}, data: any = [], idx: number = 0) => {
|
227
|
+
if (data.length > 0) {
|
228
|
+
data.splice(idx, 1);
|
229
|
+
}
|
230
|
+
};
|
231
|
+
const fhideNode = (_item: any = {}) => {
|
232
|
+
_item.attrs.hide = _item.attrs.hide ? 0 : 1;
|
233
|
+
};
|
234
|
+
|
235
|
+
// 是否同祖
|
236
|
+
function inform(from: string, data: string) {
|
237
|
+
const _form = from.split('.');
|
238
|
+
const _data = data.split('.');
|
239
|
+
return _form.every((part, index) => part === _data[index]);
|
240
|
+
}
|
241
|
+
|
242
|
+
// 按住
|
243
|
+
const fMoveSta = (ev: any, data: any, idx: number, paths: any) => {
|
244
|
+
if (paths.length > 1) {
|
245
|
+
store.layer.moveing = true;
|
246
|
+
store.layer.from = { data, idx, dom: ev?.currentTarget, y: ev.clientY, paths };
|
247
|
+
}
|
248
|
+
};
|
249
|
+
const fMoveIng = (ev: any, item: any, paths: any, isend: boolean = false, ischildandnofold: boolean = false) => {
|
250
|
+
if (!store.layer.moveing || paths.length <= 1) return;
|
251
|
+
const dom = ev?.currentTarget;
|
252
|
+
if (!dom || dom === store.layer.from.dom) return;
|
253
|
+
const rect = dom.getBoundingClientRect();
|
254
|
+
const thirdHeight = rect.height / 3 || 1;
|
255
|
+
const offsetY = ev?.layerY ?? ev?.offsetY ?? 0;
|
256
|
+
let mode = 'prev';
|
257
|
+
if (offsetY > thirdHeight * 2 || isend) {
|
258
|
+
mode = ischildandnofold ? 'inset' : 'next';
|
259
|
+
} else if (offsetY > thirdHeight && offsetY < thirdHeight * 2) {
|
260
|
+
if (isBoxNode(item.model)) {
|
261
|
+
mode = 'inset';
|
262
|
+
}
|
263
|
+
}
|
264
|
+
store.layer.correct = mode;
|
265
|
+
dom.setAttribute('data-ins', mode);
|
266
|
+
};
|
267
|
+
// 移出
|
268
|
+
const fMoveOut = (ev: any, paths: any) => {
|
269
|
+
if (store.layer.moveing && paths.length > 1) {
|
270
|
+
ev?.currentTarget?.removeAttribute('data-ins');
|
271
|
+
}
|
272
|
+
};
|
273
|
+
// 松开
|
274
|
+
const fMoveEnd = (ev: any, data: any, item: any, idx: number, paths: any) => {
|
275
|
+
console.log(207, data, item);
|
276
|
+
if (store.layer.moveing && paths.length > 1 && !inform(store.layer.from.paths, paths)) {
|
277
|
+
const from = store.layer.from;
|
278
|
+
if (from.data && from.dom !== ev?.currentTarget) {
|
279
|
+
const isAdjacent =
|
280
|
+
(store.layer.correct === 'prev' && from.dom?.nextElementSibling === ev?.currentTarget) ||
|
281
|
+
(store.layer.correct === 'next' && from.dom?.previousElementSibling === ev?.currentTarget) ||
|
282
|
+
(store.layer.correct === 'inset' && (from.dom?.contains(ev?.currentTarget) || ev?.currentTarget?.contains(from.dom)));
|
283
|
+
|
284
|
+
if (!isAdjacent) {
|
285
|
+
const [node] = from.data.splice(from.idx, 1);
|
286
|
+
let newPath = paths;
|
287
|
+
|
288
|
+
// Adjust target index if it's after the removed item in the same array
|
289
|
+
let targetIdx = idx;
|
290
|
+
if (from.data === data && from.idx < idx) {
|
291
|
+
targetIdx -= 1;
|
292
|
+
}
|
293
|
+
|
294
|
+
if (store.layer.correct === 'prev') {
|
295
|
+
data.splice(targetIdx, 0, node);
|
296
|
+
newPath = props.path ? `${props.path}-${targetIdx}` : `${targetIdx}`;
|
297
|
+
} else if (store.layer.correct === 'next') {
|
298
|
+
const insertIndex = targetIdx + 1;
|
299
|
+
data.splice(insertIndex, 0, node);
|
300
|
+
newPath = props.path ? `${props.path}-${insertIndex}` : `${insertIndex}`;
|
301
|
+
} else if (store.layer.correct === 'inset') {
|
302
|
+
if (!item.child) item.child = [];
|
303
|
+
const targetIndex = item.child.length;
|
304
|
+
item.child.push(node);
|
305
|
+
newPath = paths + '-' + targetIndex;
|
306
|
+
}
|
307
|
+
|
308
|
+
requestAnimationFrame(() => {
|
309
|
+
store.active = newPath;
|
310
|
+
});
|
311
|
+
}
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
ev?.currentTarget?.removeAttribute('data-ins');
|
316
|
+
store.layer.moveing = false;
|
317
|
+
};
|
318
|
+
</script>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
.fekit-layer-loader-item {
|
2
|
+
user-select: none;
|
3
|
+
line-height: 1.2;
|
4
|
+
}
|
5
|
+
|
6
|
+
// 箭头
|
7
|
+
[ui-arrow] {
|
8
|
+
position: relative;
|
9
|
+
width: 1em;
|
10
|
+
height: 1em;
|
11
|
+
left: 0.1em;
|
12
|
+
|
13
|
+
// &[ui-arrow~='0'] {
|
14
|
+
// &::before {
|
15
|
+
// position: absolute;
|
16
|
+
// display: inline-block;
|
17
|
+
// content: '';
|
18
|
+
// width: 0.5em;
|
19
|
+
// height: 0.5em;
|
20
|
+
// border: 1px solid currentColor;
|
21
|
+
// top: 0.25em;
|
22
|
+
// left: 0.3em;
|
23
|
+
// border-radius: 50%;
|
24
|
+
// }
|
25
|
+
// }
|
26
|
+
&[ui-arrow~='0'] {
|
27
|
+
height: 1px;
|
28
|
+
border-bottom: 1px dotted var(--co-case);
|
29
|
+
position: relative;
|
30
|
+
left: -5px;
|
31
|
+
}
|
32
|
+
|
33
|
+
&[ui-arrow~='1'] {
|
34
|
+
&::before {
|
35
|
+
position: absolute;
|
36
|
+
display: inline-block;
|
37
|
+
content: '';
|
38
|
+
width: 0;
|
39
|
+
height: 0;
|
40
|
+
border-width: 0.35em 0 0.35em 0.35em;
|
41
|
+
border-style: solid;
|
42
|
+
border-color: transparent;
|
43
|
+
border-left-color: transparent;
|
44
|
+
border-left-color: currentColor;
|
45
|
+
top: 0.2em;
|
46
|
+
left: 0.35em;
|
47
|
+
transform: rotate(90deg);
|
48
|
+
border-radius: 25%;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
&[ui-folding~='true']::before {
|
53
|
+
transform: rotate(0deg);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
// 线条
|
58
|
+
[ui-lines] {
|
59
|
+
position: relative;
|
60
|
+
&::before {
|
61
|
+
position: absolute;
|
62
|
+
content: '';
|
63
|
+
display: block;
|
64
|
+
width: 5px;
|
65
|
+
height: 100%;
|
66
|
+
left: 0.85em;
|
67
|
+
|
68
|
+
border-left: 1px dotted var(--co-case);
|
69
|
+
border-bottom: 1px dotted var(--co-case);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
.fekit-layer-node-active {
|
74
|
+
.layer-loader-tool {
|
75
|
+
opacity: 1;
|
76
|
+
}
|
77
|
+
}
|