vue-2024-ui 0.0.7 → 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 +4 -4
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/components/g-ctrl.vue +19 -11
- package/src/components/g-form.vue +68 -86
- package/src/components/g-query-item.vue +31 -0
- package/src/components/g-query.vue +217 -0
- package/src/components/g-table.vue +184 -80
- package/src/components/index.d.ts +31 -13
- package/src/components/index.js +114 -20
package/README.form.md
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
| --------- | ------------------------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------- |
|
|
11
11
|
| columns | 表单列数 | number | 2 |
|
|
12
12
|
| footer | 控制表单底部按钮是否显示和样式设置,hidden默认是false,style控制样式 | object | ```{hidden: false, style:{ display:"flex","align-items":"center",justifyContent:"center"}``` |
|
|
13
|
-
| submit | 表单提交按钮,增加了
|
|
14
|
-
| reset | 表单重置按钮,增加了
|
|
13
|
+
| submit | 表单提交按钮,增加了label,hidden 属性和click方法 | object | ``` {type: "primary",label: "提交",hidden: false,click: async (model) => {...}, } ``` |
|
|
14
|
+
| reset | 表单重置按钮,增加了label,hidden 属性和click方法 | object | ``` {type: "primary",label: "重置",hidden: false,click: () => {...}, } ``` |
|
|
15
15
|
| formItems | 通过style属性设置所有items的样式 | object | ``` style:{width:100%} ``` |
|
|
16
16
|
|
|
17
17
|
## form 增加的事件
|
|
@@ -131,8 +131,8 @@
|
|
|
131
131
|
``` javascript
|
|
132
132
|
|
|
133
133
|
<script setup>
|
|
134
|
-
import { ref
|
|
135
|
-
const formData=
|
|
134
|
+
import { ref} from "vue";
|
|
135
|
+
const formData=ref({
|
|
136
136
|
form:{
|
|
137
137
|
submit: { hidden: false },
|
|
138
138
|
reset: { hidden: false },
|
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,9 +1,11 @@
|
|
|
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
|
-
v-for="(item, key) of items" :key="key" v-bind="{ ...model
|
|
6
|
-
:style="{ ...model
|
|
7
|
+
v-for="(item, key) of items" :key="key" v-bind="{ ...model?.formItems, ...item?.formItem }"
|
|
8
|
+
:style="{ ...model?.formItems?.style, ...item?.formItem?.style }">
|
|
7
9
|
<slot :name="key" :item :model>
|
|
8
10
|
<g-ctrl v-model="model[key].value" :item :ctrlKey="key" :model class="g-ctrl-container">
|
|
9
11
|
<template v-for="(slot, slotKey, index) in $slots" :key="index" #[slotKey]="data">
|
|
@@ -15,41 +17,41 @@
|
|
|
15
17
|
<slot :name="`${key}-label`" :data :item :model>{{ item.label }}</slot>
|
|
16
18
|
</template>
|
|
17
19
|
</el-form-item>
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
-
</
|
|
20
|
+
<slot name="footer" v-if="!model.form.footer.hidden">
|
|
21
|
+
<div :style="model.form.footer.style">
|
|
22
|
+
<slot name="footer-left"></slot>
|
|
23
|
+
<el-button @click="model.form.submit.click(formInfo, model)"
|
|
24
|
+
v-bind="filterObject(model.form.submit, k => !['click'].includes(k))"
|
|
25
|
+
v-if="!model?.form?.submit?.hidden">
|
|
26
|
+
{{ model?.form?.submit?.label }}
|
|
27
|
+
</el-button>
|
|
28
|
+
<el-button @click="model.form.reset.click(model)"
|
|
29
|
+
v-bind="filterObject(model.form.reset, k => !['click'].includes(k))"
|
|
30
|
+
v-if="!model?.form?.reset?.hidden">{{
|
|
31
|
+
model?.form?.reset?.label
|
|
32
|
+
}}</el-button>
|
|
33
|
+
<slot name="footer-right"></slot>
|
|
34
|
+
</div>
|
|
35
|
+
</slot>
|
|
34
36
|
</slot>
|
|
35
37
|
</el-form>
|
|
36
38
|
</template>
|
|
37
39
|
|
|
38
40
|
<script setup>
|
|
39
|
-
import { computed, inject, onMounted, ref, useAttrs, watch } from "vue";
|
|
41
|
+
import { computed, getCurrentInstance, inject, onBeforeUnmount, onMounted, ref, useAttrs, watch } from "vue";
|
|
40
42
|
import gCtrl from "./g-ctrl.vue";
|
|
43
|
+
import { convertToPx, mergeObjects, filterObject } from "./index";
|
|
41
44
|
defineOptions({
|
|
42
45
|
name: "g-form",
|
|
43
46
|
inheritAttrs: false
|
|
44
47
|
})
|
|
45
48
|
const model = defineModel({ required: true });
|
|
46
|
-
const emits = defineEmits(["
|
|
49
|
+
const emits = defineEmits(["usubmit", "reset"]);
|
|
47
50
|
const gform = ref(null);
|
|
48
|
-
const columnsWidth = ref(0);
|
|
49
51
|
let marginRight = ref(32);
|
|
50
|
-
const options=inject('options');
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const options = inject('options');
|
|
53
|
+
|
|
54
|
+
const defaultFormAttrs = {
|
|
53
55
|
inline: true,
|
|
54
56
|
type: "hidden",
|
|
55
57
|
"scroll-to-error": true,
|
|
@@ -57,58 +59,43 @@ model.value.form = {
|
|
|
57
59
|
"status-icon": true,
|
|
58
60
|
"scroll-into-view-options": true,
|
|
59
61
|
columns: 2,
|
|
60
|
-
...options?.form,
|
|
61
|
-
...model.value.form,
|
|
62
62
|
style: {
|
|
63
63
|
width: "600px",
|
|
64
|
-
|
|
65
|
-
borderRadius: "10px",
|
|
66
|
-
padding: "0px",
|
|
67
|
-
paddingTop: "20px",
|
|
68
|
-
margin: "0px",
|
|
69
|
-
boxShadow: "0 0 5px rgba(0, 0, 0, 0.1)",
|
|
70
|
-
...options?.form?.style,
|
|
71
|
-
...model.value.form?.style,
|
|
72
|
-
...useAttrs().style
|
|
64
|
+
...useAttrs()?.style
|
|
73
65
|
},
|
|
74
66
|
// 表单底部属性
|
|
75
67
|
footer: {
|
|
76
68
|
hidden: false,
|
|
77
|
-
...options?.form?.footer,
|
|
78
|
-
...model.value.form?.footer,
|
|
79
69
|
style: {
|
|
80
|
-
display: "flex", width: "100%", "align-items": "center", justifyContent: "center", margin: "5px",
|
|
81
|
-
...options?.form?.footer?.style,
|
|
82
|
-
...model.value.form?.footer?.style
|
|
70
|
+
display: "flex", width: "100%", "align-items": "center", justifyContent: "center", margin: "5px",
|
|
83
71
|
},
|
|
84
72
|
},
|
|
85
73
|
//表单提交按钮属性
|
|
86
74
|
submit: {
|
|
87
75
|
type: "primary",
|
|
88
76
|
disabled: false,
|
|
89
|
-
|
|
77
|
+
label: "提交",
|
|
90
78
|
plain: true,
|
|
91
79
|
hidden: false,
|
|
92
|
-
|
|
93
|
-
|
|
80
|
+
click: async (formInfo, model) => {
|
|
81
|
+
emits("usubmit", formInfo, model);
|
|
94
82
|
if (!gform.value) return
|
|
95
83
|
await gform.value.validate((valid, fields) => {
|
|
96
84
|
if (valid) {
|
|
97
|
-
|
|
85
|
+
console.log("提交");
|
|
98
86
|
} else {
|
|
99
|
-
console.log('
|
|
87
|
+
console.log('表单验证不通过!', fields);
|
|
100
88
|
}
|
|
101
89
|
})
|
|
102
90
|
},
|
|
103
|
-
...model.value.form?.submit
|
|
104
91
|
},
|
|
105
92
|
//表单重置按钮属性
|
|
106
93
|
reset: {
|
|
107
94
|
disabled: false,
|
|
108
|
-
|
|
95
|
+
label: "重置",
|
|
109
96
|
hidden: false,
|
|
110
97
|
plain: true,
|
|
111
|
-
|
|
98
|
+
click: () => {
|
|
112
99
|
if (!gform.value) return
|
|
113
100
|
let isreset = true;
|
|
114
101
|
emits("reset", (flag) => {
|
|
@@ -118,25 +105,25 @@ model.value.form = {
|
|
|
118
105
|
gform.value.resetFields();
|
|
119
106
|
}
|
|
120
107
|
},
|
|
121
|
-
...options?.form?.reset,
|
|
122
|
-
...model.value.form?.reset
|
|
123
108
|
},
|
|
124
|
-
|
|
125
|
-
width: 635,
|
|
126
|
-
...options?.form?.dialog,
|
|
127
|
-
...model.value.form?.dialog
|
|
128
|
-
}
|
|
109
|
+
slots: {}
|
|
129
110
|
};
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
model.value.form = mergeObjects(
|
|
112
|
+
defaultFormAttrs,
|
|
113
|
+
options?.form,
|
|
114
|
+
model.value?.form
|
|
115
|
+
);
|
|
116
|
+
const defaultFormItemsAttrs = {
|
|
132
117
|
type: "hidden",
|
|
133
118
|
style: {
|
|
134
119
|
width: "100%",
|
|
135
|
-
|
|
136
|
-
...model.value.formItems?.style
|
|
137
|
-
},
|
|
138
|
-
...model.value.formItems,
|
|
120
|
+
}
|
|
139
121
|
};
|
|
122
|
+
model.value.formItems = mergeObjects(
|
|
123
|
+
defaultFormItemsAttrs,
|
|
124
|
+
options?.formItems,
|
|
125
|
+
model?.formItems
|
|
126
|
+
);
|
|
140
127
|
|
|
141
128
|
//获取表单项验证规则
|
|
142
129
|
const getItemRulesByItem = (item) => {
|
|
@@ -231,40 +218,35 @@ const load = () => {
|
|
|
231
218
|
if (!model.value.form.inline) {
|
|
232
219
|
model.value.form.columns = 1;
|
|
233
220
|
}
|
|
234
|
-
if (model.value.form.style.width) {
|
|
235
|
-
columnsWidth.value = removePx(model.value.form.style.width) / model.value.form.columns;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
model.value.form.style.width = "600px";
|
|
239
|
-
}
|
|
240
|
-
model.value.form.dialog.width = +model.value?.form?.style?.width.replace("px", "") + 35 + "px";
|
|
241
221
|
for (const [key, value] of Object.entries(items.value)) {
|
|
242
|
-
model.value[key].formItem = { ...model.value[key].formItem, ...value.formItem }
|
|
222
|
+
model.value[key].formItem = { ...model.value[key].formItem, ...value.formItem };
|
|
243
223
|
model.value[key].formItem.style = { ...model.value[key].formItem.style, ...value.formItem.style };
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
model.value[key].formItem.style.width = `calc(${columnsWidth.value - marginRight.value}px)`;
|
|
250
|
-
}
|
|
251
|
-
} else {
|
|
252
|
-
model.value[key].formItem.style.width = `calc(${columnsWidth.value - marginRight.value}px)`;
|
|
253
|
-
}
|
|
224
|
+
|
|
225
|
+
const columnWidth = value.columns && value.columns > 1 ?
|
|
226
|
+
columnsWidth.value * value.columns - (value.columns - 1) * marginRight.value :
|
|
227
|
+
columnsWidth.value - marginRight.value;
|
|
228
|
+
model.value[key].formItem.style.width = `calc(${columnWidth}px)`;
|
|
254
229
|
}
|
|
255
|
-
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
onBeforeUnmount(() => {
|
|
233
|
+
console.log('destroy');
|
|
234
|
+
})
|
|
256
235
|
|
|
257
236
|
watch(() => model.value.form.columns, () => load());
|
|
258
237
|
watch(() => model.value.form.inline, () => load());
|
|
259
238
|
|
|
260
|
-
const
|
|
239
|
+
const columnsWidth = computed(() => convertToPx(model.value.form.style.width) / model.value.form.columns);
|
|
261
240
|
//表单项
|
|
262
|
-
const items = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
241
|
+
const items = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
242
|
+
.filter(a => a[1].type != "hidden")
|
|
263
243
|
.filter(a => (a[1].edit !== false))
|
|
264
244
|
.filter(a => (a[1].type !== "control"))
|
|
265
245
|
.map(key => [key[0], key[1]])));
|
|
266
246
|
//表单提交对象
|
|
267
|
-
const formInfo = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
268
|
-
|
|
269
|
-
|
|
247
|
+
const formInfo = computed(() => Object.fromEntries(Object.entries(model.value)
|
|
248
|
+
.filter(a => a[1].type != "hidden")
|
|
249
|
+
.filter(a => a[1].type != "control")
|
|
250
|
+
.map(key => [key[0], key[1].value])));
|
|
251
|
+
|
|
270
252
|
</script>
|
|
@@ -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>
|
|
@@ -16,7 +40,7 @@
|
|
|
16
40
|
<slot name="control-view" :row :column :$index
|
|
17
41
|
v-if="model.control.view.auth(row, column, $index)">
|
|
18
42
|
<el-button v-bind="model.control.view"
|
|
19
|
-
@click="model.control.view.
|
|
43
|
+
@click="model.control.view.click(row, column, $index, model)">{{
|
|
20
44
|
model.control.view.label
|
|
21
45
|
}}</el-button>
|
|
22
46
|
</slot>
|
|
@@ -24,7 +48,7 @@
|
|
|
24
48
|
<slot name="control-edit" :row :column :$index
|
|
25
49
|
v-if="model.control.edit.auth(row, column, $index)">
|
|
26
50
|
<el-button v-bind="model.control.edit"
|
|
27
|
-
@click="model.control.edit.
|
|
51
|
+
@click="model.control.edit.click(row, column, $index, model)">{{
|
|
28
52
|
model.control.edit.label
|
|
29
53
|
}}</el-button>
|
|
30
54
|
</slot>
|
|
@@ -32,7 +56,7 @@
|
|
|
32
56
|
<slot name="control-delete" :row :column :$index
|
|
33
57
|
v-if="model.control.delete.auth(row, column, $index)">
|
|
34
58
|
<el-button v-bind="model.control.delete"
|
|
35
|
-
@click="model.control.delete.
|
|
59
|
+
@click="model.control.delete.click(row, column, $index)">{{
|
|
36
60
|
model.control.delete.label }}</el-button>
|
|
37
61
|
</slot>
|
|
38
62
|
<slot name="control-right" :row :column :$index> </slot>
|
|
@@ -65,148 +89,216 @@
|
|
|
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 = {
|
|
83
110
|
data: [],
|
|
84
111
|
border: true,
|
|
85
112
|
stripe: true,
|
|
86
113
|
emptyText: '暂无数据',
|
|
87
114
|
fit: false,
|
|
88
115
|
type: "hidden",
|
|
89
|
-
height: "calc(100vh -
|
|
90
|
-
...options?.table,
|
|
91
|
-
...model.value.table,
|
|
116
|
+
height: "calc(100vh - 65px)",
|
|
92
117
|
load: () => {
|
|
93
118
|
let { currentPage, pageSize } = model.value.table.page;
|
|
94
|
-
emits('load', { currentPage, pageSize }, (data, total) => {
|
|
119
|
+
emits('load', { currentPage, pageSize, ...queryInfo.value }, (data, total) => {
|
|
95
120
|
model.value.table.data = data;
|
|
96
121
|
model.value.table.page.total = total;
|
|
97
122
|
});
|
|
98
123
|
},
|
|
99
124
|
page: {
|
|
100
|
-
background:
|
|
125
|
+
background: "red",
|
|
101
126
|
total: 0,
|
|
102
127
|
pageSize: 20,
|
|
103
128
|
currentPage: 1,
|
|
104
129
|
pageSizes: [5, 10, 20, 30, 40, 50],
|
|
105
130
|
layout: 'total, sizes, prev, pager, next, jumper',
|
|
106
|
-
...options?.table?.page,
|
|
107
|
-
...model.value.table?.page,
|
|
108
131
|
style: {
|
|
109
132
|
display: 'flex',
|
|
110
133
|
justifyContent: 'flex-end',
|
|
111
134
|
padding: "10px",
|
|
112
|
-
...model.value.table?.page?.style
|
|
113
135
|
},
|
|
114
136
|
}
|
|
115
|
-
}
|
|
137
|
+
};
|
|
138
|
+
model.value.table = mergeObjects(
|
|
139
|
+
defaultTableAttrs,
|
|
140
|
+
options?.table,
|
|
141
|
+
model.value.table
|
|
142
|
+
);
|
|
116
143
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
dialog: {
|
|
122
|
-
width: 635,
|
|
123
|
-
...model.value.form?.dialog,
|
|
124
|
-
style: {
|
|
125
|
-
...model.value.form?.dialog?.style
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
model.value.control = {
|
|
130
|
-
type: 'control', label: '操作', edit: false, width: 250, fixed: 'right',...options?.control, ...model.value?.control,
|
|
144
|
+
let closeModel = ref(() => { });
|
|
145
|
+
const editModel = defineAsyncComponent(() => import('./g-form.vue'));
|
|
146
|
+
const defaultControl = {
|
|
147
|
+
type: 'control', label: '操作', edit: false, width: 250, fixed: 'right',
|
|
131
148
|
style: { ...model.value?.control?.style },
|
|
149
|
+
add: {
|
|
150
|
+
label: '新增', text: false, type: 'primary',
|
|
151
|
+
style: {
|
|
152
|
+
marginLeft: "10px",
|
|
153
|
+
},
|
|
154
|
+
dialog: {
|
|
155
|
+
title: '新增',
|
|
156
|
+
mode: "drawer",
|
|
157
|
+
slots: {
|
|
158
|
+
default: () => h(GForm, { modelValue: model.value }, { ...model.value.form.slots }),
|
|
159
|
+
},
|
|
160
|
+
},
|
|
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 => {
|
|
176
|
+
if (model.value[key]) {
|
|
177
|
+
model.value[key].value = initFormData[key];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
model.value.form.close = openModal(model.value.control.add.dialog, model.value?.control?.add?.dialog?.mode);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
132
183
|
view: {
|
|
133
184
|
label: '查看', text: true, type: 'success',
|
|
185
|
+
dialog: {
|
|
186
|
+
title: '查看',
|
|
187
|
+
mode: "dialog",
|
|
188
|
+
slots: {
|
|
189
|
+
default: () => h(GForm, { modelValue: model.value }, { ...model.value.form.slots }),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
134
192
|
auth: (row, column, $index) => {
|
|
135
193
|
return false;
|
|
136
194
|
},
|
|
137
|
-
|
|
138
|
-
model.value.form
|
|
195
|
+
click: (row, column, $index, m) => {
|
|
196
|
+
model.value.form = {
|
|
197
|
+
...model.value.form,
|
|
198
|
+
type: 'hidden',
|
|
199
|
+
disabled: true,
|
|
200
|
+
};
|
|
201
|
+
let w = model.value?.form?.style?.width || 600;
|
|
202
|
+
if (model.value.control.view.dialog.mode == "drawer") {
|
|
203
|
+
model.value.control.view.dialog.size = convertToPx(w) + 45;
|
|
204
|
+
} else {
|
|
205
|
+
model.value.control.view.dialog.width = convertToPx(w) + 35;
|
|
206
|
+
}
|
|
139
207
|
emits("view", row, column, $index, m);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
...model.value.form.dialog,
|
|
145
|
-
slots: {
|
|
146
|
-
// 自定义插槽内容
|
|
147
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
148
|
-
...model.value?.form?.dialog?.slots
|
|
149
|
-
},
|
|
208
|
+
Object.keys(row).forEach(key => {
|
|
209
|
+
if (model.value[key]) {
|
|
210
|
+
model.value[key].value = row[key];
|
|
211
|
+
}
|
|
150
212
|
});
|
|
213
|
+
model.value.form.close = openModal(model.value.control.view.dialog, model.value?.control?.view?.dialog?.mode);
|
|
151
214
|
},
|
|
152
|
-
...options?.control?.view,
|
|
153
|
-
...model.value?.control?.view
|
|
154
215
|
},
|
|
155
216
|
edit: {
|
|
156
217
|
label: '编辑', text: true, type: 'primary',
|
|
218
|
+
dialog: {
|
|
219
|
+
title: '编辑',
|
|
220
|
+
mode: "dialog",
|
|
221
|
+
slots: {
|
|
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
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
157
231
|
auth: (row, column, $index) => {
|
|
158
232
|
return true;
|
|
159
233
|
},
|
|
160
|
-
|
|
161
|
-
model.value.form
|
|
234
|
+
click: (row, column, $index, m) => {
|
|
235
|
+
model.value.form = {
|
|
236
|
+
...model.value.form,
|
|
237
|
+
type: 'hidden',
|
|
238
|
+
disabled: undefined,
|
|
239
|
+
};
|
|
240
|
+
let w = model.value?.form?.style?.width || 600;
|
|
241
|
+
if (model.value.control.edit.dialog.mode == "drawer") {
|
|
242
|
+
model.value.control.edit.dialog.size = convertToPx(w) + 45;
|
|
243
|
+
} else {
|
|
244
|
+
model.value.control.edit.dialog.width = convertToPx(w) + 35;
|
|
245
|
+
}
|
|
162
246
|
emits("edit", row, column, $index, m);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...model.value.form.dialog,
|
|
168
|
-
slots: {
|
|
169
|
-
// 自定义插槽内容
|
|
170
|
-
default: () => h(GForm, { modelValue: model.value }),
|
|
171
|
-
...model.value?.form?.dialog?.slots
|
|
172
|
-
},
|
|
247
|
+
Object.keys(row).forEach(key => {
|
|
248
|
+
if (model.value[key]) {
|
|
249
|
+
model.value[key].value = row[key];
|
|
250
|
+
}
|
|
173
251
|
});
|
|
174
|
-
|
|
175
|
-
},
|
|
176
|
-
...options?.control?.edit,
|
|
177
|
-
...model.value?.control?.edit
|
|
252
|
+
closeModel.value = openModal(model.value?.control?.edit?.dialog, model.value?.control?.edit?.dialog?.mode);
|
|
253
|
+
},
|
|
178
254
|
},
|
|
179
255
|
delete: {
|
|
180
256
|
text: true, label: '删除', type: 'danger',
|
|
181
257
|
auth: (row, column, $index) => {
|
|
182
258
|
return true;
|
|
183
259
|
},
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
260
|
+
dialog: {
|
|
261
|
+
message: '此操作将永久删除该文件, 是否继续?', title: '提示', options: { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
|
262
|
+
},
|
|
263
|
+
click: (row, column, $index) => {
|
|
264
|
+
let { message, title, options } = model.value.control.delete.dialog;
|
|
265
|
+
ElMessageBox.confirm(message, title, options).then(() => {
|
|
190
266
|
emits("delete", { row, column, $index }, (res) => {
|
|
191
267
|
if (res) {
|
|
192
268
|
model.value.table.data.splice($index, 1);
|
|
193
|
-
|
|
269
|
+
model.value.table.page.total--;
|
|
194
270
|
}
|
|
195
271
|
})
|
|
196
272
|
}).catch(() => { });
|
|
197
273
|
|
|
198
|
-
},
|
|
199
|
-
...options?.control?.delete,
|
|
200
|
-
...model.value?.control?.delete
|
|
274
|
+
},
|
|
201
275
|
},
|
|
202
276
|
};
|
|
203
277
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
278
|
+
model.value.control = mergeObjects(
|
|
279
|
+
defaultControl,
|
|
280
|
+
options?.control,
|
|
281
|
+
model.value?.control
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const defaultTableColumnsAttrs = {
|
|
285
|
+
type: "hidden",
|
|
286
|
+
width: 120,
|
|
287
|
+
align: "center",
|
|
288
|
+
formatter: (row, column, cellValue, index) => {
|
|
207
289
|
return cellValue;
|
|
208
290
|
}
|
|
209
|
-
}
|
|
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]));
|
|
210
302
|
|
|
211
303
|
//扩展formatter方法
|
|
212
304
|
const formatterExtends = (item, str) => {
|
|
@@ -247,6 +339,17 @@ const formatterExtends = (item, str) => {
|
|
|
247
339
|
return trueValue
|
|
248
340
|
}
|
|
249
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
|
+
|
|
250
353
|
onMounted(() => {
|
|
251
354
|
|
|
252
355
|
});
|
|
@@ -260,8 +363,9 @@ const tableColumns = computed(() => Object.fromEntries(
|
|
|
260
363
|
Object.entries(model.value)
|
|
261
364
|
.filter(a => a[1].type != "hidden")
|
|
262
365
|
.filter(a => a[1].visible !== false)
|
|
263
|
-
.map(key =>
|
|
366
|
+
.map(key => {
|
|
367
|
+
key[1].sort = key[1].sort || 0;
|
|
368
|
+
return [key[0], key[1]]
|
|
369
|
+
}).sort((a, b) => a[1].sort - b[1].sort))
|
|
264
370
|
);
|
|
265
|
-
</script>
|
|
266
|
-
|
|
267
|
-
<style lang="scss" scoped></style>
|
|
371
|
+
</script>
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { Component, VNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
declare const ElementPlus: any; // 假设ElementPlus已经有了类型声明
|
|
4
|
+
declare const ElDialog: Component;
|
|
5
|
+
declare const ElDrawer: Component;
|
|
6
|
+
|
|
7
|
+
export interface GlobalOptions {
|
|
8
|
+
// 这里定义全局选项的类型
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface OpenModalProps {
|
|
12
|
+
slots?: {
|
|
13
|
+
default?: (a: any) => VNode;
|
|
14
|
+
header?: (p: any) => VNode;
|
|
15
|
+
footer?: (p: any) => VNode;
|
|
9
16
|
};
|
|
10
|
-
|
|
17
|
+
// 其他props的类型定义
|
|
11
18
|
}
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
export function openModal(props?: OpenModalProps, mode?: 'dialog' | 'drawer'): () => void;
|
|
21
|
+
|
|
22
|
+
export function convertToPx(value: string | number, referenceSize?: number): number;
|
|
23
|
+
|
|
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: {
|
|
29
|
+
gTable: Component;
|
|
30
|
+
gForm: Component;
|
|
31
|
+
// 其他组件的类型定义
|
|
15
32
|
};
|
|
16
33
|
|
|
34
|
+
declare const _default: {
|
|
35
|
+
install: (app: any, options?: GlobalOptions) => void;
|
|
36
|
+
};
|
|
17
37
|
export default _default;
|
|
18
|
-
|
|
19
|
-
export declare const openModal: (props?: ModalProps, comType?: 'dialog' | 'drawer') => void;
|
package/src/components/index.js
CHANGED
|
@@ -4,46 +4,140 @@ import ElementPlus from 'element-plus'
|
|
|
4
4
|
import { ElDialog, ElDrawer } from "element-plus";
|
|
5
5
|
import gTable from "./g-table.vue";
|
|
6
6
|
import gForm from "./g-form.vue";
|
|
7
|
-
import { createApp, h, provide,
|
|
8
|
-
const
|
|
7
|
+
import { createApp, h, provide, readonly, ref } from "vue";
|
|
8
|
+
const globalOptions = {};
|
|
9
|
+
|
|
10
|
+
const components = [
|
|
11
|
+
gTable,
|
|
12
|
+
gForm
|
|
13
|
+
]
|
|
9
14
|
|
|
10
15
|
export default {
|
|
11
16
|
install(app, options = {}) {
|
|
12
|
-
Object.assign(
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
Object.assign(globalOptions, options);
|
|
18
|
+
for (const component of components) {
|
|
19
|
+
app.component(component.name, component);
|
|
20
|
+
}
|
|
15
21
|
app.provide("options", readonly(options));
|
|
22
|
+
app.config.globalProperties.openModel = openModal;
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
|
-
const openModal = (props = {},
|
|
25
|
+
const openModal = (props = {}, mode = "dialog") => {
|
|
19
26
|
const el = document.createElement('div');
|
|
20
|
-
|
|
27
|
+
let closeModal = () => { };
|
|
28
|
+
const visible = ref(true);
|
|
29
|
+
let modalApp = createApp({
|
|
21
30
|
setup() {
|
|
22
|
-
|
|
23
|
-
const closeModal = () => {
|
|
31
|
+
closeModal = () => {
|
|
24
32
|
visible.value = false;
|
|
25
|
-
|
|
33
|
+
modalApp.unmount(); // 关闭对话框时卸载组件
|
|
26
34
|
delete el.remove(); // 关闭对话框时删除元素
|
|
35
|
+
modalApp = null;
|
|
27
36
|
};
|
|
28
|
-
provide("options",
|
|
29
|
-
return () => h(
|
|
37
|
+
provide("options", globalOptions);
|
|
38
|
+
return () => h(mode == "dialog" ? ElDialog : mode == "drawer" ? ElDrawer : null, {
|
|
30
39
|
modelValue: visible.value,
|
|
31
40
|
'onUpdate:modelValue': closeModal,
|
|
32
41
|
draggable: true,
|
|
33
42
|
"append-to-body": true,
|
|
34
|
-
"v-if": visible.value,
|
|
35
43
|
...props
|
|
36
44
|
},
|
|
37
|
-
|
|
38
|
-
props.slots
|
|
39
|
-
props.slots
|
|
40
|
-
props.slots
|
|
41
|
-
|
|
45
|
+
{
|
|
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
|
|
49
|
+
}
|
|
42
50
|
);
|
|
43
51
|
}
|
|
44
52
|
});
|
|
45
|
-
|
|
53
|
+
for (const component of components) {
|
|
54
|
+
modalApp.component(component.name, component);
|
|
55
|
+
}
|
|
56
|
+
modalApp.use(ElementPlus).mount(el);
|
|
46
57
|
document.body.appendChild(el);
|
|
58
|
+
return closeModal;
|
|
47
59
|
};
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
function convertToPx(value, referenceSize = window.innerWidth) {
|
|
62
|
+
// 如果已经是px,直接返回
|
|
63
|
+
if (typeof value === 'number') {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (value.endsWith('px')) {
|
|
67
|
+
return parseFloat(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 处理vh和vw
|
|
71
|
+
const viewportHeight = window.innerHeight;
|
|
72
|
+
const viewportWidth = window.innerWidth;
|
|
73
|
+
if (value.endsWith('vh')) {
|
|
74
|
+
return (viewportHeight * parseFloat(value) / 100);
|
|
75
|
+
}
|
|
76
|
+
if (value.endsWith('vw')) {
|
|
77
|
+
return (viewportWidth * parseFloat(value) / 100);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 处理%值,需要提供参考大小
|
|
81
|
+
if (value.endsWith('%')) {
|
|
82
|
+
if (typeof referenceSize !== 'number') {
|
|
83
|
+
throw new Error('Reference size is required to convert % to px');
|
|
84
|
+
}
|
|
85
|
+
return (referenceSize * parseFloat(value) / 100);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 处理calc表达式
|
|
89
|
+
if (value.startsWith('calc(') && value.endsWith(')')) {
|
|
90
|
+
const calcExpr = value.slice(5, -1).trim();
|
|
91
|
+
// 使用一个安全的eval替代方案来解析calc表达式
|
|
92
|
+
function safeEval(expr) {
|
|
93
|
+
// 只允许数字、加减乘除运算符、小括号和空格
|
|
94
|
+
if (/[0-9+\-*/().\s]+$/.test(expr)) {
|
|
95
|
+
return new Function('return ' + expr)();
|
|
96
|
+
} else {
|
|
97
|
+
throw new Error('Invalid calc expression');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 在这里,我们假设calc表达式中可能包含其他单位,因此递归调用parseValueToPx
|
|
101
|
+
const result = safeEval(calcExpr.replace(/([0-9.]+)([a-z%]+)/g, (match, number, unit) => {
|
|
102
|
+
return convertToPx(number + unit, referenceSize);
|
|
103
|
+
}));
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 如果无法识别单位,抛出错误
|
|
108
|
+
throw new Error('Unsupported unit in value: ' + value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
function mergeObjects(...objects) {
|
|
113
|
+
const result = ref({});
|
|
114
|
+
for (const obj of objects) {
|
|
115
|
+
if (obj && typeof obj === 'object') {
|
|
116
|
+
for (const key in obj) {
|
|
117
|
+
if (obj.hasOwnProperty(key)) {
|
|
118
|
+
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
|
|
119
|
+
result.value[key] = mergeObjects(result.value[key], obj[key]);
|
|
120
|
+
} else {
|
|
121
|
+
result.value[key] = obj[key];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result.value;
|
|
128
|
+
}
|
|
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 };
|