VUE、一个简单的编辑页面我都做了哪些优化

236 阅读1分钟

用户体验

先上设计稿

查看偏好信息 编辑偏好信息

用户体验优化点分析和代码实现:

  • 1.点击编辑时尽量控制页面高度不变,这样用户视觉效果会更好,解决方法让展示时和编辑时子标签高度保持一致,保存按钮div使用visibility: hidden来控制显示隐藏;
  • 2.当用户偏好很少或者屏幕高度很高时编辑紧跟内容下方,页面出现滚动条时保存按钮悬浮在页面下方;
let customerPreference = document.getElementById('customerPreference')
if ((customerPreference.offsetHeight + 360) > window.innerHeight) { // 360是元素距页面顶部的高度
    this.isFixed = true
} else {
    this.isFixed = false
}
  • 3.保存按钮使用position: fixed定位时按钮块宽度过宽,解决方法采用获取对应正确的元素宽度赋值给按钮块元素;
this.editBoxWidth = this.$refs.edit.clientWidth
  • 4.浏览器大小改变时(缩放)的兼容,解决方案是使用window.onresize;
mounted () {
    window.onresize = () => {
        if (this.$refs.edit) {
            this.editBoxWidth = this.$refs.edit.clientWidth
        }
    }
},
destroyed () {
    window.onresize = null // 销毁事件 释放
}
  • 5.这个用户偏好的数据是可变的、可配置的,所以会出现多个大类的情况,所以样式使用:nth-child来实现。

实现后的效果图

  • 初步完成样式
  • 编辑时保存按钮悬浮
  • 兼容左侧菜单和内容区padding

整个vue文件代码

<template>
    <div class="customerPreference" id="customerPreference">
        <template v-if="productsList">
            <div class="edit" ref="edit"><el-button type="text" :disabled="isEdit" @click="showEdit"><i class="el-icon-edit">编辑</i></el-button></div>
            <div class="large" v-for="itemLarge in productsList" :key="itemLarge.code">
                <div class="largeTitle">{{itemLarge.name}}</div>
                <div class="largeTitleBorder"></div>
                <div class="mediumList">
                    <template v-if="itemLarge.children">
                        <div class="mediumLi" v-for="itemMedium in itemLarge.children" :key="itemMedium.code">
                            <div class="mediumLiTitle">{{itemMedium.name}}</div>
                            <div class="smallList">
                                <div v-show="!isEdit" :class="{ smallLiSelect: itemsmall.select, smallLi: true }" v-for="itemsmall in itemMedium.children" :key="`${itemsmall.code}a`">
                                    {{itemsmall.name}}
                                </div>
                                <el-checkbox v-show="isEdit" v-model="itemsmall.select" v-for="itemsmall in itemMedium.children" :key="`${itemsmall.code}b`">{{itemsmall.name}}</el-checkbox>
                            </div>
                        </div>
                    </template>
                </div>
            </div>
            <div :class="{ fixedBox: isFixed, buttonBox: true, isVisibility: !isEdit}" :style="`width: ${editBoxWidth + 60}px`">
                <el-button @click="cancelEdit">取消</el-button>
                <el-button @click="createCstHobby" type="primary">保存</el-button>
            </div>
        </template>
    </div>
</template>

<script>
import { listAllAndCstHobby, createCstHobby } from '@/api/lilingCustomer'

export default {
    props: {
        cstInfo: {
            type: Object
        },
        cstNo: {
            type: String
        }
    },
    watch: {
        cstNo () {
            if (this.cstNo) {
                this.listAllAndCstHobby()
            }
        }
    },
    data () {
        return {
            isFixed: false,
            isEdit: false,
            editBoxWidth: 0,
            productsList: [] // name code children parentCode
        }
    },
    created () {
        if (this.cstNo) {
            this.listAllAndCstHobby()
        }
    },
    mounted () {
        window.onresize = () => {
            if (this.$refs.edit) {
                this.editBoxWidth = this.$refs.edit.clientWidth
            }
        }
    },
    destroyed () {
        window.onresize = null // 销毁事件 释放
    },
    methods: {
        listAllAndCstHobby () {
            let params = { cstNo: this.cstNo }
            listAllAndCstHobby(params).then(res => {
                let tree = this.$util.listToTree({
                    data: res.obj,
                    parentKey: 'parentCode',
                    labelKey: 'name',
                    childrenKey: 'children',
                    valueKey: 'code',
                    rootValue: '-1'
                })
                this.productsList = tree[0].children
            })
        },
        showEdit () {
            this.editBoxWidth = this.$refs.edit.clientWidth
            this.isEdit = true
            this.$nextTick(() => {
                this.getBasePayChildrenHeight()
            })
        },
        getBasePayChildrenHeight () { // 获取屏幕高度和元素高度判断是否需要悬浮保存按钮
            let customerPreference = document.getElementById('customerPreference')
            if ((customerPreference.offsetHeight + 360) > window.innerHeight) { // 360是元素距页面顶部的高度
                this.isFixed = true
            } else {
                this.isFixed = false
            }
        },
        cancelEdit () {
            this.isEdit = false
        },
        createCstHobby () {
            let arr = []
            let getArray = (data) => {
                data.forEach((item, index) => {
                    if (item.select) {
                        arr.push(item)
                    }
                    if (item.children.length !== 0) {
                        getArray(item.children)
                    }
                })
            }
            getArray(this.productsList)
            let time = this.$util.formatFullDateTime(new Date())
            let list = arr.map((item, index) => {
                return {
                    code: item.code,
                    parentCode: item.parentCode,
                    cstNo: this.cstNo,
                    cstNm: this.cstInfo.cstNm,
                    effDt: time,
                    endDt: '2099-12-31 00:00:00'
                }
            })
            createCstHobby({ list: list }).then((res) => {
                this.$success('保存成功')
                this.listAllAndCstHobby()
                this.isEdit = false
            })
        }
    }
}
</script>

<style lang="less" scoped>
.customerPreference{
    .el-checkbox{
        margin-bottom: 9px;
    }
    width: 100%;
    .edit{
        text-align: right;
    }
    .large{
        margin: 12px 0;
        padding-bottom: 60px;
        .largeTitle{
            display: inline-block;
            padding: 7px 25px 7px 12px;
            line-height: 28px;
            background-image: linear-gradient(to right, #FE998C, #F78095);
            border-top-left-radius: 4px;
            border-top-right-radius: 42px;
        }
        .largeTitleBorder{
            position: relative;
            top: -4px;
            height: 3px;
            opacity: 0.45;
            background-image: linear-gradient(to right, #FE998C, #F78095);
        }
        .mediumList{
            display: flex;
            justify-content: space-between;
            .mediumLi{
                flex: 1;
                background-color:rgba(218, 144, 154, 0.08);
                margin-right: 4px;
                padding: 10px 18px;
                .mediumLiTitle{
                    color: #333;
                    font-size: 14px;
                    line-height: 20px;
                    margin-bottom: 16px;
                    font-weight: 800;
                }
            }
            .mediumLi:last-child{
                margin-right: 0;
            }
            .smallList{
                display: flex;
                justify-content: flex-start;
                flex-wrap: wrap;
                .smallLi{
                    padding: 2px 15px;
                    line-height: 18px;
                    font-size: 12px;
                    color: #3c3b3b;
                    border: 1px solid #F78294;
                    border-radius: 11px;
                    margin: 0 4px 4px 0;
                    display: none;
                    background-color: rgba(247, 130, 148, 0.11);
                }
                .smallLiSelect{
                    display: block;
                }
            }
        }
    }
    .buttonBox{
        background-color: #fff;
        padding: 12px;
        display: flex;
        justify-content: center;
    }
    .fixedBox{
        z-index: 100;
        position: fixed;
        bottom: 0px;
        left: 212px;
        box-shadow: 0 -2px 2px rgba(0,0,0,.1);
    }
    .isVisibility{
        visibility: hidden;
    }
    .large:nth-child(5n+2) { // 第二项
        .largeTitle{
            background-image: linear-gradient(to right, #929DF0, #9E6DEB);
        }
        .largeTitleBorder{
            background-image: linear-gradient(to right, #929DF0, #9E6DEB);
        }
        .mediumList{
            .mediumLi{
                background-color: rgba(138, 121, 218, 0.07);
            }
            .smallList{
                .smallLi{
                    border: 1px solid #8A79DA;
                    background-color: rgba(138, 121, 218, 0.1);
                }
            }
        }
    }
    .large:nth-child(5n+3) { // 第3.largeTitle{
            background-image: linear-gradient(to right, #6DA7F2, #3F8CF1);
        }
        .largeTitleBorder{
            background-image: linear-gradient(to right, #6DA7F2, #3F8CF1);
        }
        .mediumList{
            .mediumLi{
                background-color: rgba(164, 185, 212, 0.08);
            }
            .smallList{
                .smallLi{
                    border: 1px solid #418EF2;
                    background-color: rgba(65, 142, 242, 0.1);
                }
            }
        }
    }
    .large:nth-child(5n+4) { // 第4.largeTitle{
            background-image: linear-gradient(to right, #36CFC9, #09C7DB);
        }
        .largeTitleBorder{
            background-image: linear-gradient(to right, #36CFC9, #09C7DB);
        }
        .mediumList{
            .mediumLi{
                background-color:rgba(154, 204, 203, 0.08);
            }
            .smallList{
                .smallLi{
                    border: 1px solid #2DCDCD;
                    background-color: rgba(11, 199, 219, 0.1);
                }
            }
        }
    }
    .large:nth-child(5n) { // 第5.largeTitle{
            background-image: linear-gradient(to right, #4FC8B1, #1BC68D);
        }
        .largeTitleBorder{
            background-image: linear-gradient(to right, #4FC8B1, #1BC68D);
        }
        .mediumList{
            .mediumLi{
                background-color:rgba(37, 198, 148, 0.08);
            }
            .smallList{
                .smallLi{
                    border: 1px solid #25C694;
                    background-color: rgba(37, 198, 148, 0.1);
                }
            }
        }
    }
}
</style>