Appearance
BizOrgTreeSelect 组织树选择
部门 / 组织选择的表单字段:el-tree-select 业务封装——静态全量树与懒加载(展开拉子级)二选一,单选 / 多选、仅叶子可选、本地搜索。区别于 BizTablePicker(弹窗选人 / 选单据),本组件是「选部门」的字段级控件;组件零接口,数据由业务侧注入。
基础用法
v-model:111(父级部门置灰不可选,输入关键词过滤)
静态树 +
leaf-only 父级置灰 + 本地名称搜索。<script setup lang="ts">
/** 基础用法:静态组织树 + 本地搜索 + 仅叶子可选 */
import { ref } from 'vue'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
import type { BizOrgTreeNode } from 'vue3-ep-biz-lib'
const tree: BizOrgTreeNode[] = [
{
id: 1,
name: '总部',
children: [
{
id: 11,
name: '研发中心',
children: [
{ id: 111, name: '前端组', isLeaf: true },
{ id: 112, name: '后端组', isLeaf: true },
],
},
{
id: 12,
name: '运营中心',
children: [
{ id: 121, name: '用户运营组', isLeaf: true },
{ id: 122, name: '内容运营组', isLeaf: true },
],
},
],
},
]
const deptId = ref<number | undefined>(111)
</script>
<template>
<div>
<BizOrgTreeSelect v-model="deptId" :data="tree" leaf-only style="max-width: 320px" />
<div class="tip">v-model:{{ deptId }}(父级部门置灰不可选,输入关键词过滤)</div>
</div>
</template>
<style scoped>
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
懒加载 · 多选
请选择部门
v-model:(空)(展开节点时才请求子级)
load-request 展开时拉子级(大组织架构不必全量下发);multiple 多选(默认父子不级联,check-strictly 可配)。<script setup lang="ts">
/** 进阶:懒加载(按需拉子级)+ 多选 + 父子不级联 */
import { ref } from 'vue'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
import type { BizOrgTreeNode } from 'vue3-ep-biz-lib'
/** mock:三级组织,逐级返回子节点 */
const loadRequest = async (node: BizOrgTreeNode): Promise<BizOrgTreeNode[]> => {
await new Promise((resolve) => setTimeout(resolve, 300))
if (node.id === 'root') {
return [
{ id: 'hq', name: '集团总部' },
{ id: 'sh', name: '上海分公司' },
]
}
if (node.id === 'hq') {
return [
{ id: 'hq-it', name: '信息部', isLeaf: true },
{ id: 'hq-fin', name: '财务部', isLeaf: true },
]
}
if (node.id === 'sh') {
return [
{ id: 'sh-dev', name: '研发部', isLeaf: true },
{ id: 'sh-sale', name: '销售部', isLeaf: true },
]
}
return []
}
const deptIds = ref<Array<string | number>>([])
</script>
<template>
<div>
<BizOrgTreeSelect
v-model="deptIds"
:load-request="loadRequest"
multiple
style="max-width: 320px"
/>
<div class="tip">v-model:{{ deptIds.join('、') || '(空)' }}(展开节点时才请求子级)</div>
</div>
</template>
<style scoped>
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
表单必填集成
el-form rules 必填 + leaf-only 只能选末级部门(入职登记场景)。
<script setup lang="ts">
/** el-form 集成:部门必填校验 + 提交 */
import { reactive, ref } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
const formRef = ref<FormInstance>()
const form = reactive({ name: '', deptId: undefined as string | number | undefined })
const tree = [
{ id: 1, name: '总部', children: [
{ id: 11, name: '研发中心', children: [{ id: 111, name: '前端组', isLeaf: true }, { id: 112, name: '后端组', isLeaf: true }] },
{ id: 12, name: '运营中心', children: [{ id: 121, name: '用户运营组', isLeaf: true }] },
] },
]
const rules: FormRules = { deptId: [{ required: true, message: '请选择所属部门', trigger: 'change' }] }
const submit = (): void => {
void formRef.value?.validate((valid) => {
if (valid) ElMessage.success(`入职部门:${form.deptId}`)
})
}
</script>
<template>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="90px" style="max-width: 420px">
<ElFormItem label="姓名">
<ElInput v-model="form.name" placeholder="员工姓名" />
</ElFormItem>
<ElFormItem label="所属部门" prop="deptId">
<BizOrgTreeSelect v-model="form.deptId" :data="tree" leaf-only />
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="submit">提交</ElButton>
</ElFormItem>
</ElForm>
</template>
显示代码
编辑回显(值先到)
v-model=112——树晚到 1s 替换,选中项始终显示名称(回显占位节点技巧)
回显值先到、组织树后到:预埋占位节点保证显示名称而非 id。
<script setup lang="ts">
/** 编辑回显:值先到、树后到——把回显节点预埋进 data 避免显示 id */
import { onMounted, ref } from 'vue'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
import type { BizOrgTreeNode } from 'vue3-ep-biz-lib'
const deptId = ref<number | undefined>(112)
/** 先用回显路径占位(选中链路可显示),接口树到位后整体替换 */
const data = ref<BizOrgTreeNode[]>([
{ id: 1, name: '总部', children: [{ id: 11, name: '研发中心', children: [{ id: 112, name: '后端组(回显占位)', isLeaf: true }] }] },
])
onMounted(() => {
setTimeout(() => {
data.value = [
{ id: 1, name: '总部', children: [
{ id: 11, name: '研发中心', children: [{ id: 111, name: '前端组', isLeaf: true }, { id: 112, name: '后端组', isLeaf: true }] },
{ id: 12, name: '运营中心', children: [{ id: 121, name: '用户运营组', isLeaf: true }] },
] },
]
}, 1000)
})
</script>
<template>
<div>
<BizOrgTreeSelect v-model="deptId" :data="data" style="max-width: 320px" />
<div class="tip">v-model={{ deptId }}——树晚到 1s 替换,选中项始终显示名称(回显占位节点技巧)</div>
</div>
</template>
<style scoped>
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
父子级联多选
勾「华东大区」会级联勾中两个分公司(默认 check-strictly=true 不级联)
check-strictly=false 勾父级联勾子(数据权限授权场景)。
<script setup lang="ts">
/** check-strictly=false 父子级联:勾父即勾全部子级(数据权限场景) */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
const tree = [
{ id: 1, name: '华东大区', children: [
{ id: 11, name: '上海分公司', isLeaf: true },
{ id: 12, name: '杭州分公司', isLeaf: true },
] },
{ id: 2, name: '华北大区', children: [
{ id: 21, name: '北京分公司', isLeaf: true },
] },
]
const deptIds = ref<Array<string | number>>([11, 12])
const show = (): void => {
ElMessage.info(`授权部门:${deptIds.value.join('、') || '(空)'}`)
}
</script>
<template>
<div>
<BizOrgTreeSelect v-model="deptIds" :data="tree" multiple :check-strictly="false" style="max-width: 320px" />
<div class="ops">
<ElButton size="small" @click="show">查看已授权</ElButton>
<span class="tip">勾「华东大区」会级联勾中两个分公司(默认 check-strictly=true 不级联)</span>
</div>
</div>
</template>
<style scoped>
.ops {
display: flex;
align-items: center;
gap: 10px;
margin-top: 8px;
}
.tip {
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
大组织架构搜索
输入「深圳」等关键词即时过滤已加载节点;缺省展开全部便于回显定位
180 节点静态树 + 本地名称过滤,快速定位目标部门。
<script setup lang="ts">
/** 大组织架构(180 节点)本地搜索定位 */
import { ref } from 'vue'
import { BizOrgTreeSelect } from 'vue3-ep-biz-lib'
import type { BizOrgTreeNode } from 'vue3-ep-biz-lib'
/** 生成 6 大区 × 6 城市 × 5 部门 = 180 节点 */
const build = (): BizOrgTreeNode[] => {
const regions = ['华东', '华南', '华北', '西南', '华中', '东北']
const cities = ['上海', '杭州', '广州', '深圳', '北京', '成都']
const depts = ['研发部', '运营部', '销售部', '财务部', '人事部']
return regions.slice(0, 6).map((r, ri) => ({
id: `r${ri}`,
name: `${r}大区`,
children: cities.slice(ri % 2, ri % 2 + 3).map((c, ci) => ({
id: `r${ri}-c${ci}`,
name: `${c}分公司`,
children: depts.map((d, di) => ({ id: `r${ri}-c${ci}-d${di}`, name: `${c}${d}`, isLeaf: true })),
})),
}))
}
const data = build()
const deptId = ref<string | number | undefined>('r2-c1-d0')
</script>
<template>
<div>
<BizOrgTreeSelect v-model="deptId" :data="data" style="max-width: 320px" />
<div class="tip">输入「深圳」等关键词即时过滤已加载节点;缺省展开全部便于回显定位</div>
</div>
</template>
<style scoped>
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| model-value / v-model | key | key[] | — | 选中部门 id(multiple 数组) |
| data | BizOrgTreeNode[] | [] | 静态全量树(与 load-request 二选一) |
| load-request | (node) => Promise<BizOrgTreeNode[]> | — | 懒加载子级(data 为空时启用) |
| multiple | boolean | false | 多选(checkbox 形态) |
| leaf-only | boolean | false | 仅叶子可选(父级置灰;懒加载模式按节点 isLeaf 标记) |
| check-strictly | boolean | true | 父子不级联(multiple 模式;传 false 勾父即勾全部子级) |
| filterable | boolean | true | 本地按名称搜索(懒加载仅过滤已加载节点) |
| placeholder | string | '请选择部门' | 占位文案 |
| disabled | boolean | false | 禁用 |
| clearable | boolean | true | 可清空 |
其余属性经
v-bind="$attrs"透传 el-tree-select(node-key已内置"id",节点字段映射label=name/isLeaf=isLeaf已内置)。
BizOrgTreeNode:id、name 必填;children 子级;isLeaf 叶子标记(懒加载 + leafOnly 场景建议后端返回,未标记的节点默认可选)。
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:model-value | (value) | 选中变化(其余事件透传 el-tree-select) |
Slots
| 插槽名 | 说明 |
|---|---|
| — | 透传 el-tree-select 插槽 |
Expose
| 属性/方法 | 说明 |
|---|---|
| — | 透传 el-tree-select 实例(模板 ref 直接访问) |