前端自定义组件之左右滑动

本文将讲解如何通过自定义,实现容器内的左右两个元素,通过左右拖拽分隔线,灵活调整宽度。其中左右元素里的内容都是界面设计器拖出来的。
前端自定义组件之左右滑动

实现路径

1. 界面设计器拖出页面

我们界面设计器拖个布局容器,然后在左右容器里拖拽任意元素。完成后点击右上角九宫格,选中布局容器,填入组件 api 名称,作用是把布局容器切换成我们自定义的左右滑动组件,这里的 api 名称和自定义组件的 widget 对应。最后发布页面,并绑定菜单。
前端自定义组件之左右滑动

2. 组件实现

widget 组件重写了布局容器,核心函数 renderLeftrenderRight,通过 DslRender.render 方法渲染界面设计器拖拽的元素。

import {
  BasePackWidget,
  DefaultContainersWidget,
  DslDefinition,
  DslRender,
  SPI,
  Widget
} from '@oinone/kunlun-dependencies';
import LeftRightSlide from './LeftRightSlide.vue';

// 拿到界面设计器配置的子容器元素
function fetchContainerChildren(widgets?: DslDefinition[], level = 3): DslDefinition[] {
  if (!widgets) {
    return [];
  }
  const children: DslDefinition[] = [];
  for (const widget of widgets) {
    if (widget.widget === 'container') {
      children.push(widget);
    } else if (level >= 1) {
      fetchContainerChildren(widget.widgets, level - 1).forEach((child) => children.push(child));
    }
  }
  return children;
}

@SPI.ClassFactory(BasePackWidget.Token({ widget: 'LeftRightSlide' }))
export class LeftRightSlideWidget extends DefaultContainersWidget {
  public initialize(props) {
    super.initialize(props);
    this.setComponent(LeftRightSlide);
    return this;
  }

  // 获取容器的子元素
  public get containerChildren(): DslDefinition[] {
    return fetchContainerChildren(this.template?.widgets);
  }

  // 初始宽度配置
  @Widget.Reactive()
  public get initialLeftWidth() {
    return this.getDsl().initialLeftWidth || 400;
  }
  // 最小左宽度配置
  @Widget.Reactive()
  public get minLeftWidth() {
    return this.getDsl().minLeftWidth || 200;
  }
  // 最小右宽度配置
  @Widget.Reactive()
  public get minRightWidth() {
    return this.getDsl().minRightWidth || 200;
  }

  // 根据容器子元素渲染左侧
  @Widget.Method()
  public renderLeft() {
    // 把容器的第一个元素作为左侧
    const containerLeft = this.containerChildren[0];
    if (containerLeft) {
      return DslRender.render(containerLeft);
    }
  }

  // 根据容器子元素渲染右侧
  @Widget.Method()
  public renderRight() {
    // 把容器的第二个元素作为右侧
    const containerRight = this.containerChildren[1];
    if (containerRight) {
      return DslRender.render(containerRight);
    }
  }
}

vue组件核心内容是用component :is属性,渲染出配置的容器组件

<template>
  <div class="LeftRightSlide">
    <div class="panel left-panel" :style="{ width: leftWidth + 'px' }">
      <component :is="leftComponent" />
    </div>
    <div class="splitter-bar" @mousedown="startDrag"></div>
    <div class="panel right-panel">
      <component :is="rightComponent" />
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'LeftRightSlide',
  components: {},
  props: {
    renderLeft: {
      type: Function
    },
    renderRight: {
      type: Function
    },
    initialLeftWidth: {
      type: Number,
      default: 400
    },
    minLeftWidth: {
      type: Number,
      default: 200
    },
    minRightWidth: {
      type: Number,
      default: 200
    }
  },
  setup(props) {
    const leftComponent = computed(() => props.renderLeft?.());
    const rightComponent = computed(() => props.renderRight?.());

    const leftWidth = ref(props.initialLeftWidth);

    const startDrag = (e: MouseEvent) => {
      const startX = e.clientX;
      const startWidth = leftWidth.value;

      const onMouseMove = (moveEvent: MouseEvent) => {
        const delta = moveEvent.clientX - startX;
        const newLeftWidth = Math.max(props.minLeftWidth, startWidth + delta);
        leftWidth.value = newLeftWidth;
      };

      const onMouseUp = () => {
        window.removeEventListener('mousemove', onMouseMove);
        window.removeEventListener('mouseup', onMouseUp);
      };

      window.addEventListener('mousemove', onMouseMove);
      window.addEventListener('mouseup', onMouseUp);
    };

    return {
      leftComponent,
      rightComponent,
      leftWidth,
      startDrag
    };
  }
});
</script>

<style lang="scss">
.LeftRightSlide {
  display: flex;
  height: 100%;
  width: 100%;
  overflow: hidden;
  position: relative;

  .panel {
    height: 100%;
    overflow: auto;
  }

  .left-panel {
  }

  .right-panel {
    flex: 1;
  }

  .splitter-bar {
    width: 6px;
    cursor: col-resize;
    background-color: #ccc;
    position: relative;
    z-index: 1;
    transition: background-color 0.2s;

    &:hover {
      background-color: #999;
    }
  }
}
</style>

3. 效果

前端自定义组件之左右滑动

Oinone社区 作者:银时原创文章,如若转载,请注明出处:https://doc.oinone.top/frontend/components/21328.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
银时的头像银时数式员工
上一篇 2025年6月26日 pm3:10
下一篇 2025年7月8日 pm3:43

相关推荐

  • oio-input 输入框

    代码演示 <oio-input v-model:value="value"></oio-input> API Input 参数 说明 类型 默认值 版本 addonAfter 带标签的 input,设置后置标签 string|slot addonBefore 带标签的 input,设置前置标签 string|slot allowClear 可以点击清除图标删除内容 boolean defaultValue 输入框默认内容 string disabled 是否禁用状态,默认为 false boolean false maxlength 最大长度 number prefix 带有前缀图标的 input slot showCount 是否展示字数 boolean false suffix 带有后缀图标的 input slot type 声明 input 类型,同原生 input 标签的 type 属性,见:MDN(请直接使用 <a-textarea /> 代替 type="textarea")。 string text value(v-model:value) 输入框内容 string Input 事件 事件名称 说明 回调参数 update:value 输入框内容变化时的回调 function(e) pressEnter 按下回车的回调 function(e) Input.Search 代码演示 <oio-input-search v-model:value="value"></oio-input-search> Input.Search 事件 事件名称 说明 回调参数 search 点击搜索或按下回车键时的回调 function(value, event) 其余属性和 Input 一致。

    2023年12月18日
    1.8K00
  • OioNotification 通知提醒框

    全局展示通知提醒信息。 何时使用 在系统四个角显示通知提醒信息。经常用于以下情况: 较为复杂的通知内容。 带有交互的通知,给出用户下一步的行动点。 系统主动推送。 API OioNotification.success(title,message, config) OioNotification.error(title,message, config) OioNotification.info(title,message, config) OioNotification.warning(title,message, config) config 参数如下: 参数 说明 类型 默认值 版本 duration 默认 3 秒后自动关闭 number 3 class 自定义 CSS class string –

    2023年12月18日
    70400
  • oio-pagination 分页

    API 参数 说明 类型 默认值 版本 currentPage(v-model:currentPage) 当前页数 number – defaultPageSize 默认的每页条数 number 15 disabled 禁用分页 boolean – pageSize 每页条数 number – pageSizeOptions 指定每页可以显示多少条 string[] [’10’, ’15’, ’30’, ’50’, ‘100’, ‘200’] showQuickJumper 是否可以快速跳转至某页 boolean false showSizeChanger 是否展示 pageSize 切换器,当 total 大于 50 时默认为 true boolean – total 数据总数 number 0 事件 事件名称 说明 回调参数 change 页码或 pageSize 改变的回调,参数是改变后的页码及每页条数 Function(page, pageSize) noop

    2023年12月18日
    69300
  • oio-button 按钮

    主按钮:用于主行动点,一个操作区域只能有一个主按钮。 默认按钮:用于没有主次之分的一组行动点。 虚线按钮:常用于添加操作。 文本按钮:用于最次级的行动点。 链接按钮:一般用于链接,即导航至某位置。 以及四种状态属性与上面配合使用。 危险:删除/移动/修改权限等危险操作,一般需要二次确认。 禁用:行动点不可用的时候,一般需要文案解释。 加载中:用于异步操作等待反馈的时候,也可以避免多次提交。 API 按钮的属性说明如下: 属性 说明 类型 默认值 版本 block 将按钮宽度调整为其父宽度的选项 boolean false disabled 按钮失效状态 boolean false icon 设置按钮的图标类型 v-slot – loading 设置按钮载入状态 boolean | { delay: number } false type 设置按钮类型 primary | ghost | dashed | link | text | default default 事件 事件名称 说明 回调参数 版本 click 点击按钮时的回调 (event) => void 支持原生 button 的其他所有属性。

    2023年12月18日
    53000
  • oio-spin 加载中

    用于页面和区块的加载中状态。 何时使用 页面局部处于等待异步数据或正在渲染过程时,合适的加载动效会有效缓解用户的焦虑。 API 参数 说明 类型 默认值 版本 delay 延迟显示加载效果的时间(防止闪烁) number (毫秒) – loading 是否为加载中状态 boolean true wrapperClassName 包装器的类属性 string –

    2023年12月18日
    73600

Leave a Reply

登录后才能评论