Layout
Row
export default {
name: 'ElRow',
componentName: 'ElRow',
props: {
tag: {
type: String,
default: 'div'
},
gutter: Number,
type: String,
justify: {
type: String,
default: 'start'
},
align: String
},
computed: {
style() {
const ret = {};
if (this.gutter) {
ret.marginLeft = `-${this.gutter / 2}px`;
ret.marginRight = ret.marginLeft;
}
return ret;
}
},
render(h) {
return h(this.tag, {
class: [
'el-row',
this.justify !== 'start' ? `is-justify-${this.justify}` : '',
this.align ? `is-align-${this.align}` : '',
{ 'el-row--flex': this.type === 'flex' }
],
style: this.style
}, this.$slots.default);
}
};
Col
export default {
name: 'ElCol',
props: {
span: {
type: Number,
default: 24
},
tag: {
type: String,
default: 'div'
},
offset: Number,
pull: Number,
push: Number,
xs: [Number, Object],
sm: [Number, Object],
md: [Number, Object],
lg: [Number, Object],
xl: [Number, Object]
},
computed: {
gutter() {
let parent = this.$parent;
while (parent && parent.$options.componentName !== 'ElRow') {
parent = parent.$parent;
}
return parent ? parent.gutter : 0;
}
},
render(h) {
let classList = [];
let style = {};
if (this.gutter) {
style.paddingLeft = this.gutter / 2 + 'px';
style.paddingRight = style.paddingLeft;
}
['span', 'offset', 'pull', 'push'].forEach(prop => {
if (this[prop] || this[prop] === 0) {
classList.push(
prop !== 'span'
? `el-col-${prop}-${this[prop]}`
: `el-col-${this[prop]}`
);
}
});
['xs', 'sm', 'md', 'lg', 'xl'].forEach(size => {
if (typeof this[size] === 'number') {
classList.push(`el-col-${size}-${this[size]}`);
} else if (typeof this[size] === 'object') {
let props = this[size];
Object.keys(props).forEach(prop => {
classList.push(
prop !== 'span'
? `el-col-${size}-${prop}-${props[prop]}`
: `el-col-${size}-${props[prop]}`
);
});
}
});
return h(this.tag, {
class: ['el-col', classList],
style
}, this.$slots.default);
}
};
总结
- 布局类使用类似于 jsx 的写法,可以动态渲染外层标签。
- props 中的部分参数可以仅指定类型,而不指定 default 或者 validator,虽然校验上稍弱,但是可拓展性强。
- 子元素可以通过 while 循环 this.$parent 查找匹配的父级。
Container
Container
<template>
<section class="el-container" :class="{ 'is-vertical': isVertical }">
<slot></slot>
</section>
</template>
<script>
export default {
name: 'ElContainer',
componentName: 'ElContainer',
props: {
direction: String
},
computed: {
isVertical() {
if (this.direction === 'vertical') {
return true;
} else if (this.direction === 'horizontal') {
return false;
}
return this.$slots && this.$slots.default
? this.$slots.default.some(vnode => {
const tag = vnode.componentOptions && vnode.componentOptions.tag;
return tag === 'el-header' || tag === 'el-footer';
})
: false;
}
}
};
</script>
Header
<template>
<header class="el-header" :style="{ height }">
<slot></slot>
</header>
</template>
<script>
export default {
name: 'ElHeader',
componentName: 'ElHeader',
props: {
height: {
type: String,
default: '60px'
}
}
};
</script>
Aside
<template>
<aside class="el-aside" :style="{ width }">
<slot></slot>
</aside>
</template>
<script>
export default {
name: 'ElAside',
componentName: 'ElAside',
props: {
width: {
type: String,
default: '300px'
}
}
};
</script>
Main
<template>
<main class="el-main">
<slot></slot>
</main>
</template>
<script>
export default {
name: 'ElMain',
componentName: 'ElMain'
};
</script>
Footer
<template>
<footer class="el-footer" :style="{ height }">
<slot></slot>
</footer>
</template>
<script>
export default {
name: 'ElFooter',
componentName: 'ElFooter',
props: {
height: {
type: String,
default: '60px'
}
}
};
</script>
总结
- 通过对 this.$slot.default 中的子组件进行循环,判断 componentOptions.tag 的名字是不是子组件的名字。
- flex布局通过 flex: 1 保证内容填充满。
Icon
<template>
<i :class="'el-icon-' + name"></i>
</template>
<script>
export default {
name: 'ElIcon',
props: {
name: String
}
};
</script>
Button
<template>
<button class="el-button" @click="handleClick" :disabled="buttonDisabled || loading" :autofocus="autofocus"
:type="nativeType" :class="[
type ? 'el-button--' + type : '',
buttonSize ? 'el-button--' + buttonSize : '',
{
'is-disabled': buttonDisabled,
'is-loading': loading,
'is-plain': plain,
'is-round': round,
'is-circle': circle
}
]">
<i class="el-icon-loading" v-if="loading"></i>
<i :class="icon" v-if="icon && !loading"></i>
<span v-if="$slots.default">
<slot></slot>
</span>
</button>
</template>
<script>
export default {
name: 'ElButton',
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
props: {
type: {
type: String,
default: 'default'
},
size: String,
icon: {
type: String,
default: ''
},
nativeType: {
type: String,
default: 'button'
},
loading: Boolean,
disabled: Boolean,
plain: Boolean,
autofocus: Boolean,
round: Boolean,
circle: Boolean
},
computed: {
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
buttonSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
buttonDisabled() {
return Object.prototype.hasOwnProperty.call(this.$options.propsData, 'disabled') ? this.disabled : (this.elForm || {}).disabled;
}
},
methods: {
handleClick(evt) {
this.$emit('click', evt);
}
}
};
</script>
总结
- 判断 props 中的部分参数是否存在,可以使用 Object.prototype.hasOwnProperty 方法进行确定。
- 按钮 loading 状态同 disabled 状态。
Link
<template>
<a :class="[
'el-link',
type ? `el-link--${type}` : '',
disabled && 'is-disabled',
underline && !disabled && 'is-underline'
]" :href="disabled ? null : href" v-bind="$attrs" @click="handleClick">
<i :class="icon" v-if="icon"></i>
<span v-if="$slots.default" class="el-link--inner">
<slot></slot>
</span>
<template v-if="$slots.icon">
<slot v-if="$slots.icon" name="icon"></slot>
</template>
</a>
</template>
<script>
export default {
name: 'ElLink',
props: {
type: {
type: String,
default: 'default'
},
underline: {
type: Boolean,
default: true
},
disabled: Boolean,
href: String,
icon: String
},
methods: {
handleClick(event) {
if (!this.disabled) {
if (!this.href) {
this.$emit('click', event);
}
}
}
}
};
</script>
总结
- link 的 disabled 状态需要从三个方面限制:样式、href、click回调。