vue-2024-ui 0.0.8 → 0.0.10
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.form.md +2 -2
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/components/g-ctrl.vue +19 -11
- package/src/components/g-form.vue +22 -12
- package/src/components/g-query-item.vue +31 -0
- package/src/components/g-query.vue +217 -0
- package/src/components/g-table.vue +138 -44
- package/src/components/index.d.ts +77 -21
- package/src/components/index.js +72 -17
package/README.form.md
CHANGED
package/README.md
CHANGED
|
@@ -36,8 +36,8 @@ app.mount('#app')
|
|
|
36
36
|
|
|
37
37
|
``` javascript
|
|
38
38
|
<script setup>
|
|
39
|
-
import {
|
|
40
|
-
const formData =
|
|
39
|
+
import { ref } from "vue";
|
|
40
|
+
const formData = ref({
|
|
41
41
|
id: { label: "ID", type: "number", value: 1, disabled: true, edit: false },
|
|
42
42
|
name: {
|
|
43
43
|
label: "姓名", value: "张三", width: 100,
|
|
@@ -215,7 +215,7 @@ const edit = (row, c, i, m) => {
|
|
|
215
215
|
}
|
|
216
216
|
const read = (row, c, i, m) => {
|
|
217
217
|
}
|
|
218
|
-
formData.table = {
|
|
218
|
+
formData.value.table = {
|
|
219
219
|
border: undefined,
|
|
220
220
|
page: { pageSize: 10, style: { justifyContent: 'flex-end' }, onChange: (a, b) => console.log(a, b) }
|
|
221
221
|
}
|
package/package.json
CHANGED
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
</slot>
|
|
38
38
|
</el-checkbox-group>
|
|
39
39
|
<!--select-->
|
|
40
|
-
<el-select v-model="ctrlValue"
|
|
40
|
+
<el-select v-model="ctrlValue" :placeholder="itemInfo.label" v-bind="selectModel"
|
|
41
|
+
v-else-if="ctrlType == 'select'">
|
|
41
42
|
<template #default v-if="itemInfo?.options?.some(a => a.options)">
|
|
42
43
|
<slot :name="`${ctrlKey}-ctrl`" :options="itemInfo.options">
|
|
43
44
|
<el-option-group v-for="group in itemInfo.options" :label="group.label">
|
|
@@ -61,24 +62,28 @@
|
|
|
61
62
|
</template>
|
|
62
63
|
</el-select>
|
|
63
64
|
<!--tree-select-->
|
|
64
|
-
<el-tree-select v-model="ctrlValue"
|
|
65
|
+
<el-tree-select v-model="ctrlValue" :placeholder="itemInfo.label" v-bind="itemInfo"
|
|
66
|
+
v-else-if="ctrlType == 'tree-select'">
|
|
65
67
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
66
68
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
67
69
|
</template>
|
|
68
70
|
</el-tree-select>
|
|
69
71
|
<!--time-->
|
|
70
|
-
<el-time-picker v-model="ctrlValue" arrow-control v-bind="itemInfo"
|
|
72
|
+
<el-time-picker v-model="ctrlValue" arrow-control v-bind="itemInfo" :placeholder="itemInfo.label"
|
|
73
|
+
v-else-if="ctrlType == 'time'" />
|
|
71
74
|
<!--time-select-->
|
|
72
|
-
<el-time-select v-model="ctrlValue" start="06:30" step="00:15" end="23:30"
|
|
73
|
-
v-else-if="ctrlType == 'time-select'" />
|
|
75
|
+
<el-time-select v-model="ctrlValue" start="06:30" step="00:15" end="23:30" :placeholder="itemInfo.label"
|
|
76
|
+
v-bind="itemInfo" v-else-if="ctrlType == 'time-select'" />
|
|
74
77
|
<!--date-->
|
|
75
|
-
<el-date-picker v-model="ctrlValue" type="date"
|
|
78
|
+
<el-date-picker v-model="ctrlValue" type="date" :placeholder="itemInfo.label" v-bind="itemInfo"
|
|
79
|
+
v-else-if="ctrlType == 'date'">
|
|
76
80
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
77
81
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
78
82
|
</template>
|
|
79
83
|
</el-date-picker>
|
|
80
84
|
<!--datetime-->
|
|
81
|
-
<el-date-picker v-model="ctrlValue" type="datetime"
|
|
85
|
+
<el-date-picker v-model="ctrlValue" type="datetime" :placeholder="itemInfo.label" v-bind="itemInfo"
|
|
86
|
+
v-else-if="ctrlType == 'datetime'">
|
|
82
87
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
83
88
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
84
89
|
</template>
|
|
@@ -114,20 +119,21 @@
|
|
|
114
119
|
</template>
|
|
115
120
|
</el-upload>
|
|
116
121
|
<!--autocomplete-->
|
|
117
|
-
<el-autocomplete v-model="ctrlValue" v-bind="itemInfo"
|
|
122
|
+
<el-autocomplete v-model="ctrlValue" v-bind="itemInfo" :placeholder="itemInfo.label"
|
|
123
|
+
v-else-if="ctrlType == 'autocomplete'">
|
|
118
124
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
119
125
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
120
126
|
</template>
|
|
121
127
|
</el-autocomplete>
|
|
122
128
|
<!--input-number-->
|
|
123
|
-
<el-input-number v-model="ctrlValue" :min="0" :max="100000000" v-bind="itemInfo"
|
|
129
|
+
<el-input-number v-model="ctrlValue" :min="0" :max="100000000" :placeholder="itemInfo.label" v-bind="itemInfo"
|
|
124
130
|
v-else-if="ctrlType == 'input-number'">
|
|
125
131
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
126
132
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
127
133
|
</template>
|
|
128
134
|
</el-input-number>
|
|
129
135
|
<!--input-->
|
|
130
|
-
<el-input v-model="ctrlValue"
|
|
136
|
+
<el-input v-model="ctrlValue" :placeholder="itemInfo.label" :maxlength="itemInfo.max || 25" :clearable="true"
|
|
131
137
|
v-bind="itemInfo" v-else>
|
|
132
138
|
<template v-for="(slot, index) in slots()" :key="index" #[slot]="data">
|
|
133
139
|
<slot :name="`${ctrlKey}-ctrl-${slot}`" :data></slot>
|
|
@@ -139,8 +145,10 @@
|
|
|
139
145
|
<script setup>
|
|
140
146
|
defineOptions({ inheritAttrs: false });
|
|
141
147
|
import { onMounted, useAttrs, useSlots } from "vue";
|
|
148
|
+
import { filterObject } from ".";
|
|
142
149
|
const ctrlValue = defineModel({ required: true });
|
|
143
|
-
const itemInfo = useAttrs().item;
|
|
150
|
+
const itemInfo = filterObject(useAttrs().item, k => k != 'value');
|
|
151
|
+
delete itemInfo.formatter;
|
|
144
152
|
const ctrlKey = useAttrs().ctrlKey;
|
|
145
153
|
const ctrlType = itemInfo.type;
|
|
146
154
|
const model = useAttrs().model;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-form ref="gform" :model="model"
|
|
2
|
+
<el-form ref="gform" :model="model"
|
|
3
|
+
v-bind="filterObject(model.form, k => !['columns', 'footer', 'submit', 'reset', 'slots', 'type', 'sort'].includes(k))"
|
|
4
|
+
class="g-form-container">
|
|
3
5
|
<slot>
|
|
4
6
|
<el-form-item :label="item.label" :prop="`${key}.value`" :rules="getItemRulesByItem(item)"
|
|
5
7
|
v-for="(item, key) of items" :key="key" v-bind="{ ...model?.formItems, ...item?.formItem }"
|
|
@@ -18,11 +20,13 @@
|
|
|
18
20
|
<slot name="footer" v-if="!model.form.footer.hidden">
|
|
19
21
|
<div :style="model.form.footer.style">
|
|
20
22
|
<slot name="footer-left"></slot>
|
|
21
|
-
<el-button @click="model.form.submit.click(model)"
|
|
23
|
+
<el-button @click.stop="model.form.submit.click(formInfo, model)"
|
|
24
|
+
v-bind="filterObject(model.form.submit, k => !['click'].includes(k))"
|
|
22
25
|
v-if="!model?.form?.submit?.hidden">
|
|
23
26
|
{{ model?.form?.submit?.label }}
|
|
24
27
|
</el-button>
|
|
25
|
-
<el-button @click="model.form.reset.click(model)"
|
|
28
|
+
<el-button @click="model.form.reset.click(model)"
|
|
29
|
+
v-bind="filterObject(model.form.reset, k => !['click'].includes(k))"
|
|
26
30
|
v-if="!model?.form?.reset?.hidden">{{
|
|
27
31
|
model?.form?.reset?.label
|
|
28
32
|
}}</el-button>
|
|
@@ -34,15 +38,15 @@
|
|
|
34
38
|
</template>
|
|
35
39
|
|
|
36
40
|
<script setup>
|
|
37
|
-
import { computed, inject, onMounted, ref, useAttrs, watch
|
|
41
|
+
import { computed, getCurrentInstance, inject, onBeforeUnmount, onMounted, ref, useAttrs, watch } from "vue";
|
|
38
42
|
import gCtrl from "./g-ctrl.vue";
|
|
39
|
-
import { convertToPx, mergeObjects } from "./index";
|
|
43
|
+
import { convertToPx, mergeObjects, filterObject } from "./index";
|
|
40
44
|
defineOptions({
|
|
41
45
|
name: "g-form",
|
|
42
46
|
inheritAttrs: false
|
|
43
47
|
})
|
|
44
48
|
const model = defineModel({ required: true });
|
|
45
|
-
const emits = defineEmits(["
|
|
49
|
+
const emits = defineEmits(["handSubmit", "reset"]);
|
|
46
50
|
const gform = ref(null);
|
|
47
51
|
let marginRight = ref(32);
|
|
48
52
|
const options = inject('options');
|
|
@@ -73,13 +77,14 @@ const defaultFormAttrs = {
|
|
|
73
77
|
label: "提交",
|
|
74
78
|
plain: true,
|
|
75
79
|
hidden: false,
|
|
76
|
-
click: async (model) => {
|
|
80
|
+
click: async (formInfo, model) => {
|
|
77
81
|
if (!gform.value) return
|
|
78
82
|
await gform.value.validate((valid, fields) => {
|
|
79
83
|
if (valid) {
|
|
80
|
-
emits("
|
|
84
|
+
emits("handSubmit", formInfo, model);
|
|
85
|
+
console.log("提交");
|
|
81
86
|
} else {
|
|
82
|
-
console.log('
|
|
87
|
+
console.log('表单验证不通过!', fields);
|
|
83
88
|
}
|
|
84
89
|
})
|
|
85
90
|
},
|
|
@@ -101,6 +106,7 @@ const defaultFormAttrs = {
|
|
|
101
106
|
}
|
|
102
107
|
},
|
|
103
108
|
},
|
|
109
|
+
slots: {}
|
|
104
110
|
};
|
|
105
111
|
model.value.form = mergeObjects(
|
|
106
112
|
defaultFormAttrs,
|
|
@@ -138,7 +144,6 @@ const getItemRulesByItem = (item) => {
|
|
|
138
144
|
}
|
|
139
145
|
// 获取item属性并合并规则
|
|
140
146
|
const itemInfo = { ...itemDefaultRule, ...item };
|
|
141
|
-
|
|
142
147
|
//必填验证
|
|
143
148
|
if (itemInfo.required) {
|
|
144
149
|
itemRules.push({ required: true, message: `${itemInfo.label}不能为空!`, trigger: 'blur' });
|
|
@@ -204,7 +209,9 @@ const getItemRulesByItem = (item) => {
|
|
|
204
209
|
return itemRules;
|
|
205
210
|
}
|
|
206
211
|
|
|
207
|
-
onMounted(() => {
|
|
212
|
+
onMounted(() => {
|
|
213
|
+
load();
|
|
214
|
+
})
|
|
208
215
|
|
|
209
216
|
const load = () => {
|
|
210
217
|
if (!model.value.form.inline) {
|
|
@@ -221,11 +228,14 @@ const load = () => {
|
|
|
221
228
|
}
|
|
222
229
|
};
|
|
223
230
|
|
|
231
|
+
onBeforeUnmount(() => {
|
|
232
|
+
console.log('destroy');
|
|
233
|
+
})
|
|
234
|
+
|
|
224
235
|
watch(() => model.value.form.columns, () => load());
|
|
225
236
|
watch(() => model.value.form.inline, () => load());
|
|
226
237
|
|
|
227
238
|
const columnsWidth = computed(() => convertToPx(model.value.form.style.width) / model.value.form.columns);
|
|
228
|
-
console.log(columnsWidth.value);
|
|
229
239
|
//表单项
|
|
230
240
|
const items = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
231
241
|
.filter(a => a[1].type != "hidden")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-for="(item, key) of getQueryModel(props.more)" :key="key">
|
|
3
|
+
<slot :name="`query-item-${key}`" :item :model>
|
|
4
|
+
<el-form-item :label="item.label" v-bind="{ ...model.queryItems, ...item?.search }">
|
|
5
|
+
<slot :name="'query-' + key" :item :model>
|
|
6
|
+
<g-ctrl v-model="queryModel[key]" :item :ctrlKey="key" :model>
|
|
7
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
8
|
+
<slot :name="slotKey" :data :item :model></slot>
|
|
9
|
+
</template>
|
|
10
|
+
</g-ctrl>
|
|
11
|
+
</slot>
|
|
12
|
+
<template #label>
|
|
13
|
+
<slot :name="'query-label-' + key" :item :model></slot>
|
|
14
|
+
</template>
|
|
15
|
+
</el-form-item>
|
|
16
|
+
</slot>
|
|
17
|
+
</template>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup>
|
|
21
|
+
import gCtrl from './g-ctrl.vue';
|
|
22
|
+
import { computed, ref } from "vue";
|
|
23
|
+
const model = defineModel({ required: true });
|
|
24
|
+
const props = defineProps(["more"]);
|
|
25
|
+
const queryModel = defineModel("info", { required: true });
|
|
26
|
+
const getQueryModel = (flag) => computed(() =>
|
|
27
|
+
Object.fromEntries(Object.entries(model.value)
|
|
28
|
+
.filter(a => a[1].search && a[1].search.more === flag).map(key => [key[0], key[1]]))).value;
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<style lang="scss" scoped></style>
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="g-query-container" style="position: relative;">
|
|
3
|
+
<el-form v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))">
|
|
4
|
+
<g-query-item v-model="model" v-model:info="queryModel" :more="undefined">
|
|
5
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
6
|
+
<slot :name="slotKey" :model></slot>
|
|
7
|
+
</template>
|
|
8
|
+
</g-query-item>
|
|
9
|
+
<el-form-item>
|
|
10
|
+
<el-button v-bind="model.query.query" @click="model.query.query.click()">
|
|
11
|
+
{{ model.query.query.label }}
|
|
12
|
+
</el-button>
|
|
13
|
+
<el-button v-bind="model.query.reset" @click="model.query.reset.click()">
|
|
14
|
+
{{ model.query.reset.label }}
|
|
15
|
+
</el-button>
|
|
16
|
+
<el-button v-bind="model.query.more" @click="model.query.more.click()"
|
|
17
|
+
v-if="model.query.more.mode !== 3">
|
|
18
|
+
{{ model.query.more.label }}
|
|
19
|
+
</el-button>
|
|
20
|
+
<el-popover v-bind="model.query.more.dialog" v-if="model.query.more.mode == 3">
|
|
21
|
+
<template #reference>
|
|
22
|
+
<el-button v-bind="model.query.more">
|
|
23
|
+
{{ model.query.more.label }}
|
|
24
|
+
</el-button>
|
|
25
|
+
</template>
|
|
26
|
+
<template #default>
|
|
27
|
+
<el-form v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))">
|
|
28
|
+
<g-query-item v-model="model" v-model:info="queryModel" :more="true">
|
|
29
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
30
|
+
<slot :name="slotKey" :model></slot>
|
|
31
|
+
</template>
|
|
32
|
+
</g-query-item>
|
|
33
|
+
</el-form>
|
|
34
|
+
</template>
|
|
35
|
+
</el-popover>
|
|
36
|
+
<slot name="btns"></slot>
|
|
37
|
+
</el-form-item>
|
|
38
|
+
</el-form>
|
|
39
|
+
<Transition :duration="300" name="nested">
|
|
40
|
+
<div v-if="isShowMore && !model.query.more.hidden && model.query.more.mode == 1" class="outer">
|
|
41
|
+
<div class="inner">
|
|
42
|
+
<el-form v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))"
|
|
43
|
+
v-show="isShowMore" v-if="model.query.more.mode == 1">
|
|
44
|
+
<g-query-item v-model="model" v-model:info="queryModel" :more="true">
|
|
45
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
46
|
+
<slot :name="slotKey" :model></slot>
|
|
47
|
+
</template>
|
|
48
|
+
</g-query-item>
|
|
49
|
+
</el-form>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</Transition>
|
|
53
|
+
<el-drawer v-model="isShowMore" title="更多查询" :show-close="false" v-bind="model.query.more.dialog"
|
|
54
|
+
v-if="isShowMore && !model.query.more.hidden && model.query.more.mode == 2">
|
|
55
|
+
<el-form v-bind="filterObject(model.query, k => !['query', 'more', 'reset'].includes(k))">
|
|
56
|
+
<g-query-item v-model="model" v-model:info="queryModel" :more="true">
|
|
57
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
58
|
+
<slot :name="slotKey" :model></slot>
|
|
59
|
+
</template>
|
|
60
|
+
</g-query-item>
|
|
61
|
+
</el-form>
|
|
62
|
+
<template #footer v-if="!model.query.more.dialog.footer.hidden">
|
|
63
|
+
<div v-bind="model.query.more.dialog.footer">
|
|
64
|
+
<el-button v-bind="model.query.query" @click="model.query.query.click()">
|
|
65
|
+
{{ model.query.query.label }}
|
|
66
|
+
</el-button>
|
|
67
|
+
<el-button v-bind="model.query.reset" @click="model.query.reset.click()">
|
|
68
|
+
{{ model.query.reset.label }}
|
|
69
|
+
</el-button>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
</el-drawer>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script setup>
|
|
77
|
+
import { ref, inject } from 'vue'
|
|
78
|
+
import gQueryItem from './g-query-item.vue';
|
|
79
|
+
import { mergeObjects, filterObject } from './index';
|
|
80
|
+
const model = defineModel({ required: true });
|
|
81
|
+
const emits = defineEmits(['query', 'reset']);
|
|
82
|
+
const options = inject('options');
|
|
83
|
+
const isShowMore = ref(false);
|
|
84
|
+
let initValues = Object.fromEntries(Object.entries(model.value).filter(a => a[1].search).map(([key, val]) => [key, val.value]));
|
|
85
|
+
const queryModel = ref(initValues);
|
|
86
|
+
const defaultQueryAttrs = {
|
|
87
|
+
hidden: false,
|
|
88
|
+
type: "hidden",
|
|
89
|
+
labelWidth: 100,
|
|
90
|
+
inline: true,
|
|
91
|
+
style: { display: "flex", flex: 1, "flex-wrap": "wrap" },
|
|
92
|
+
more: {
|
|
93
|
+
mode: 2,
|
|
94
|
+
hidden: false,
|
|
95
|
+
type: "primary",
|
|
96
|
+
label: "更多",
|
|
97
|
+
click: () => {
|
|
98
|
+
isShowMore.value = !isShowMore.value;
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
reset: {
|
|
102
|
+
label: "重置",
|
|
103
|
+
type: "primary",
|
|
104
|
+
hidden: false,
|
|
105
|
+
plain: false,
|
|
106
|
+
click: () => {
|
|
107
|
+
queryModel.value = ref(initValues);
|
|
108
|
+
emits('reset', queryModel.value);
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
query: {
|
|
112
|
+
hidden: false,
|
|
113
|
+
type: "primary",
|
|
114
|
+
label: "查询",
|
|
115
|
+
click: () => {
|
|
116
|
+
emits('query', queryModel.value);
|
|
117
|
+
isShowMore.value = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 查询表单属性
|
|
123
|
+
model.value.query = mergeObjects(defaultQueryAttrs, options?.query, model.value.query);
|
|
124
|
+
|
|
125
|
+
// 表单 查询更多 属性 drawer 方式2 和 popover 方式3
|
|
126
|
+
model.value.query.more.dialog = model.value.query.more.mode == 2 ? mergeObjects({
|
|
127
|
+
title: "更多查询", showClose: false, size: '30%', direction: "ttb", footer: {
|
|
128
|
+
hidden: false,
|
|
129
|
+
style: { textAlign: 'center' }
|
|
130
|
+
}
|
|
131
|
+
}, model.value.query.more.dialog) : model.value.query.more.mode == 3 ? mergeObjects({
|
|
132
|
+
placement: "top-start", title: "更多查询", width: 1200, trigger: "click"
|
|
133
|
+
}, model.value.query.more.dialog) : {};
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// 表单查询项属性
|
|
137
|
+
const defaultQueryItemsAttrs = {
|
|
138
|
+
type: "hidden",
|
|
139
|
+
labelPosition: 'right',
|
|
140
|
+
style: {
|
|
141
|
+
width: "250px"
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
model.value.queryItems = mergeObjects(defaultQueryItemsAttrs, options?.queryItems, model.value.queryItems);
|
|
145
|
+
|
|
146
|
+
</script>
|
|
147
|
+
|
|
148
|
+
<style scoped>
|
|
149
|
+
.g-query-container {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex: 1;
|
|
152
|
+
flex-direction: column;
|
|
153
|
+
padding: 10px;
|
|
154
|
+
box-sizing: border-box;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.outer {
|
|
158
|
+
/* position: absolute; */
|
|
159
|
+
min-height: 100px;
|
|
160
|
+
border-radius: 10px;
|
|
161
|
+
background-color: rgba(255, 255, 255, 0.8);
|
|
162
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
163
|
+
top: 60px;
|
|
164
|
+
padding: 10px;
|
|
165
|
+
width: 100%;
|
|
166
|
+
box-sizing: border-box;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.inner {
|
|
170
|
+
border-radius: 0;
|
|
171
|
+
min-height: 100px;
|
|
172
|
+
border: 1px solid #f2f2f2;
|
|
173
|
+
padding: 20px;
|
|
174
|
+
box-sizing: border-box;
|
|
175
|
+
width: 100%;
|
|
176
|
+
background-color: rgba(255, 255, 255, 1);
|
|
177
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.nested-enter-active,
|
|
181
|
+
.nested-leave-active {
|
|
182
|
+
transition: all 0.3s ease-in-out;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* delay leave of parent element */
|
|
186
|
+
.nested-leave-active {
|
|
187
|
+
transition-delay: 0.25s;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.nested-enter-from,
|
|
191
|
+
.nested-leave-to {
|
|
192
|
+
transform: translateY(-100px);
|
|
193
|
+
opacity: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* we can also transition nested elements using nested selectors */
|
|
197
|
+
.nested-enter-active .inner,
|
|
198
|
+
.nested-leave-active .inner {
|
|
199
|
+
transition: all 0.3s ease-in-out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* delay enter of nested element */
|
|
203
|
+
.nested-enter-active .inner {
|
|
204
|
+
transition-delay: 0.25s;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.nested-enter-from .inner,
|
|
208
|
+
.nested-leave-to .inner {
|
|
209
|
+
transform: translateY(-100px);
|
|
210
|
+
/*
|
|
211
|
+
Hack around a Chrome 96 bug in handling nested opacity transitions.
|
|
212
|
+
This is not needed in other browsers or Chrome 99+ where the bug
|
|
213
|
+
has been fixed.
|
|
214
|
+
*/
|
|
215
|
+
opacity: 0.001;
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<div class="table-query-container">
|
|
3
|
+
<slot name="query">
|
|
4
|
+
<g-query v-model="model" @query="query" @reset="reset">
|
|
5
|
+
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
6
|
+
<slot :name="slotKey" :model></slot>
|
|
7
|
+
</template>
|
|
8
|
+
<template #btns>
|
|
9
|
+
<slot name="btns">
|
|
10
|
+
<div class="btns" v-if="model.control.add.auth()">
|
|
11
|
+
<slot name="control-add-left"></slot>
|
|
12
|
+
<slot name="control-add">
|
|
13
|
+
<el-button v-bind="model.control.add" @click="model.control.add.click(model)">{{
|
|
14
|
+
model.control.add.label
|
|
15
|
+
}}</el-button>
|
|
16
|
+
</slot>
|
|
17
|
+
<slot name="control-add-right"></slot>
|
|
18
|
+
</div>
|
|
19
|
+
</slot>
|
|
20
|
+
|
|
21
|
+
</template>
|
|
22
|
+
</g-query>
|
|
23
|
+
</slot>
|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
<el-table class="g-table-container" v-bind="{ ...$attrs, ...filterObject(model.table, k => k !== 'page') }">
|
|
3
27
|
<slot>
|
|
4
28
|
<el-table-column :prop="key" v-for="(value, key) of tableColumns" :key="key"
|
|
5
|
-
v-bind="{ ...
|
|
29
|
+
v-bind="{ ...model.tableColumns, ...value }" header-align="center">
|
|
6
30
|
<template #default="{ row, column, $index }"
|
|
7
31
|
v-if="value.type != 'selection' && value.type != 'index' && value.type != 'expand'">
|
|
8
32
|
<slot :name="`${key}`" :row :column :$index>
|
|
@@ -65,18 +89,20 @@
|
|
|
65
89
|
<script setup>
|
|
66
90
|
import dayjs from 'dayjs';
|
|
67
91
|
import { ElMessageBox } from 'element-plus';
|
|
68
|
-
import { computed,
|
|
92
|
+
import { computed, watchEffect, inject, ref, h } from 'vue';
|
|
69
93
|
import GForm from './g-form.vue';
|
|
70
|
-
import
|
|
94
|
+
import gQuery from './g-query.vue';
|
|
95
|
+
import { openModal, mergeObjects, convertToPx, filterObject } from './index';
|
|
71
96
|
|
|
72
97
|
defineOptions({
|
|
73
98
|
name: "g-table",
|
|
74
99
|
inheritAttrs: false
|
|
75
100
|
})
|
|
76
101
|
|
|
77
|
-
const emits = defineEmits(['load', 'view', "edit", "delete"]);
|
|
102
|
+
const emits = defineEmits(['load', 'view', "edit", "delete", 'add']);
|
|
78
103
|
const model = defineModel({ required: true });
|
|
79
104
|
const options = inject('options');
|
|
105
|
+
const queryInfo = ref({});
|
|
80
106
|
|
|
81
107
|
//table default attrs
|
|
82
108
|
const defaultTableAttrs = {
|
|
@@ -86,10 +112,10 @@ const defaultTableAttrs = {
|
|
|
86
112
|
emptyText: '暂无数据',
|
|
87
113
|
fit: false,
|
|
88
114
|
type: "hidden",
|
|
89
|
-
height: "calc(100vh -
|
|
115
|
+
height: "calc(100vh - 65px)",
|
|
90
116
|
load: () => {
|
|
91
117
|
let { currentPage, pageSize } = model.value.table.page;
|
|
92
|
-
emits('load', { currentPage, pageSize }, (data, total) => {
|
|
118
|
+
emits('load', { currentPage, pageSize, ...queryInfo.value }, (data, total) => {
|
|
93
119
|
model.value.table.data = data;
|
|
94
120
|
model.value.table.page.total = total;
|
|
95
121
|
});
|
|
@@ -113,78 +139,115 @@ model.value.table = mergeObjects(
|
|
|
113
139
|
options?.table,
|
|
114
140
|
model.value.table
|
|
115
141
|
);
|
|
116
|
-
|
|
117
142
|
const defaultControl = {
|
|
118
143
|
type: 'control', label: '操作', edit: false, width: 250, fixed: 'right',
|
|
119
144
|
style: { ...model.value?.control?.style },
|
|
120
145
|
add: {
|
|
121
146
|
label: '新增', text: false, type: 'primary',
|
|
147
|
+
style: {
|
|
148
|
+
marginLeft: "10px",
|
|
149
|
+
},
|
|
122
150
|
dialog: {
|
|
123
151
|
title: '新增',
|
|
124
|
-
|
|
125
|
-
slots: {
|
|
126
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
auth: () => {
|
|
130
|
-
return true;
|
|
152
|
+
mode: "drawer",
|
|
131
153
|
},
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
154
|
+
auth: () => true,
|
|
155
|
+
click: (m) => {
|
|
156
|
+
model.value.form = {
|
|
157
|
+
...model.value.form,
|
|
158
|
+
type: 'hidden',
|
|
159
|
+
disabled: undefined,
|
|
160
|
+
footer: { hidden: false }
|
|
161
|
+
};
|
|
162
|
+
let w = model.value?.form?.style?.width || 600;
|
|
163
|
+
if (model.value.control.add.dialog.mode == "drawer") {
|
|
164
|
+
model.value.control.add.dialog.size = convertToPx(w) + 45;
|
|
165
|
+
} else {
|
|
166
|
+
model.value.control.add.dialog.width = convertToPx(w) + 35;
|
|
167
|
+
}
|
|
168
|
+
emits("add", m);
|
|
169
|
+
Object.keys(initFormData).forEach(key => {
|
|
136
170
|
if (model.value[key]) {
|
|
137
|
-
model.value[key].value =
|
|
171
|
+
model.value[key].value = initFormData[key];
|
|
138
172
|
}
|
|
139
173
|
});
|
|
140
|
-
openModal(
|
|
174
|
+
let colse = openModal(
|
|
175
|
+
{
|
|
176
|
+
...model.value?.control?.add?.dialog,
|
|
177
|
+
slots: {
|
|
178
|
+
default: () => h(GForm, {
|
|
179
|
+
modelValue: model.value,
|
|
180
|
+
...model.value.form,
|
|
181
|
+
key: new Date(),
|
|
182
|
+
onHandSubmit: () => {
|
|
183
|
+
colse();
|
|
184
|
+
},
|
|
185
|
+
}, { ...model.value.form.slots })
|
|
186
|
+
}
|
|
187
|
+
}, model.value?.control?.add?.dialog?.mode);
|
|
141
188
|
},
|
|
142
189
|
},
|
|
143
190
|
view: {
|
|
144
191
|
label: '查看', text: true, type: 'success',
|
|
145
192
|
dialog: {
|
|
146
193
|
title: '查看',
|
|
147
|
-
|
|
148
|
-
slots: {
|
|
149
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
150
|
-
},
|
|
194
|
+
mode: "dialog",
|
|
151
195
|
},
|
|
152
196
|
auth: (row, column, $index) => {
|
|
153
197
|
return false;
|
|
154
198
|
},
|
|
155
199
|
click: (row, column, $index, m) => {
|
|
200
|
+
model.value.form = {
|
|
201
|
+
...model.value.form,
|
|
202
|
+
type: 'hidden',
|
|
203
|
+
disabled: true,
|
|
204
|
+
footer: { hidden: true }
|
|
205
|
+
};
|
|
156
206
|
let w = model.value?.form?.style?.width || 600;
|
|
157
|
-
if (model.value.control.view.dialog.
|
|
207
|
+
if (model.value.control.view.dialog.mode == "drawer") {
|
|
158
208
|
model.value.control.view.dialog.size = convertToPx(w) + 45;
|
|
159
209
|
} else {
|
|
160
210
|
model.value.control.view.dialog.width = convertToPx(w) + 35;
|
|
161
211
|
}
|
|
162
|
-
model.value.form.disabled = true;
|
|
163
212
|
emits("view", row, column, $index, m);
|
|
164
213
|
Object.keys(row).forEach(key => {
|
|
165
214
|
if (model.value[key]) {
|
|
166
215
|
model.value[key].value = row[key];
|
|
167
216
|
}
|
|
168
217
|
});
|
|
169
|
-
openModal(
|
|
218
|
+
let colse = openModal(
|
|
219
|
+
{
|
|
220
|
+
...model.value?.control?.view?.dialog,
|
|
221
|
+
slots: {
|
|
222
|
+
default: () => h(GForm, {
|
|
223
|
+
modelValue: model.value,
|
|
224
|
+
...model.value.form,
|
|
225
|
+
onHandSubmit: () => {
|
|
226
|
+
colse();
|
|
227
|
+
},
|
|
228
|
+
}, { ...model.value.form.slots })
|
|
229
|
+
}
|
|
230
|
+
}, model.value?.control?.view?.dialog?.mode);
|
|
170
231
|
},
|
|
171
232
|
},
|
|
172
233
|
edit: {
|
|
173
234
|
label: '编辑', text: true, type: 'primary',
|
|
174
235
|
dialog: {
|
|
175
236
|
title: '编辑',
|
|
176
|
-
|
|
177
|
-
slots: {
|
|
178
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
179
|
-
},
|
|
237
|
+
mode: "dialog",
|
|
180
238
|
},
|
|
181
239
|
auth: (row, column, $index) => {
|
|
182
240
|
return true;
|
|
183
241
|
},
|
|
184
242
|
click: (row, column, $index, m) => {
|
|
185
|
-
model.value.form
|
|
243
|
+
model.value.form = {
|
|
244
|
+
...model.value.form,
|
|
245
|
+
type: 'hidden',
|
|
246
|
+
disabled: undefined,
|
|
247
|
+
footer: { hidden: false }
|
|
248
|
+
};
|
|
186
249
|
let w = model.value?.form?.style?.width || 600;
|
|
187
|
-
if (model.value.control.edit.dialog.
|
|
250
|
+
if (model.value.control.edit.dialog.mode == "drawer") {
|
|
188
251
|
model.value.control.edit.dialog.size = convertToPx(w) + 45;
|
|
189
252
|
} else {
|
|
190
253
|
model.value.control.edit.dialog.width = convertToPx(w) + 35;
|
|
@@ -195,7 +258,19 @@ const defaultControl = {
|
|
|
195
258
|
model.value[key].value = row[key];
|
|
196
259
|
}
|
|
197
260
|
});
|
|
198
|
-
openModal(
|
|
261
|
+
let colse = openModal(
|
|
262
|
+
{
|
|
263
|
+
...model.value?.control?.edit?.dialog,
|
|
264
|
+
slots: {
|
|
265
|
+
default: () => h(GForm, {
|
|
266
|
+
modelValue: model.value,
|
|
267
|
+
...model.value.form,
|
|
268
|
+
onHandSubmit: () => {
|
|
269
|
+
colse();
|
|
270
|
+
},
|
|
271
|
+
}, { ...model.value.form.slots })
|
|
272
|
+
}
|
|
273
|
+
}, model.value?.control?.edit?.dialog?.mode);
|
|
199
274
|
},
|
|
200
275
|
},
|
|
201
276
|
delete: {
|
|
@@ -212,6 +287,7 @@ const defaultControl = {
|
|
|
212
287
|
emits("delete", { row, column, $index }, (res) => {
|
|
213
288
|
if (res) {
|
|
214
289
|
model.value.table.data.splice($index, 1);
|
|
290
|
+
model.value.table.page.total--;
|
|
215
291
|
}
|
|
216
292
|
})
|
|
217
293
|
}).catch(() => { });
|
|
@@ -226,12 +302,24 @@ model.value.control = mergeObjects(
|
|
|
226
302
|
model.value?.control
|
|
227
303
|
);
|
|
228
304
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
width:
|
|
305
|
+
const defaultTableColumnsAttrs = {
|
|
306
|
+
type: "hidden",
|
|
307
|
+
width: 120,
|
|
308
|
+
align: "center",
|
|
309
|
+
formatter: (row, column, cellValue, index) => {
|
|
232
310
|
return cellValue;
|
|
233
311
|
}
|
|
234
|
-
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
model.value.tableColumns = mergeObjects(
|
|
315
|
+
defaultTableColumnsAttrs,
|
|
316
|
+
options?.tableColumns,
|
|
317
|
+
model.value?.tableColumns
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const initFormData = Object.fromEntries(Object.entries(filterObject(model.value, (k, val) => {
|
|
321
|
+
return (val.type != 'control' && val.edit !== false && val.type != 'hidden');
|
|
322
|
+
})).map(([key, val]) => [key, val.value]));
|
|
235
323
|
|
|
236
324
|
//扩展formatter方法
|
|
237
325
|
const formatterExtends = (item, str) => {
|
|
@@ -272,9 +360,17 @@ const formatterExtends = (item, str) => {
|
|
|
272
360
|
return trueValue
|
|
273
361
|
}
|
|
274
362
|
|
|
275
|
-
|
|
363
|
+
const reset = (q) => {
|
|
364
|
+
console.log(q);
|
|
365
|
+
queryInfo.value = q.value;
|
|
366
|
+
//model.value.table.load();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const query = (q) => {
|
|
370
|
+
queryInfo.value = q;
|
|
371
|
+
//model.value.table.load();
|
|
372
|
+
}
|
|
276
373
|
|
|
277
|
-
});
|
|
278
374
|
|
|
279
375
|
watchEffect(() => {
|
|
280
376
|
model.value.table.load();
|
|
@@ -290,6 +386,4 @@ const tableColumns = computed(() => Object.fromEntries(
|
|
|
290
386
|
return [key[0], key[1]]
|
|
291
387
|
}).sort((a, b) => a[1].sort - b[1].sort))
|
|
292
388
|
);
|
|
293
|
-
</script>
|
|
294
|
-
|
|
295
|
-
<style lang="scss" scoped></style>
|
|
389
|
+
</script>
|
|
@@ -1,34 +1,90 @@
|
|
|
1
|
-
|
|
1
|
+
// 导入Vue的Component模块
|
|
2
2
|
import { Component } from 'vue';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
declare module 'vue' {
|
|
6
|
-
interface ComponentCustomProperties {
|
|
7
|
-
openModel: typeof openModal;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
3
|
+
// 从element-plus库中导入ElDialog和ElDrawer组件
|
|
4
|
+
import { ElDialog, ElDrawer } from 'element-plus';
|
|
10
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 全局选项的接口定义
|
|
8
|
+
*/
|
|
11
9
|
export interface GlobalOptions {
|
|
12
10
|
// 在这里定义全局选项的类型
|
|
13
11
|
}
|
|
14
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 打开模态框的属性接口定义
|
|
15
|
+
*/
|
|
15
16
|
export interface OpenModalProps {
|
|
17
|
+
// 在这里定义传递给模态组件的属性类型
|
|
16
18
|
slots?: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
// 默认插槽的函数类型定义
|
|
20
|
+
default?: (a: any) => any;
|
|
21
|
+
// 头部插槽的函数类型定义
|
|
22
|
+
header?: (p: any) => any;
|
|
23
|
+
// 底部插槽的函数类型定义
|
|
24
|
+
footer?: (p: any) => any;
|
|
20
25
|
};
|
|
21
|
-
// 在这里定义其他传递给openModal函数的属性
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export
|
|
28
|
+
/**
|
|
29
|
+
* 转换为像素的选项接口定义
|
|
30
|
+
*/
|
|
31
|
+
export interface ConvertToPxOptions {
|
|
32
|
+
// 参考尺寸的可选属性,类型为number
|
|
33
|
+
referenceSize?: number;
|
|
34
|
+
}
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 合并对象的选项接口定义
|
|
38
|
+
*/
|
|
39
|
+
export interface MergeObjectsOptions {
|
|
40
|
+
// 在这里定义合并对象的选项类型
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 过滤对象的选项接口定义
|
|
45
|
+
*/
|
|
46
|
+
export interface FilterObjectOptions {
|
|
47
|
+
// 过滤函数的可选属性,类型为(key: string, value: any) => boolean
|
|
48
|
+
filter?: (key: string, value: any) => boolean;
|
|
49
|
+
}
|
|
33
50
|
|
|
34
|
-
|
|
51
|
+
/**
|
|
52
|
+
* 打开模态框的函数定义
|
|
53
|
+
* @param {OpenModalProps} [props] - 传递给模态组件的属性
|
|
54
|
+
* @param {'dialog' | 'drawer'} [mode] - 模态框的模式,可选值为'dialog'或'drawer'
|
|
55
|
+
* @returns {() => void} - 返回一个函数,用于关闭模态框
|
|
56
|
+
*/
|
|
57
|
+
export function openModal(props?: OpenModalProps, mode?: 'dialog' | 'drawer'): () => void;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 将值转换为像素的函数定义
|
|
61
|
+
* @param {string | number} value - 需要转换的值
|
|
62
|
+
* @param {ConvertToPxOptions} [options] - 转换选项
|
|
63
|
+
* @returns {number} - 转换后的像素值
|
|
64
|
+
*/
|
|
65
|
+
export function convertToPx(value: string | number, options?: ConvertToPxOptions): number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 合并多个对象的函数定义
|
|
69
|
+
* @param {...Record<string, any>} objects - 需要合并的对象
|
|
70
|
+
* @returns {Record<string, any>} - 合并后的对象
|
|
71
|
+
*/
|
|
72
|
+
export function mergeObjects(...objects: Array<Record<string, any>>): Record<string, any>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 过滤对象的函数定义
|
|
76
|
+
* @param {Record<string, any>} obj - 需要过滤的对象
|
|
77
|
+
* @param {FilterObjectOptions} [options] - 过滤选项
|
|
78
|
+
* @returns {Record<string, any>} - 过滤后的对象
|
|
79
|
+
*/
|
|
80
|
+
export function filterObject(obj: Record<string, any>, options?: FilterObjectOptions): Record<string, any>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 扩展Vue的ComponentCustomProperties接口,添加openModel属性
|
|
84
|
+
*/
|
|
85
|
+
declare module 'vue' {
|
|
86
|
+
interface ComponentCustomProperties {
|
|
87
|
+
// openModel属性的类型定义,指向openModal函数
|
|
88
|
+
openModel: typeof openModal;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/components/index.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import ElementPlus from 'element-plus'
|
|
4
4
|
import { ElDialog, ElDrawer } from "element-plus";
|
|
5
|
+
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
|
5
6
|
import gTable from "./g-table.vue";
|
|
6
7
|
import gForm from "./g-form.vue";
|
|
7
8
|
import { createApp, h, provide, readonly, ref } from "vue";
|
|
8
9
|
const globalOptions = {};
|
|
9
|
-
|
|
10
10
|
const components = [
|
|
11
11
|
gTable,
|
|
12
12
|
gForm
|
|
@@ -19,42 +19,73 @@ export default {
|
|
|
19
19
|
app.component(component.name, component);
|
|
20
20
|
}
|
|
21
21
|
app.provide("options", readonly(options));
|
|
22
|
-
app.config.globalProperties.
|
|
22
|
+
app.config.globalProperties.openModal = openModal;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 打开模态对话框的函数。
|
|
28
|
+
* @param {Object} modalProps - 模态对话框的属性。
|
|
29
|
+
* @param {string} [mode="dialog"] - 模态对话框的模式,支持"dialog"和"drawer"。
|
|
30
|
+
* @returns {Function} - 关闭模态对话框的函数。
|
|
31
|
+
*/
|
|
32
|
+
const openModal = (modalProps = {}, mode = "dialog") => {
|
|
33
|
+
// 创建一个新的 div 元素
|
|
26
34
|
const el = document.createElement('div');
|
|
27
|
-
|
|
35
|
+
// 定义关闭模态对话框的函数
|
|
36
|
+
let closeModal = () => { };
|
|
37
|
+
// 使用 ref 创建一个响应式的 visible 变量
|
|
38
|
+
const visible = ref(true);
|
|
39
|
+
// 创建一个新的 Vue 应用实例
|
|
40
|
+
let modalApp = createApp({
|
|
28
41
|
setup() {
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
// 定义关闭模态对话框的函数
|
|
43
|
+
closeModal = () => {
|
|
31
44
|
visible.value = false;
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
modalApp.unmount(); // 关闭对话框时卸载组件
|
|
46
|
+
el.remove(); // 关闭对话框时删除元素
|
|
47
|
+
modalApp = null;
|
|
34
48
|
};
|
|
35
|
-
|
|
36
|
-
return () => h(
|
|
49
|
+
// 返回渲染函数
|
|
50
|
+
return () => h(mode == "dialog" ? ElDialog : mode == "drawer" ? ElDrawer : null, {
|
|
37
51
|
modelValue: visible.value,
|
|
38
52
|
'onUpdate:modelValue': closeModal,
|
|
39
53
|
draggable: true,
|
|
40
54
|
"append-to-body": true,
|
|
41
|
-
|
|
55
|
+
'destroy-on-close': true,
|
|
56
|
+
...modalProps
|
|
42
57
|
},
|
|
43
58
|
{
|
|
44
|
-
default:
|
|
45
|
-
header:
|
|
46
|
-
footer:
|
|
59
|
+
default: modalProps.slots?.default ? () => modalProps.slots.default() : null,
|
|
60
|
+
header: modalProps.slots?.header ? () => h('div', {}, modalProps.slots.header()) : null,
|
|
61
|
+
footer: modalProps.slots?.footer ? () => h('div', {}, modalProps.slots.footer()) : null
|
|
47
62
|
}
|
|
48
63
|
);
|
|
49
64
|
}
|
|
50
65
|
});
|
|
66
|
+
// 注册全局组件
|
|
51
67
|
for (const component of components) {
|
|
52
|
-
|
|
68
|
+
modalApp.component(component.name, component);
|
|
53
69
|
}
|
|
54
|
-
|
|
70
|
+
// 提供全局选项
|
|
71
|
+
modalApp.provide("options", globalOptions);
|
|
72
|
+
// 使用 ElementPlus 插件
|
|
73
|
+
modalApp.use(ElementPlus, { locale: zhCn }).mount(el);
|
|
74
|
+
// 将元素添加到 body 中
|
|
55
75
|
document.body.appendChild(el);
|
|
76
|
+
// 返回关闭模态对话框的函数
|
|
77
|
+
return closeModal;
|
|
56
78
|
};
|
|
57
79
|
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 将值转换为像素的函数,支持px、vh、vw、%和calc表达式。
|
|
83
|
+
* @param {string|number} value - 需要转换的值。
|
|
84
|
+
* @param {number} [referenceSize=window.innerWidth] - 用于转换%值的参考大小。
|
|
85
|
+
* @returns {number} - 转换后的像素值。
|
|
86
|
+
* @throws {Error} - 如果无法识别单位或提供的参考大小无效。
|
|
87
|
+
*/
|
|
88
|
+
|
|
58
89
|
function convertToPx(value, referenceSize = window.innerWidth) {
|
|
59
90
|
// 如果已经是px,直接返回
|
|
60
91
|
if (typeof value === 'number') {
|
|
@@ -105,6 +136,11 @@ function convertToPx(value, referenceSize = window.innerWidth) {
|
|
|
105
136
|
throw new Error('Unsupported unit in value: ' + value);
|
|
106
137
|
}
|
|
107
138
|
|
|
139
|
+
/**
|
|
140
|
+
* 合并多个对象的函数,递归地合并嵌套对象。
|
|
141
|
+
* @param {...Object} objects - 需要合并的对象。
|
|
142
|
+
* @returns {Object} - 合并后的对象。
|
|
143
|
+
*/
|
|
108
144
|
|
|
109
145
|
function mergeObjects(...objects) {
|
|
110
146
|
const result = ref({});
|
|
@@ -123,4 +159,23 @@ function mergeObjects(...objects) {
|
|
|
123
159
|
}
|
|
124
160
|
return result.value;
|
|
125
161
|
}
|
|
126
|
-
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 过滤对象的函数,根据给定的过滤条件返回一个新的对象。
|
|
165
|
+
* @param {Object} obj - 需要过滤的对象。
|
|
166
|
+
* @param {Function} filter - 过滤条件函数,接收键和值作为参数,返回布尔值。
|
|
167
|
+
* @returns {Object} - 过滤后的新对象。
|
|
168
|
+
*/
|
|
169
|
+
function filterObject(obj, filter = (key, value) => filter(key, value)) {
|
|
170
|
+
const result = {};
|
|
171
|
+
for (const key in obj) {
|
|
172
|
+
if (obj.hasOwnProperty(key)) {
|
|
173
|
+
if (filter(key, obj[key])) {
|
|
174
|
+
result[key] = obj[key];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { openModal, convertToPx, mergeObjects, filterObject };
|