Skip to content

Commit 47650a8

Browse files
committed
添加表单底部操作按钮自定义 #43
1 parent c8304fa commit 47650a8

File tree

12 files changed

+384
-61
lines changed

12 files changed

+384
-61
lines changed

public/0.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/5.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/app.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/mix-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"/app.js": "/app.js?id=eb51d460047b5cee6b48",
2+
"/app.js": "/app.js?id=bd89b077f08d1b3fe3c8",
33
"/manifest.js": "/manifest.js?id=8991394a854ee5cdffc3",
44
"/vendor.js": "/vendor.js?id=df0be4950fcb717193ba"
55
}

resources/js/components/Root.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@
152152
</el-footer>
153153
</el-container>
154154
</el-container>
155-
<el-backtop></el-backtop>
155+
<el-backtop :bottom="80"></el-backtop>
156156
<el-drawer :visible.sync="showAdminSet" size="250px">
157157
<div style="padding:0 10px;">
158158
<el-divider>主题风格</el-divider>

resources/js/components/form/Form.vue

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<template>
22
<div class="form-page">
3-
<component v-if="attrs.top" :is="attrs.top.componentName" :attrs="attrs.top" />
3+
<component
4+
v-if="attrs.top"
5+
:is="attrs.top.componentName"
6+
:attrs="attrs.top"
7+
/>
48
<component
59
:is="attrs.attrs.isDialog ? 'div' : 'el-card'"
610
shadow="never"
@@ -56,9 +60,7 @@
5660
:size="item.size"
5761
>
5862
<span slot="label" v-if="!item.hideLabel">
59-
{{
60-
item.label
61-
}}
63+
{{ item.label }}
6264
</span>
6365
<template>
6466
<el-col :span="item.inputWidth">
@@ -81,7 +83,11 @@
8183
/>
8284
</template>
8385

84-
<div v-if="item.help" class="form-item-help" v-html="item.help"></div>
86+
<div
87+
v-if="item.help"
88+
class="form-item-help"
89+
v-html="item.help"
90+
></div>
8591
</el-col>
8692
</template>
8793
</el-form-item>
@@ -94,54 +100,90 @@
94100
</template>
95101
</component>
96102
</component>
97-
<div class="form-bottom-actions">
98-
<div></div>
99-
<div>
100-
<el-button
101-
v-if="!attrs.attrs.isDialog"
102-
class="submit-btn"
103-
@click="$router.go(-1)"
104-
:style="{ width: attrs.attrs.buttonWidth }"
105-
>{{ attrs.attrs.backButtonName }}</el-button>
106-
<el-button
107-
v-else
108-
class="submit-btn"
109-
@click="closeDialog"
110-
:style="{ width: attrs.attrs.buttonWidth }"
111-
>{{ attrs.attrs.backButtonName }}</el-button>
112-
<el-button
113-
:loading="loading"
114-
class="submit-btn"
115-
type="primary"
116-
:style="{ width: attrs.attrs.buttonWidth }"
117-
@click="submitForm(attrs.ref || 'form')"
118-
>
119-
{{
120-
isEdit
121-
? attrs.attrs.updateButtonName
122-
: attrs.attrs.createButtonName
123-
}}
124-
</el-button>
103+
<component :is="attrs.actions.fixed?'Affix':'div'" :offset-bottom="2">
104+
<div
105+
class="form-bottom-actions flex padding-tb"
106+
:class="{ 'form-bottom-actions-fixedxxx': attrs.actions.fixed }"
107+
>
108+
<div>
109+
<component
110+
v-for="(component, index) in attrs.actions.addLeftActions"
111+
:key="component.componentName + index"
112+
:is="component.componentName"
113+
:attrs="component"
114+
/>
115+
</div>
116+
<div class="flex">
117+
<component
118+
v-for="(component, index) in attrs.actions.addRightActions"
119+
:key="component.componentName + index"
120+
:is="component.componentName"
121+
:attrs="component"
122+
/>
123+
<el-button
124+
v-if="attrs.actions.cancelButton"
125+
:style="attrs.actions.cancelButton.style"
126+
:class="attrs.actions.cancelButton.className"
127+
:type="attrs.actions.cancelButton.type"
128+
:size="attrs.actions.cancelButton.size"
129+
:plain="attrs.actions.cancelButton.plain"
130+
:round="attrs.actions.cancelButton.round"
131+
:circle="attrs.actions.cancelButton.circle"
132+
:disabled="attrs.actions.cancelButton.disabled"
133+
:icon="attrs.actions.cancelButton.icon"
134+
:autofocus="attrs.actions.cancelButton.autofocus"
135+
:loading="loading"
136+
@click="attrs.attrs.isDialog ? closeDialog : $router.go(-1)"
137+
><template v-if="attrs.actions.cancelButton.content">{{
138+
attrs.actions.cancelButton.content
139+
}}</template>
140+
</el-button>
141+
142+
<el-button
143+
v-if="attrs.actions.submitButton"
144+
:style="attrs.actions.submitButton.style"
145+
:class="attrs.actions.submitButton.className"
146+
:type="attrs.actions.submitButton.type"
147+
:size="attrs.actions.submitButton.size"
148+
:plain="attrs.actions.submitButton.plain"
149+
:round="attrs.actions.submitButton.round"
150+
:circle="attrs.actions.submitButton.circle"
151+
:disabled="attrs.actions.submitButton.disabled"
152+
:icon="attrs.actions.submitButton.icon"
153+
:autofocus="attrs.actions.submitButton.autofocus"
154+
:loading="loading"
155+
@click="submitForm(attrs.ref || 'form')"
156+
><template v-if="attrs.actions.submitButton.content">{{
157+
attrs.actions.submitButton.content
158+
}}</template>
159+
</el-button>
160+
</div>
125161
</div>
126-
</div>
162+
</component>
127163
</el-form>
128164
</component>
129-
<component v-if="attrs.bottom" :is="attrs.bottom.componentName" :attrs="attrs.bottom" />
165+
<component
166+
v-if="attrs.bottom"
167+
:is="attrs.bottom.componentName"
168+
:attrs="attrs.bottom"
169+
/>
130170
</div>
131171
</template>
132172
<script>
133173
import { BaseComponent } from "@/mixins.js";
134174
import ItemDiaplsy from "./ItemDiaplsy";
135175
import ItemIf from "./ItemIf";
136176
import { isNull } from "../../utils";
177+
import Affix from "../widgets/common/affix";
137178
export default {
138179
mixins: [BaseComponent],
139180
components: {
140181
ItemDiaplsy,
141-
ItemIf
182+
ItemIf,
183+
Affix,
142184
},
143185
props: {
144-
attrs: Object
186+
attrs: Object,
145187
},
146188
computed: {
147189
isEdit() {
@@ -150,17 +192,17 @@ export default {
150192
ignoreKey() {
151193
return this._.map(
152194
this.attrs.formItems.filter(
153-
e => !e.ignoreEmpty || !isNull(this.formData[e.prop])
195+
(e) => !e.ignoreEmpty || !isNull(this.formData[e.prop])
154196
),
155197
"prop"
156198
);
157-
}
199+
},
158200
},
159201
data() {
160202
return {
161203
loading: false,
162204
init: false,
163-
formData: null
205+
formData: null,
164206
};
165207
},
166208
mounted() {
@@ -185,8 +227,8 @@ export default {
185227
this.$http
186228
.get(this.attrs.dataUrl, {
187229
params: {
188-
get_data: true
189-
}
230+
get_data: true,
231+
},
190232
})
191233
.then(({ data }) => {
192234
this.formData = data;
@@ -202,7 +244,7 @@ export default {
202244
});
203245
},
204246
submitForm(formName) {
205-
this.$refs[formName].validate(valid => {
247+
this.$refs[formName].validate((valid) => {
206248
if (valid) {
207249
this.loading = true;
208250
const formatData = this._.pick(this.formData, this.ignoreKey);
@@ -259,8 +301,8 @@ export default {
259301
},
260302
closeDialog() {
261303
this.$bus.emit("showDialogGridFrom", { isShow: false });
262-
}
263-
}
304+
},
305+
},
264306
};
265307
</script>
266308
<style lang="scss">
@@ -276,6 +318,14 @@ export default {
276318
align-items: center;
277319
justify-content: space-between;
278320
}
321+
.form-bottom-actions-fixed {
322+
position: fixed;
323+
bottom: 15px;
324+
left: 0;
325+
right: 0;
326+
padding: 15px;
327+
background: #ffffff;
328+
}
279329
.form-item-help {
280330
color: #999;
281331
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<template>
2+
<div>
3+
<div ref="point" :class="classes" :style="styles">
4+
<slot></slot>
5+
</div>
6+
<div v-show="slot" :style="slotStyle"></div>
7+
</div>
8+
</template>
9+
<script>
10+
import { on, off } from '@/util/dom';
11+
const prefixCls = 'admin-affix';
12+
function getScroll(target, top) {
13+
const prop = top ? 'pageYOffset' : 'pageXOffset';
14+
const method = top ? 'scrollTop' : 'scrollLeft';
15+
let ret = target[prop];
16+
if (typeof ret !== 'number') {
17+
ret = window.document.documentElement[method];
18+
}
19+
return ret;
20+
}
21+
function getOffset(element) {
22+
const rect = element.getBoundingClientRect();
23+
const scrollTop = getScroll(window, true);
24+
const scrollLeft = getScroll(window);
25+
const docEl = window.document.body;
26+
const clientTop = docEl.clientTop || 0;
27+
const clientLeft = docEl.clientLeft || 0;
28+
return {
29+
top: rect.top + scrollTop - clientTop,
30+
left: rect.left + scrollLeft - clientLeft
31+
};
32+
}
33+
export default {
34+
name: 'Affix',
35+
props: {
36+
offsetTop: {
37+
type: Number,
38+
default: 0
39+
},
40+
offsetBottom: {
41+
type: Number
42+
},
43+
useCapture: {
44+
type: Boolean,
45+
default: false
46+
}
47+
},
48+
data () {
49+
return {
50+
affix: false,
51+
styles: {},
52+
slot: false,
53+
slotStyle: {}
54+
};
55+
},
56+
computed: {
57+
offsetType () {
58+
let type = 'top';
59+
if (this.offsetBottom >= 0) {
60+
type = 'bottom';
61+
}
62+
return type;
63+
},
64+
classes () {
65+
return [
66+
{
67+
[`${prefixCls}`]: this.affix
68+
}
69+
];
70+
}
71+
},
72+
mounted () {
73+
// window.addEventListener('scroll', this.handleScroll, false);
74+
// window.addEventListener('resize', this.handleScroll, false);
75+
on(window, 'scroll', this.handleScroll, this.useCapture);
76+
on(window, 'resize', this.handleScroll, this.useCapture);
77+
this.$nextTick(() => {
78+
this.handleScroll();
79+
});
80+
},
81+
beforeDestroy () {
82+
// window.removeEventListener('scroll', this.handleScroll, false);
83+
// window.removeEventListener('resize', this.handleScroll, false);
84+
off(window, 'scroll', this.handleScroll, this.useCapture);
85+
off(window, 'resize', this.handleScroll, this.useCapture);
86+
},
87+
methods: {
88+
handleScroll () {
89+
const affix = this.affix;
90+
const scrollTop = getScroll(window, true);
91+
const elOffset = getOffset(this.$el);
92+
const windowHeight = window.innerHeight;
93+
const elHeight = this.$el.getElementsByTagName('div')[0].offsetHeight;
94+
// Fixed Top
95+
if ((elOffset.top - this.offsetTop) < scrollTop && this.offsetType == 'top' && !affix) {
96+
this.affix = true;
97+
this.slotStyle = {
98+
width: this.$refs.point.clientWidth + 'px',
99+
height: this.$refs.point.clientHeight + 'px'
100+
};
101+
this.slot = true;
102+
this.styles = {
103+
top: `${this.offsetTop}px`,
104+
left: `${elOffset.left}px`,
105+
width: `${this.$el.offsetWidth}px`
106+
};
107+
this.$emit('on-change', true);
108+
} else if ((elOffset.top - this.offsetTop) > scrollTop && this.offsetType == 'top' && affix) {
109+
this.slot = false;
110+
this.slotStyle = {};
111+
this.affix = false;
112+
this.styles = null;
113+
this.$emit('on-change', false);
114+
}
115+
// Fixed Bottom
116+
if ((elOffset.top + this.offsetBottom + elHeight) > (scrollTop + windowHeight) && this.offsetType == 'bottom' && !affix) {
117+
this.affix = true;
118+
this.styles = {
119+
bottom: `${this.offsetBottom}px`,
120+
left: `${elOffset.left}px`,
121+
width: `${this.$el.offsetWidth}px`
122+
};
123+
this.$emit('on-change', true);
124+
} else if ((elOffset.top + this.offsetBottom + elHeight) < (scrollTop + windowHeight) && this.offsetType == 'bottom' && affix) {
125+
this.affix = false;
126+
this.styles = null;
127+
this.$emit('on-change', false);
128+
}
129+
}
130+
}
131+
};
132+
</script>

resources/js/styles/admin.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,7 @@ html {
180180
padding: 0;
181181
}
182182

183+
.admin-affix {
184+
position: fixed;
185+
z-index: 10;
186+
}

0 commit comments

Comments
 (0)