vue-2024-ui 0.0.8 → 0.0.9
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 -11
- package/src/components/g-query-item.vue +31 -0
- package/src/components/g-query.vue +217 -0
- package/src/components/g-table.vue +111 -35
- package/src/components/index.d.ts +22 -19
- package/src/components/index.js +29 -12
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="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(["usubmit", "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) => {
|
|
81
|
+
emits("usubmit", formInfo, model);
|
|
77
82
|
if (!gform.value) return
|
|
78
83
|
await gform.value.validate((valid, fields) => {
|
|
79
84
|
if (valid) {
|
|
80
|
-
|
|
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,
|
|
@@ -204,7 +210,9 @@ const getItemRulesByItem = (item) => {
|
|
|
204
210
|
return itemRules;
|
|
205
211
|
}
|
|
206
212
|
|
|
207
|
-
onMounted(() => {
|
|
213
|
+
onMounted(() => {
|
|
214
|
+
load();
|
|
215
|
+
})
|
|
208
216
|
|
|
209
217
|
const load = () => {
|
|
210
218
|
if (!model.value.form.inline) {
|
|
@@ -221,11 +229,14 @@ const load = () => {
|
|
|
221
229
|
}
|
|
222
230
|
};
|
|
223
231
|
|
|
232
|
+
onBeforeUnmount(() => {
|
|
233
|
+
console.log('destroy');
|
|
234
|
+
})
|
|
235
|
+
|
|
224
236
|
watch(() => model.value.form.columns, () => load());
|
|
225
237
|
watch(() => model.value.form.inline, () => load());
|
|
226
238
|
|
|
227
239
|
const columnsWidth = computed(() => convertToPx(model.value.form.style.width) / model.value.form.columns);
|
|
228
|
-
console.log(columnsWidth.value);
|
|
229
240
|
//表单项
|
|
230
241
|
const items = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
231
242
|
.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,21 @@
|
|
|
65
89
|
<script setup>
|
|
66
90
|
import dayjs from 'dayjs';
|
|
67
91
|
import { ElMessageBox } from 'element-plus';
|
|
68
|
-
import { computed, onMounted,
|
|
92
|
+
import { computed, onMounted, watchEffect, h, inject, ref, defineAsyncComponent, shallowRef } from 'vue';
|
|
69
93
|
import GForm from './g-form.vue';
|
|
70
|
-
import
|
|
94
|
+
import gQuery from './g-query.vue';
|
|
95
|
+
import { openModal, mergeObjects, convertToPx } from './index';
|
|
96
|
+
import { filterObject } from '.';
|
|
71
97
|
|
|
72
98
|
defineOptions({
|
|
73
99
|
name: "g-table",
|
|
74
100
|
inheritAttrs: false
|
|
75
101
|
})
|
|
76
102
|
|
|
77
|
-
const emits = defineEmits(['load', 'view', "edit", "delete"]);
|
|
103
|
+
const emits = defineEmits(['load', 'view', "edit", "delete", 'add']);
|
|
78
104
|
const model = defineModel({ required: true });
|
|
79
105
|
const options = inject('options');
|
|
106
|
+
const queryInfo = ref({});
|
|
80
107
|
|
|
81
108
|
//table default attrs
|
|
82
109
|
const defaultTableAttrs = {
|
|
@@ -86,10 +113,10 @@ const defaultTableAttrs = {
|
|
|
86
113
|
emptyText: '暂无数据',
|
|
87
114
|
fit: false,
|
|
88
115
|
type: "hidden",
|
|
89
|
-
height: "calc(100vh -
|
|
116
|
+
height: "calc(100vh - 65px)",
|
|
90
117
|
load: () => {
|
|
91
118
|
let { currentPage, pageSize } = model.value.table.page;
|
|
92
|
-
emits('load', { currentPage, pageSize }, (data, total) => {
|
|
119
|
+
emits('load', { currentPage, pageSize, ...queryInfo.value }, (data, total) => {
|
|
93
120
|
model.value.table.data = data;
|
|
94
121
|
model.value.table.page.total = total;
|
|
95
122
|
});
|
|
@@ -114,77 +141,104 @@ model.value.table = mergeObjects(
|
|
|
114
141
|
model.value.table
|
|
115
142
|
);
|
|
116
143
|
|
|
144
|
+
let closeModel = ref(() => { });
|
|
145
|
+
const editModel = defineAsyncComponent(() => import('./g-form.vue'));
|
|
117
146
|
const defaultControl = {
|
|
118
147
|
type: 'control', label: '操作', edit: false, width: 250, fixed: 'right',
|
|
119
148
|
style: { ...model.value?.control?.style },
|
|
120
149
|
add: {
|
|
121
150
|
label: '新增', text: false, type: 'primary',
|
|
151
|
+
style: {
|
|
152
|
+
marginLeft: "10px",
|
|
153
|
+
},
|
|
122
154
|
dialog: {
|
|
123
155
|
title: '新增',
|
|
124
|
-
|
|
156
|
+
mode: "drawer",
|
|
125
157
|
slots: {
|
|
126
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
158
|
+
default: () => h(GForm, { modelValue: model.value }, { ...model.value.form.slots }),
|
|
127
159
|
},
|
|
128
160
|
},
|
|
129
|
-
auth: () =>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
161
|
+
auth: () => true,
|
|
162
|
+
click: (m) => {
|
|
163
|
+
model.value.form = {
|
|
164
|
+
...model.value.form,
|
|
165
|
+
type: 'hidden',
|
|
166
|
+
disabled: undefined,
|
|
167
|
+
};
|
|
168
|
+
let w = model.value?.form?.style?.width || 600;
|
|
169
|
+
if (model.value.control.add.dialog.mode == "drawer") {
|
|
170
|
+
model.value.control.add.dialog.size = convertToPx(w) + 45;
|
|
171
|
+
} else {
|
|
172
|
+
model.value.control.add.dialog.width = convertToPx(w) + 35;
|
|
173
|
+
}
|
|
174
|
+
emits("add", m);
|
|
175
|
+
Object.keys(initFormData).forEach(key => {
|
|
136
176
|
if (model.value[key]) {
|
|
137
|
-
model.value[key].value =
|
|
177
|
+
model.value[key].value = initFormData[key];
|
|
138
178
|
}
|
|
139
179
|
});
|
|
140
|
-
openModal(model.value.control.add.dialog, model.value?.control?.add?.dialog?.
|
|
180
|
+
model.value.form.close = openModal(model.value.control.add.dialog, model.value?.control?.add?.dialog?.mode);
|
|
141
181
|
},
|
|
142
182
|
},
|
|
143
183
|
view: {
|
|
144
184
|
label: '查看', text: true, type: 'success',
|
|
145
185
|
dialog: {
|
|
146
186
|
title: '查看',
|
|
147
|
-
|
|
187
|
+
mode: "dialog",
|
|
148
188
|
slots: {
|
|
149
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
189
|
+
default: () => h(GForm, { modelValue: model.value }, { ...model.value.form.slots }),
|
|
150
190
|
},
|
|
151
191
|
},
|
|
152
192
|
auth: (row, column, $index) => {
|
|
153
193
|
return false;
|
|
154
194
|
},
|
|
155
195
|
click: (row, column, $index, m) => {
|
|
196
|
+
model.value.form = {
|
|
197
|
+
...model.value.form,
|
|
198
|
+
type: 'hidden',
|
|
199
|
+
disabled: true,
|
|
200
|
+
};
|
|
156
201
|
let w = model.value?.form?.style?.width || 600;
|
|
157
|
-
if (model.value.control.view.dialog.
|
|
202
|
+
if (model.value.control.view.dialog.mode == "drawer") {
|
|
158
203
|
model.value.control.view.dialog.size = convertToPx(w) + 45;
|
|
159
204
|
} else {
|
|
160
205
|
model.value.control.view.dialog.width = convertToPx(w) + 35;
|
|
161
206
|
}
|
|
162
|
-
model.value.form.disabled = true;
|
|
163
207
|
emits("view", row, column, $index, m);
|
|
164
208
|
Object.keys(row).forEach(key => {
|
|
165
209
|
if (model.value[key]) {
|
|
166
210
|
model.value[key].value = row[key];
|
|
167
211
|
}
|
|
168
212
|
});
|
|
169
|
-
openModal(model.value.control.view.dialog, model.value?.control?.view?.dialog?.
|
|
213
|
+
model.value.form.close = openModal(model.value.control.view.dialog, model.value?.control?.view?.dialog?.mode);
|
|
170
214
|
},
|
|
171
215
|
},
|
|
172
216
|
edit: {
|
|
173
217
|
label: '编辑', text: true, type: 'primary',
|
|
174
218
|
dialog: {
|
|
175
219
|
title: '编辑',
|
|
176
|
-
|
|
220
|
+
mode: "dialog",
|
|
177
221
|
slots: {
|
|
178
|
-
default: () =>
|
|
222
|
+
default: () => {
|
|
223
|
+
model.value.form.onUsubmit = () => {
|
|
224
|
+
if (closeModel.value)
|
|
225
|
+
closeModel.value();
|
|
226
|
+
}
|
|
227
|
+
return h(editModel, { modelValue: model.value, ...model.value.form }, { ...model.value.form.slots })
|
|
228
|
+
},
|
|
179
229
|
},
|
|
180
230
|
},
|
|
181
231
|
auth: (row, column, $index) => {
|
|
182
232
|
return true;
|
|
183
233
|
},
|
|
184
234
|
click: (row, column, $index, m) => {
|
|
185
|
-
model.value.form
|
|
235
|
+
model.value.form = {
|
|
236
|
+
...model.value.form,
|
|
237
|
+
type: 'hidden',
|
|
238
|
+
disabled: undefined,
|
|
239
|
+
};
|
|
186
240
|
let w = model.value?.form?.style?.width || 600;
|
|
187
|
-
if (model.value.control.edit.dialog.
|
|
241
|
+
if (model.value.control.edit.dialog.mode == "drawer") {
|
|
188
242
|
model.value.control.edit.dialog.size = convertToPx(w) + 45;
|
|
189
243
|
} else {
|
|
190
244
|
model.value.control.edit.dialog.width = convertToPx(w) + 35;
|
|
@@ -195,7 +249,7 @@ const defaultControl = {
|
|
|
195
249
|
model.value[key].value = row[key];
|
|
196
250
|
}
|
|
197
251
|
});
|
|
198
|
-
openModal(model.value?.control?.edit?.dialog, model.value?.control?.edit?.dialog?.
|
|
252
|
+
closeModel.value = openModal(model.value?.control?.edit?.dialog, model.value?.control?.edit?.dialog?.mode);
|
|
199
253
|
},
|
|
200
254
|
},
|
|
201
255
|
delete: {
|
|
@@ -212,6 +266,7 @@ const defaultControl = {
|
|
|
212
266
|
emits("delete", { row, column, $index }, (res) => {
|
|
213
267
|
if (res) {
|
|
214
268
|
model.value.table.data.splice($index, 1);
|
|
269
|
+
model.value.table.page.total--;
|
|
215
270
|
}
|
|
216
271
|
})
|
|
217
272
|
}).catch(() => { });
|
|
@@ -226,12 +281,24 @@ model.value.control = mergeObjects(
|
|
|
226
281
|
model.value?.control
|
|
227
282
|
);
|
|
228
283
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
width:
|
|
284
|
+
const defaultTableColumnsAttrs = {
|
|
285
|
+
type: "hidden",
|
|
286
|
+
width: 120,
|
|
287
|
+
align: "center",
|
|
288
|
+
formatter: (row, column, cellValue, index) => {
|
|
232
289
|
return cellValue;
|
|
233
290
|
}
|
|
234
|
-
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
model.value.tableColumns = mergeObjects(
|
|
294
|
+
defaultTableColumnsAttrs,
|
|
295
|
+
options?.tableColumns,
|
|
296
|
+
model.value?.tableColumns
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const initFormData = Object.fromEntries(Object.entries(filterObject(model.value, (k, val) => {
|
|
300
|
+
return (val.type != 'control' && val.edit !== false && val.type != 'hidden');
|
|
301
|
+
})).map(([key, val]) => [key, val.value]));
|
|
235
302
|
|
|
236
303
|
//扩展formatter方法
|
|
237
304
|
const formatterExtends = (item, str) => {
|
|
@@ -272,6 +339,17 @@ const formatterExtends = (item, str) => {
|
|
|
272
339
|
return trueValue
|
|
273
340
|
}
|
|
274
341
|
|
|
342
|
+
const reset = (q) => {
|
|
343
|
+
console.log(q);
|
|
344
|
+
queryInfo.value = q.value;
|
|
345
|
+
//model.value.table.load();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const query = (q) => {
|
|
349
|
+
queryInfo.value = q;
|
|
350
|
+
//model.value.table.load();
|
|
351
|
+
}
|
|
352
|
+
|
|
275
353
|
onMounted(() => {
|
|
276
354
|
|
|
277
355
|
});
|
|
@@ -290,6 +368,4 @@ const tableColumns = computed(() => Object.fromEntries(
|
|
|
290
368
|
return [key[0], key[1]]
|
|
291
369
|
}).sort((a, b) => a[1].sort - b[1].sort))
|
|
292
370
|
);
|
|
293
|
-
</script>
|
|
294
|
-
|
|
295
|
-
<style lang="scss" scoped></style>
|
|
371
|
+
</script>
|
|
@@ -1,34 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
declare
|
|
6
|
-
interface ComponentCustomProperties {
|
|
7
|
-
openModel: typeof openModal;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
1
|
+
import { Component, VNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
declare const ElementPlus: any; // 假设ElementPlus已经有了类型声明
|
|
4
|
+
declare const ElDialog: Component;
|
|
5
|
+
declare const ElDrawer: Component;
|
|
10
6
|
|
|
11
7
|
export interface GlobalOptions {
|
|
12
|
-
//
|
|
8
|
+
// 这里定义全局选项的类型
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
export interface OpenModalProps {
|
|
16
12
|
slots?: {
|
|
17
|
-
default?: () =>
|
|
18
|
-
header?: () =>
|
|
19
|
-
footer?: () =>
|
|
13
|
+
default?: (a: any) => VNode;
|
|
14
|
+
header?: (p: any) => VNode;
|
|
15
|
+
footer?: (p: any) => VNode;
|
|
20
16
|
};
|
|
21
|
-
//
|
|
17
|
+
// 其他props的类型定义
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
export function
|
|
25
|
-
|
|
20
|
+
export function openModal(props?: OpenModalProps, mode?: 'dialog' | 'drawer'): () => void;
|
|
21
|
+
|
|
26
22
|
export function convertToPx(value: string | number, referenceSize?: number): number;
|
|
27
|
-
export function mergeObjects<T extends object>(...objects: T[]): T;
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
export function mergeObjects<T>(...objects: T[]): T;
|
|
25
|
+
|
|
26
|
+
export function filterObject<T>(obj: T, filter?: (key: keyof T) => boolean): T;
|
|
27
|
+
|
|
28
|
+
export const components: {
|
|
30
29
|
gTable: Component;
|
|
31
30
|
gForm: Component;
|
|
31
|
+
// 其他组件的类型定义
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
declare const _default: {
|
|
35
|
+
install: (app: any, options?: GlobalOptions) => void;
|
|
36
|
+
};
|
|
37
|
+
export default _default;
|
package/src/components/index.js
CHANGED
|
@@ -22,18 +22,20 @@ export default {
|
|
|
22
22
|
app.config.globalProperties.openModel = openModal;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
const openModal = (props = {},
|
|
25
|
+
const openModal = (props = {}, mode = "dialog") => {
|
|
26
26
|
const el = document.createElement('div');
|
|
27
|
-
|
|
27
|
+
let closeModal = () => { };
|
|
28
|
+
const visible = ref(true);
|
|
29
|
+
let modalApp = createApp({
|
|
28
30
|
setup() {
|
|
29
|
-
|
|
30
|
-
const closeModal = () => {
|
|
31
|
+
closeModal = () => {
|
|
31
32
|
visible.value = false;
|
|
32
|
-
|
|
33
|
+
modalApp.unmount(); // 关闭对话框时卸载组件
|
|
33
34
|
delete el.remove(); // 关闭对话框时删除元素
|
|
35
|
+
modalApp = null;
|
|
34
36
|
};
|
|
35
37
|
provide("options", globalOptions);
|
|
36
|
-
return () => h(
|
|
38
|
+
return () => h(mode == "dialog" ? ElDialog : mode == "drawer" ? ElDrawer : null, {
|
|
37
39
|
modelValue: visible.value,
|
|
38
40
|
'onUpdate:modelValue': closeModal,
|
|
39
41
|
draggable: true,
|
|
@@ -41,18 +43,19 @@ const openModal = (props = {}, popMode = "dialog") => {
|
|
|
41
43
|
...props
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
|
-
default: props.slots?.default ? () => h('div', {}, props.slots.default()) : null,
|
|
45
|
-
header: props.slots?.header ? () => h('div', {}, props.slots.header()) : null,
|
|
46
|
-
footer: props.slots?.footer ? () => h('div', {}, props.slots.footer()) : null
|
|
46
|
+
default: props.slots?.default ? (a) => h('div', {}, props.slots.default(a)) : null,
|
|
47
|
+
header: props.slots?.header ? (p) => h('div', {}, props.slots.header(p)) : null,
|
|
48
|
+
footer: props.slots?.footer ? (p) => h('div', {}, props.slots.footer(p)) : null
|
|
47
49
|
}
|
|
48
50
|
);
|
|
49
51
|
}
|
|
50
52
|
});
|
|
51
53
|
for (const component of components) {
|
|
52
|
-
|
|
54
|
+
modalApp.component(component.name, component);
|
|
53
55
|
}
|
|
54
|
-
|
|
56
|
+
modalApp.use(ElementPlus).mount(el);
|
|
55
57
|
document.body.appendChild(el);
|
|
58
|
+
return closeModal;
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
function convertToPx(value, referenceSize = window.innerWidth) {
|
|
@@ -123,4 +126,18 @@ function mergeObjects(...objects) {
|
|
|
123
126
|
}
|
|
124
127
|
return result.value;
|
|
125
128
|
}
|
|
126
|
-
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
function filterObject(obj, filter = (key, value) => filter(key, value)) {
|
|
132
|
+
const result = {};
|
|
133
|
+
for (const key in obj) {
|
|
134
|
+
if (obj.hasOwnProperty(key)) {
|
|
135
|
+
if (filter(key, obj[key])) {
|
|
136
|
+
result[key] = obj[key];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { openModal, convertToPx, mergeObjects, filterObject };
|