原型
原型图
注意:上面原型图中缺少缩放按钮的设计
要求
- 左右分栏统一使用间距16px进行分割。
- 左侧栏目是否可以收缩由项目自行决定。
- 左侧栏目默认宽度280px,右侧栏宽度自适应。
- 左侧分栏的缩放按钮可使用纯图标按钮。
分析
1、由于当前组件是一个布局组件,而且是左右横向布局的组件,所以选用antd组件中的Grid 栅格布局组件
2、左右部分的内容都是有head和body部分,所以可以选用antd组件中最常用的Card 卡片组件,这样组件内的间距也可以默认使用,不用在自定义样式
代码实现
<!--
* @description GHorizontalColumns.vue 横向左右分栏布局
* @author JUN - 2023/8/10 10:06
-->
<template>
<a-row type="flex" justify="space-between" align="top" :gutter="gutter" class="h-full">
<!--左边部分-->
<a-col :flex="isShrinkSwitch?'0px':'280px'"
class="h-full"
:style="{left:isShrinkSwitch?`-${this.paddingLeft}`:0}">
<a-card v-show="!isShrinkSwitch"
:bordered="bordered"
:head-style="getLeftHeadStyle"
:style="{backgroundColor: this.bgColor}"
:body-style="getLeftBodyStyle"
class="h-full shadow-md shadow-black">
<div slot="title">
<slot name="leftTitle">{{ leftTitle }}</slot>
</div>
<div slot="extra">
<slot name="leftExtra">{{ leftExtra }}</slot>
</div>
<div>
<slot name="leftContent"></slot>
</div>
</a-card>
<!--伸缩操纵杆-->
<div v-if="isVisibleSwitchHandler"
@click.prevent="onTriggerSwitchHandler()"
class="z-10 absolute top-20 -right-10">
<slot name="switchHandler" v-bind="{isShrinkSwitch}">
<img :src="activeSwitchHandlerIcon" width="48px" height="136px">
</slot>
</div>
</a-col>
<!--右边部分-->
<a-col flex="auto" class="h-full w-0">
<a-card :bordered="bordered"
:head-style="getRightHeadStyle"
:style="{backgroundColor: this.bgColor}"
:body-style="getRightBodyStyle"
class="h-full shadow-md">
<div slot="title">
<slot name="rightTitle">{{ rightTitle }}</slot>
</div>
<div slot="extra">
<slot name="rightExtra">{{ rightExtra }}</slot>
</div>
<div>
<slot name="rightContent"></slot>
</div>
</a-card>
</a-col>
</a-row>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
@Component({ name: 'GHorizontalColumns' })
export default class GHorizontalColumns extends Vue {
/* 左边部分的title */
@Prop({
type: String,
default: '左边栏标题slot'
}) leftTitle: string | undefined;
/* 左边部分的extra */
@Prop({
type: String,
default: '左边栏extra的slot'
}) leftExtra: string | undefined;
/* 右边部分的title */
@Prop({
type: String,
default: '右边栏标题slot'
}) rightTitle: string | undefined;
/* 右边部分的extra */
@Prop({
type: String,
default: '右边栏extra的slot'
}) rightExtra: string | undefined;
/* 左右分栏的间隔 */
@Prop({
type: Number,
default: 16
}) gutter: number | undefined;
/* 是否显示border */
@Prop({
type: Boolean,
default: true
}) bordered: boolean | undefined;
/* 是否显示左边的head */
@Prop({
type: Boolean,
default: true
}) isVisibleLeftHead: boolean | undefined;
/* 是否显示右边的head */
@Prop({
type: Boolean,
default: true
}) isVisibleRightHead: boolean | undefined;
/* 是否显示border */
@Prop({
type: String,
default: '#ffff'
}) bgColor: string | undefined;
/* 是否显示伸缩操纵杆 */
@Prop({
type: Boolean,
default: true
}) isVisibleSwitchHandler: boolean | undefined;
/* 是否显示伸缩操纵杆 */
@Prop({
type: Object,
default: {
extend: '/images/switch/extend_icon.png',
shrink: '/images/switch/shrink_icon.png'
}
}) switchHandlerIcons: { extend: string; shrink: string };
/* 是否收起操纵杆 */
isShrinkSwitch = false;
paddingLeft = '0px';
/**
* @description 动态设置headStyle (左)
* @return void
* @author JUN - 2023/8/10 10:52
*/
get getLeftHeadStyle() {
const bordered = this.bordered ? {} : { border: 0 };
const visible = this.isVisibleLeftHead ? {} : { display: 'none' };
return { ...bordered, ...visible };
}
/**
* @description 动态设置headStyle (右)
* @return void
* @author JUN - 2023/8/10 10:52
*/
get getRightHeadStyle() {
const bordered = this.bordered ? {} : { border: 0 };
const visible = this.isVisibleRightHead ? {} : { display: 'none' };
return { ...bordered, ...visible };
}
/**
* @description 动态设置bodyStyle (左)
* @return void
* @author JUN - 2023/8/10 10:52
*/
get getLeftBodyStyle() {
const bordered = this.bordered ? {} : { border: 0 };
const height = this.isVisibleLeftHead ? { height: 'calc(100% - 55px)' } : { height: '100%' };
return {
...bordered,
...height,
overflow: 'auto',
};
}
/**
* @description 动态设置bodyStyle (右)
* @return void
* @author JUN - 2023/8/10 10:52
*/
get getRightBodyStyle() {
const bordered = this.bordered ? {} : { border: 0 };
const height = this.isVisibleRightHead ? { height: 'calc(100% - 55px)' } : { height: '100%' };
return {
...bordered,
...height,
overflow: 'auto',
};
}
/**
* @description 返回当前操作杆的icon
* @return void
* @author JUN - 2023/8/10 13:45
*/
get activeSwitchHandlerIcon() {
const icon = this.isShrinkSwitch ? 'extend' : 'shrink';
return this.switchHandlerIcons[icon];
}
/**
* @description 触发操纵杆事件处理
* @return void
* @author JUN - 2023/8/10 13:41
*/
onTriggerSwitchHandler() {
this.isShrinkSwitch = !this.isShrinkSwitch;
}
mounted() {
const parentElement = this?.$el?.parentElement;
const { paddingLeft } = getComputedStyle(parentElement);
this.paddingLeft = paddingLeft;
}
}
</script>
<style scoped lang="less">
/deep/ .ant-card-body {
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
&::-webkit-scrollbar {
width: 8px;
height: 8px;
border-radius: 0px;
}
/*定义滚动条轨道 内阴影+圆角*/
&::-webkit-scrollbar-track {
//-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 3px;
background: rgba(0, 0, 0, 0.05);
}
/*定义滑块 内阴影+圆角*/
&::-webkit-scrollbar-thumb {
border-radius: 4px;
//-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background: rgba(0, 0, 0, 0.10);
}
}
</style>
由于默认的滚动条颜值太低了,所以我还自定义了滚动条的样式(less),代码如下:
/deep/ .ant-card-body {
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
&::-webkit-scrollbar {
width: 8px;
height: 8px;
border-radius: 0px;
}
/*定义滚动条轨道 内阴影+圆角*/
&::-webkit-scrollbar-track {
//-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 3px;
background: rgba(0, 0, 0, 0.05);
}
/*定义滑块 内阴影+圆角*/
&::-webkit-scrollbar-thumb {
border-radius: 4px;
//-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background: rgba(0, 0, 0, 0.10);
}
}
使用
<!--
* @description GHorizontalColumnsDemo.vue 横向左右分栏布局的demo
* @author JUN - 2023/8/10 14:54
-->
<template>
<g-horizontal-columns :bordered="false" :is-visible-left-head="false" style="height: 85vh;">
<div class="border" style="height: 800px" slot="leftContent">
测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试
</div>
<div class="border" style="height: 800px" slot="rightContent">
测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试
</div>
<div slot="switchHandler" slot-scope="{isShrinkSwitch}">
<img v-if="isShrinkSwitch" src="/images/switch/extend_icon.png" width="48px" height="136px">
<img v-else src="/images/switch/shrink_icon.png" width="48px" height="136px">
</div>
</g-horizontal-columns>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import GHorizontalColumns from '@/components/common/layouts/GHorizontalColumns.vue';
@Component({
name: 'GHorizontalColumnsDemo',
components: { GHorizontalColumns }
})
export default class GHorizontalColumnsDemo extends Vue {
}
</script>
<style scoped lang="less">
</style>
效果
技术不行,还有很多可以优化的地方,希望大家留言指教