PS: 修改 Element 中的内置样式时,需要在该组件上定义一个类名,然后在该类名中使用穿透样式覆盖内部样式。
1. Cascader
2. Input
当给 v-model 绑定 number 修饰符时,不知为何,效果总是不尽人意,于是乎,自己总结了两种方案以此来解决某些输入框只输入数字。
- type=“number”
包含三部分操作:
- 指定类型为 number;
- css 去除 input 输入框中的上下小箭头,指定 input__inner 行高,消除输入非数字字符时鼠标指针上浮问题;
- 由于指定类型为 number,此时用鼠标滚轮会改变数字,此种效果并不是我们期望的,于是使用自定义指令阻止鼠标默认行为
// html
<el-input type="number" v-disable-wheel></el-input>
// css
/*去除在Chrome中type=number的input输入框中的上下小箭头*/
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
appearance: none;
}
.el-input__inner {
padding: 0 10px;
line-height: 1 !important;
}
// js
directives: {
'disable-mousewheel': {
bind(el) {
el.addEventListener('wheel', (e) => e.preventDefault())
},
update(el) {
el.addEventListener('wheel', (e) => e.preventDefault())
},
},
}
- 监听 input 事件
可以根据需求动态的变化正则表达式中的条件。
// utils
export const removeExtraCharacterOfNumber = (value) => {
return value
.replace(/[^-0-9.]/g, '')
.replace(/^\./g, '')
.replace('.', '$#$')
.replace(/\./g, '')
.replace('$#$', '.')
.replace('.', '')
}
export const removeExtraCharacterOfNumberWithDot = (value) => {
return value
.replace(/[^-0-9.]/g, '')
.replace(/^\./g, '')
.replace('.', '$#$')
.replace(/\./g, '')
.replace('$#$', '.')
}
<el-input v-model="inputValue" @input="handleInput"></el-input>
data() {
return {
inputValue: null
}
},
methods: {
...
// 由于此方法多处会用到,所以抽离成一个公共方法
handleInput(value) {
this.inputValue = removeExtraCharacterOfNumber(value)
},
// 仅输入正负数
// input event
const formatPositiveAndNegativeNumber = (value: string) => {
let tempValue = value
const char1 = value.charAt(0)
tempValue = tempValue.replace(/[^\d]/g, '')
if (tempValue && tempValue[0] === '0') {
tempValue = tempValue.substring(1)
}
if (char1 === '-') {
tempValue = '-' + tempValue
}
this.inputValue = tempValue
}
// change event
const changePositiveAndNegativeNumber = () => {
if (this.inputValue === '-') {
this.inputValue = null
}
}
3. DatePicker
有关日期的组件需求往往格式固定,形如 "YYYY-MM-DD",格式验证,结束日期是开始日期的后多少天,遇到此类问题时往往总是去查组件库,然后每次都要重新写,因此在这里总结一下方便后期使用。
<el-form :model="form" :rules="formRules">
<el-form-item label="开始日期:" label-width="120px" prop="startDate">
<el-date-picker
style="width: 100%;"
v-model="startDate"
type="date"
placeholder="请选择开始日期"
value-format="yyyy-MM-dd"
@change="startDateChange"
/></el-form-item>
<el-form-item label="结束日期:" label-width="120px" prop="endDate">
<el-date-picker
style="width: 100%;"
v-model="endDate"
type="date"
placeholder="请选择结束日期"
value-format="yyyy-MM-dd"
/></el-form-item>
</el-form>
// js
form: {
startDate: "",
endDate: ""
},
formRules: {
startDate: [
{ type: 'string', required: true, message: '请选择留样开始日期', trigger: 'change' }
],
endDate: [
{ type: 'string', required: true, message: '请选择留样开始日期', trigger: 'change' }
]
}
openDateInfo() {
this.startDate = formatDate(this.startDate)
this.endDate = formatDate(this.endDate)
},
startDateChange() {
// 这里默认90天
this.endDate = new Date(new Date().setDate(new Date(val).getDate() + 90))
this.endDate = formateDate(this.endDate)
}
// utils.js
const formatDate = date => {
const d = new Date(date)
const year = d.getFullYear()
let month = '' + (d.getMonth() + 1)
let day = '' + d.getDate()
if (month.length < 2) month = '0' + month
if (day.length < 2) day = '0' + day
return [year, month, day].join('-')
}
// 感觉上述的方法太过繁琐,直接使用 dayjs 即可
this.startDate = dayjs(new Date()).format('YYYY-MM-DD')
this.endDate = dayjs(new Date(new Date().setDate(new Date(val).getDate() + 90))).formate('YYYY-MM-DD')
4. Layout
4.1 自定义 span 宽度
<el-col :xl="{ span: '4-5' }">
</el-col>
<style lang="less" scoped>
@media only screen and (min-width: 1920px) {
.el-col-xl-4-5 {
width: 13%;
}
.el-col-xl-offset-4-5 {
margin-left: 13%;
}
.el-col-xl-pull-4-5 {
position: relative;
right: 13%;
}
.el-col-xl-push-4-5 {
position: relative;
left: 13%;
}
}
</style>
5. Loading
5.1 v-loading conflict with el-table binding ref
解决方法:重新自定义v-loading。
具体详情请见
5.2 CSS style
-
方案一(只在父节点有宽高的情况下生效)
<div> <div class="mongolia" v-if="loadingCharts"> <el-icon class="loading-icon"><Loading /></el-icon> </div> </div> const loadingCharts = ref(false) setTimeout(() => { loadingCharts.value = true }, 100) .mongolia { width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); display: flex; justify-content: center; align-items: center; z-index: 9999; } .loading-icon { position: relative; top: 5px; animation: spin 2s linear infinite; } @keyframes spin { from { transform: rotate(0); } to { transform: rotate(360deg); } } -
方案二 整个页面添加 loading 效果
<div class="mongolia" v-if="requestNextPageLoading"> <i class="el-icon-loading loading-icon" /> <p>loading...</p> </div> // css .mongolia { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(255, 255, 255, 0.9); display: flex; justify-content: center; align-items: center; font-size: 1.5rem; color: #409eff; z-index: 9999; } .loading-icon { color: #409eff; font-size: 32px; } -
方案三:在局部页面添加 loading 效果 此种方案只需要将 loading 相关的 html 放到高度为 100% 的元素内部即可。
// html
<div v-if="waitLoading">
...
</div>
<div class="mongolia" v-else-if="waitLoading">
<el-icon class="el-icon-loading loading-icon"> <Loading /> </el-icon>
<p class="text-loading">loading...</p>
</div>
// css
.mongolia {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
color: #409eff;
}
.loading-icon {
font-size: 32px;
animation: spin 2s linear infinite;
}
.text-loading {
position: relative;
left: 5px;
}
@keyframes spin {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
6. Tabs
需求:每个 tab 都是一个单独的页面文件,点击不同的 tab 发送不同的请求从而渲染不同的页面。
6.1 v-if
使用不同的 flag 绑定在不同的子组件上,有些场景失效。
6.2 Flags
如果子页面无法正确监听,可以在页面失活时重置 Flag。
PS:在 Vue2 中,要确保 router 中的 name 名称和组件中的 name 保持一致。
// parent
<el-tabs @tab-click='changeTab'>
<el-tab-pane name='child1' label='child1'>
<Child1 :active-flag='activeChild1' />
</el-tab-pane>
<el-tab-pane name='child2' label='child2'>
<Child2 :active-flag='activeChild2' />
</el-tab-pane>
</el-tabs>
// script
const changeTab = () => {
switch (activeName) {
case 'child1':
this.activeChild1 = true
this.activeChild2 = false
break
case 'child2':
this.activeChild1 = false
this.activeChild2 = true
break
default:
break
}
}
const resetFlag = () => {
this.activeChild1 = false
this.activeChild2 = false
}
// 如果发现子组件无法正确监听,可以在 deActivated 中重置一下
deActivated() {
resetFlag()
}
// Child
watch: {
activeFlag: {
handler(val) {
val && this.getRequest()
}
}
}
6.3 props
当不使用方式二中的 deActivated 重置方法,可以在子组件中进行操作。
此页面刷新触发条件有两种:1. 从别的页面切换到当前页面(activated);2. 点击 tab 切换。
如果当前页面不是 tab 的第一个,那么当停留在当前页面时,如果从别的页面切换到当前页面,那么执行的就是 activated 中的 else 方法;如果点击 tab,那么执行的就是 watch 中的方法,如果 watch 和 firstFlag 同时执行的话,会发送两次请求,此时禁止掉第一次请求即可。
PS:如果是 tab 的第一个页面那么需要在 watch 的时候加上 immediate 。
// child
activated() {
if (this.firstFlag) {
this.firstFlag = false
} else {
this.activeFlag && this.getRequest()
}
},
// 区别:
// child1
activeFlag: {
handler(val) {
val && this.getRequest()
},
immediate: true
}
// child2
activeFlag: {
handler(val) {
val && this.getRequest()
}
}
7. Table
7.1 修改表格的一些样式
.el-table {
--el-table-border-color: var(--custom-global-table-border-color) !important;
--el-fill-color-blank: var(--custom-global-bg-color) !important;
--el-table-row-hover-bg-color: var(--custom-global-table-row-hover-color) !important;
.el-table-fixed-column--right {
background: var(--custom-global-bg-color) !important;
}
.el-table__body .el-table__row.hover-row td {
background: var(--custom-global-table-row-hover-color) !important;
}
}