AGILE TEAM
Skip to content

BaseToolbar 工具栏组件

来源:@jhlc/common-core 远程组件

BaseToolbar 是一个灵活的工具栏组件,用于放置操作按钮,支持左侧和右侧按钮区域、下拉菜单、权限控制、加载状态等功能。

📦 导入方式

typescript
// 全局注册(已在项目中配置)
// 直接使用 <BaseToolbar /> 即可

// 类型导入
import type { ActionButtonDesc, ActionType } from "@jhlc/common-core/src/components/toolbar/type";

🚀 基本用法

vue
<template>
  <BaseToolbar :items="toolbarItems" />
</template>

<script setup lang="ts">
import { computed } from "vue";

const toolbarItems = computed(() => [
  {
    name: "add",
    type: "add",
    onClick: () => handleAdd()
  },
  {
    name: "delete",
    type: "delete",
    onClick: () => handleDelete()
  }
]);

const handleAdd = () => console.log("新增");
const handleDelete = () => console.log("删除");
</script>

📋 Props 属性

属性名类型默认值说明
itemsActionButtonDesc[][]左侧按钮配置
rightItemsActionButtonDesc[][]右侧按钮配置
size'small' | 'default' | 'large''default'按钮尺寸

📋 ActionType 按钮类型

使用 type 属性可以快速设置按钮的图标和样式:

类型说明图标样式
add新增Plusprimary
edit编辑Editwarning
delete删除Deletedanger
batchDel批量删除Deletedanger
import导入Uploadinfo
export导出Downloadwarning
query查询Searchprimary
reset重置Refresh-
print打印Printerwarning
submit提交CircleCheckprimary
finish完成CircleCheckprimary
close关闭CircleCloseinfo
cancel取消CircleCloseinfo
rollback退回RefreshLeftwarning
back返回ArrowLeft-
copy复制CopyDocument-
view查看View-
refresh刷新Refresh-
save保存Documentprimary
collapse收起DArrowLeft-
expand展开DArrowRight-
log日志Document-
prev上一步ArrowLeftBoldwarning
next下一步ArrowRightBoldprimary
confirm确认Selectprimary

📋 ActionButtonDesc 按钮配置

typescript
interface ActionButtonDesc {
  // 按钮唯一标识(必填)
  name: string;
  // 按钮类型,会自动设置图标和样式
  type?: ActionType;
  // 自定义标签文本
  label?: string;
  // 自定义图标
  icon?: string;
  // 是否禁用
  disabled?: boolean | (() => boolean);
  // 是否显示
  show?: boolean | (() => boolean);
  // 加载状态
  loading?: boolean | (() => boolean);
  // 点击事件
  onClick?: () => void | Promise<void>;
  // 权限标识
  permission?: string;
  // 下拉子按钮
  children?: ActionButtonDesc[];
  // 是否朴素按钮
  plain?: boolean;
  // 是否自动管理加载状态(点击后自动 loading)
  autoLoading?: boolean;
  // 自定义渲染
  renderNode?: () => VNode;
  // 更多样式
  style?: any;
  // 下拉按钮(当存在 children 时)
  split?: boolean;
}

💡 使用示例

基础工具栏

vue
<template>
  <BaseToolbar :items="toolbarItems" />
</template>

<script setup lang="ts">
import { computed } from "vue";

const toolbarItems = computed(() => [
  {
    name: "add",
    type: "add",
    onClick: handleAdd
  },
  {
    name: "edit",
    type: "edit",
    disabled: () => selectedRows.value.length !== 1,
    onClick: handleEdit
  },
  {
    name: "delete",
    type: "delete",
    disabled: () => selectedRows.value.length === 0,
    onClick: handleDelete
  }
]);
</script>

左右布局

vue
<template>
  <BaseToolbar
    :items="leftItems"
    :rightItems="rightItems"
  />
</template>

<script setup lang="ts">
const leftItems = computed(() => [
  { name: "add", type: "add", onClick: handleAdd },
  { name: "delete", type: "batchDel", onClick: handleBatchDelete }
]);

const rightItems = computed(() => [
  { name: "export", type: "export", onClick: handleExport },
  { name: "import", type: "import", onClick: handleImport }
]);
</script>

自定义图标和标签

vue
<script setup lang="ts">
const toolbarItems = computed(() => [
  {
    name: "approve",
    label: "审批通过",
    icon: "Select",
    onClick: handleApprove
  },
  {
    name: "reject",
    label: "审批退回",
    icon: "CloseBold",
    onClick: handleReject
  }
]);
</script>

下拉菜单按钮

vue
<script setup lang="ts">
const toolbarItems = computed(() => [
  {
    name: "add",
    type: "add",
    onClick: handleAdd
  },
  {
    name: "more",
    label: "更多操作",
    icon: "More",
    children: [
      {
        name: "copy",
        type: "copy",
        label: "复制",
        onClick: handleCopy
      },
      {
        name: "export",
        type: "export",
        label: "导出",
        onClick: handleExport
      },
      {
        name: "print",
        type: "print",
        label: "打印",
        onClick: handlePrint
      }
    ]
  }
]);
</script>

分裂式下拉按钮

vue
<script setup lang="ts">
const toolbarItems = computed(() => [
  {
    name: "save",
    type: "save",
    label: "保存",
    split: true,   // 分裂式下拉
    onClick: handleSave,
    children: [
      {
        name: "saveAndNew",
        label: "保存并新增",
        onClick: handleSaveAndNew
      },
      {
        name: "saveAndCopy",
        label: "保存并复制",
        onClick: handleSaveAndCopy
      }
    ]
  }
]);
</script>

权限控制

vue
<script setup lang="ts">
const toolbarItems = computed(() => [
  {
    name: "add",
    type: "add",
    permission: "sale:order:add",   // 需要此权限才显示
    onClick: handleAdd
  },
  {
    name: "edit",
    type: "edit",
    permission: "sale:order:edit",
    onClick: handleEdit
  },
  {
    name: "delete",
    type: "delete",
    permission: "sale:order:delete",
    onClick: handleDelete
  }
]);
</script>

加载状态

vue
<script setup lang="ts">
import { ref } from "vue";

const saving = ref(false);

const toolbarItems = computed(() => [
  {
    name: "save",
    type: "save",
    loading: () => saving.value,   // 响应式加载状态
    onClick: async () => {
      saving.value = true;
      await save();
      saving.value = false;
    }
  },
  {
    name: "submit",
    type: "submit",
    autoLoading: true,   // 自动管理加载状态
    onClick: async () => {
      await submit();   // 点击后自动 loading,完成后自动恢复
    }
  }
]);
</script>

动态显示/禁用

vue
<script setup lang="ts">
import { ref } from "vue";

const selectedRows = ref([]);
const isEditing = ref(false);

const toolbarItems = computed(() => [
  {
    name: "add",
    type: "add",
    show: () => !isEditing.value,   // 编辑时隐藏
    onClick: handleAdd
  },
  {
    name: "edit",
    type: "edit",
    show: () => !isEditing.value,
    disabled: () => selectedRows.value.length !== 1,   // 只选中一条时可用
    onClick: handleEdit
  },
  {
    name: "save",
    type: "save",
    show: () => isEditing.value,   // 编辑时显示
    onClick: handleSave
  },
  {
    name: "cancel",
    type: "cancel",
    show: () => isEditing.value,
    onClick: handleCancel
  }
]);
</script>

自定义渲染

vue
<script setup lang="ts">
import { h } from "vue";
import { ElSwitch } from "element-plus";

const autoRefresh = ref(false);

const rightItems = computed(() => [
  {
    name: "autoRefresh",
    renderNode: () => h("div", { class: "flex items-center gap-2" }, [
      h("span", "自动刷新"),
      h(ElSwitch, {
        modelValue: autoRefresh.value,
        "onUpdate:modelValue": (val) => autoRefresh.value = val
      })
    ])
  },
  {
    name: "refresh",
    type: "refresh",
    onClick: handleRefresh
  }
]);
</script>

🎨 与其他组件配合

配合 BaseQuery

vue
<template>
  <div class="page">
    <BaseQuery
      :form="queryParam"
      :items="queryItems"
      @select="handleSearch"
      @reset="handleReset"
    />
    <BaseToolbar :items="toolbarItems" />
    <BaseTable :data="tableData" :columns="columns" />
  </div>
</template>

在 BaseTable 中使用操作列

typescript
// 表格列配置中的操作列
{
  name: "",
  label: "操作",
  width: 150,
  actions: [
    { name: "edit", type: "edit" },
    { name: "delete", type: "delete" }
  ]
}

⚠️ 注意事项

  1. 使用 computed 包装按钮配置

    typescript
    // ✅ 正确
    const toolbarItems = computed(() => [...]);
    
    // ❌ 避免
    const toolbarItems = [...];
  2. disabled/show 使用函数形式

    typescript
    // ✅ 响应式
    disabled: () => selectedRows.value.length === 0
    
    // ❌ 非响应式(只在初始化时计算一次)
    disabled: selectedRows.value.length === 0
  3. autoLoading 适用于异步操作

    typescript
    {
      name: "submit",
      autoLoading: true,   // 点击后自动 loading
      onClick: async () => {
        await submitApi();   // 完成后自动恢复
      }
    }
  4. permission 需要后端配合

    • 权限标识需要与后端菜单权限配置一致
  5. 下拉按钮的 children 中也可以有权限控制

    typescript
    {
      name: "more",
      label: "更多",
      children: [
        { name: "export", permission: "sale:order:export" },
        { name: "print", permission: "sale:order:print" }
      ]
    }

📚 相关文档

You may not distribute, modify, or sell this software without permission.