Appearance
BizMentionInput @提及输入
文本域 + 候选人弹层的 @ 提及输入:输入 @ 唤起候选、随输随筛(姓名 / 拼音等 keyword)、↑↓ + Enter / Tab 键盘选择、选中插入 @姓名 并回填光标、getMentions() 提取已提及人员。评论 @ 提醒、协作通知、工单转发选人等场景的轻量选人输入(重型选人用 BizTablePicker)。
基础用法
输入 @ 唤起候选;支持姓名或拼音关键字筛选,↑↓ 选择、Enter / Tab 确认
输入
@ 唤起候选人,继续输入筛选;点击或 ↑↓ + Enter 选中插入。<script setup lang="ts">
/** 场景:基础——输入 @ 唤起候选,随输随筛,点击或键盘选中插入 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('本周迭代评审请相关同学关注:')
const members: BizMentionMember[] = [
{ id: 'u1', name: '张伟', keyword: 'zhangwei ZW001', description: '前端组 · 组长' },
{ id: 'u2', name: '李娜', keyword: 'lina LN002', description: '产品组 · PM' },
{ id: 'u3', name: '王强', keyword: 'wangqiang WQ003', description: '后端组 · 开发' },
{ id: 'u4', name: '刘洋', keyword: 'liuyang LY004', description: '测试组 · QA' },
{ id: 'u5', name: '陈静', keyword: 'chenjing CJ005', description: '设计组 · UI' },
{ id: 'u6', name: '赵磊', keyword: 'zhaolei ZL006', description: '运维组 · SRE' },
]
</script>
<template>
<div>
<BizMentionInput v-model="value" :members="members" />
<p style="margin: 10px 0 0; font-size: 12px; color: var(--el-text-color-secondary)">
输入 @ 唤起候选;支持姓名或拼音关键字筛选,↑↓ 选择、Enter / Tab 确认
</p>
</div>
</template>
显示代码
自定义触发字符
trigger 支持任意字符与多字符组合(如 '@@'),getMentions 同样按自定义触发符提取
trigger 支持 #(话题)、@@(多字符精确唤起)等自定义触发。<script setup lang="ts">
/** 场景:自定义触发字符——# 唤起话题列表 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('')
const topics: BizMentionMember[] = [
{ id: 't1', name: '需求评审', description: '话题' },
{ id: 't2', name: '故障复盘', description: '话题' },
{ id: 't3', name: '版本发布', description: '话题' },
{ id: 't4', name: '周报', description: '话题' },
]
</script>
<template>
<div>
<BizMentionInput v-model="value" :members="topics" trigger="#" placeholder="输入内容,# 关联话题" />
<p style="margin: 10px 0 0; font-size: 12px; color: var(--el-text-color-secondary)">
trigger 支持任意字符与多字符组合(如 '@@'),getMentions 同样按自定义触发符提取
</p>
</div>
</template>
显示代码
远程检索
filter-method 返回 true 关闭本地过滤,候选完全由远程结果驱动
filter-method 接管过滤 + loading 展示搜索态,search 事件回传查询串做远程请求。<script setup lang="ts">
/** 场景:远程检索——filter-method 接管过滤,search 事件发起请求,loading 展示搜索态 */
import { onBeforeUnmount, ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('')
const loading = ref(false)
const members = ref<BizMentionMember[]>([])
/** 模拟远程接口:按工号 / 姓名前缀查询(实际项目替换为 axios 请求) */
const remoteSearch = (query: string): void => {
loading.value = true
setTimeout(() => {
const pool: BizMentionMember[] = [
{ id: 'r1', name: '欧阳锋', keyword: 'oyf 1001', description: '华东大区 · 销售' },
{ id: 'r2', name: '欧阳娜娜', keyword: 'oynn 1002', description: '华北大区 · 运营' },
{ id: 'r3', name: '司马光', keyword: 'smg 1003', description: '总部 · 财务' },
{ id: 'r4', name: '上官婉儿', keyword: 'sgwe 1004', description: '总部 · 人事' },
]
members.value = query === '' ? pool : pool.filter((m) => `${m.name}${m.keyword}`.includes(query))
loading.value = false
}, 500)
}
let timer: ReturnType<typeof setTimeout> | null = null
const onSearch = (query: string): void => {
if (timer !== null) clearTimeout(timer)
timer = setTimeout(() => remoteSearch(query), 300)
}
onBeforeUnmount(() => {
if (timer !== null) clearTimeout(timer)
})
</script>
<template>
<div>
<BizMentionInput
v-model="value"
:members="members"
:loading="loading"
:filter-method="() => true"
placeholder="输入 @ 并输入关键字,远程检索人员"
@search="onSearch"
/>
<p style="margin: 10px 0 0; font-size: 12px; color: var(--el-text-color-secondary)">
filter-method 返回 true 关闭本地过滤,候选完全由远程结果驱动
</p>
</div>
</template>
显示代码
自定义候选项
item 插槽接管候选行(头像 / 部门 / 工号自由排版)。<script setup lang="ts">
/** 场景:item 插槽接管候选行——头像 + 姓名 + 工号 / 部门自由排版 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('')
const members: BizMentionMember[] = [
{ id: 'u1', name: '张伟', keyword: 'ZW001', description: '前端组' },
{ id: 'u2', name: '李娜', keyword: 'LN002', description: '产品组' },
{ id: 'u3', name: '王强', keyword: 'WQ003', description: '后端组' },
]
</script>
<template>
<div>
<BizMentionInput v-model="value" :members="members">
<template #item="{ member, active }">
<span class="demo-mention-avatar">{{ member.name.charAt(0) }}</span>
<span class="demo-mention-info">
<span class="demo-mention-name" :style="active ? { color: 'var(--el-color-primary)' } : undefined">
{{ member.name }}
</span>
<span class="demo-mention-code">工号 {{ member.keyword }}</span>
</span>
<span class="demo-mention-dept">{{ member.description }}</span>
</template>
</BizMentionInput>
</div>
</template>
<style scoped>
.demo-mention-avatar {
flex-shrink: 0;
width: 30px;
height: 30px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background-color: var(--el-color-primary-light-8);
color: var(--el-color-primary);
font-size: 13px;
}
.demo-mention-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
line-height: 18px;
}
.demo-mention-name {
font-size: 13px;
}
.demo-mention-code {
font-size: 12px;
color: var(--el-text-color-secondary);
}
.demo-mention-dept {
font-size: 12px;
color: var(--el-text-color-secondary);
border: 1px solid var(--el-border-color-lighter);
border-radius: var(--el-border-radius-base);
padding: 0 6px;
}
</style>
显示代码
提及提取与程序化插入
已提及(getMentions 实时提取):
getMentions 提取文本中已提及人员(含位置),insertMention 程序化追加。<script setup lang="ts">
/** 场景:getMentions 提取已提及人员 + insertMention 程序化插入 */
import { computed, ref } from 'vue'
import { ElButton, ElTag } from 'element-plus'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionInputInstance, BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('请 @张伟 @李娜 确认方案')
const members: BizMentionMember[] = [
{ id: 'u1', name: '张伟', keyword: 'ZW001', description: '前端组' },
{ id: 'u2', name: '李娜', keyword: 'LN002', description: '产品组' },
{ id: 'u3', name: '王强', keyword: 'WQ003', description: '后端组' },
]
const inputRef = ref<BizMentionInputInstance | null>(null)
const mentions = computed(() => inputRef.value?.getMentions() ?? [])
const insert = (member: BizMentionMember): void => {
inputRef.value?.insertMention(member)
}
</script>
<template>
<div>
<BizMentionInput ref="inputRef" v-model="value" :members="members" />
<div style="display: flex; gap: 8px; margin-top: 10px">
<ElButton size="small" @click="insert(members[2])">程序化提及王强</ElButton>
<ElButton size="small" @click="inputRef?.clear()">清空</ElButton>
</div>
<p style="margin: 10px 0 4px; font-size: 12px; color: var(--el-text-color-secondary)">
已提及(getMentions 实时提取):
</p>
<ElTag v-for="hit in mentions" :key="hit.member.id" style="margin-right: 6px" size="small">
{{ hit.member.name }}({{ hit.start }}~{{ hit.end }})
</ElTag>
</div>
</template>
显示代码
禁用与只读
disabled / readonly 透传至文本域,不唤起候选弹层。<script setup lang="ts">
/** 场景:禁用与只读——不唤起候选弹层,透传 el-input 状态样式 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const disabledValue = ref('已提交的说明内容 @张伟')
const readonlyValue = ref('审阅中的说明内容 @李娜')
const members: BizMentionMember[] = [
{ id: 'u1', name: '张伟', keyword: 'ZW001', description: '前端组' },
{ id: 'u2', name: '李娜', keyword: 'LN002', description: '产品组' },
]
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px">
<BizMentionInput v-model="disabledValue" :members="members" disabled />
<BizMentionInput v-model="readonlyValue" :members="members" readonly />
</div>
</template>
显示代码
头像展示
members 配 avatar 字段时候选行渲染真实头像,缺省回退姓名首字圆形底。<script setup lang="ts">
/** 场景:头像展示——members 配 avatar 字段,候选行渲染真实头像 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('值班同学请注意:')
const members: BizMentionMember[] = [
{ id: 'u1', name: '张伟', keyword: 'ZW001', description: '前端组 · 组长', avatar: 'https://i.pravatar.cc/64?img=11' },
{ id: 'u2', name: '李娜', keyword: 'LN002', description: '产品组 · PM', avatar: 'https://i.pravatar.cc/64?img=5' },
{ id: 'u3', name: '王强', keyword: 'WQ003', description: '后端组 · 开发', avatar: 'https://i.pravatar.cc/64?img=33' },
{ id: 'u4', name: '刘洋', keyword: 'LY004', description: '测试组 · QA' },
]
</script>
<template>
<BizMentionInput v-model="value" :members="members" />
</template>
显示代码
大文本域与弹层高度
14 个候选超出 max-height 180 时弹层内部滚动;换行后输入 @ 弹层跟随光标所在行下移
rows=6 光标行跟随唤起位置;候选超出 max-height 时弹层内部滚动。<script setup lang="ts">
/** 场景:大文本域 + 弹层高度——rows=6 光标行跟随,maxHeight 弹层内滚动 */
import { ref } from 'vue'
import { BizMentionInput } from 'vue3-ep-biz-lib'
import type { BizMentionMember } from 'vue3-ep-biz-lib'
const value = ref('评审结论:\n\n补充意见:')
const members: BizMentionMember[] = Array.from({ length: 14 }, (_, i) => ({
id: `m${i}`,
name: `评审成员${String(i + 1).padStart(2, '0')}`,
keyword: `M${i + 1}`,
description: `评审组 ${i + 1}`,
}))
</script>
<template>
<div>
<BizMentionInput v-model="value" :members="members" :rows="6" :max-height="180" />
<p style="margin: 10px 0 0; font-size: 12px; color: var(--el-text-color-secondary)">
14 个候选超出 max-height 180 时弹层内部滚动;换行后输入 @ 弹层跟随光标所在行下移
</p>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| v-model | string | '' | 输入内容 |
| members | BizMentionMember[] | [] | 候选人员(id / name / keyword / avatar / description) |
| trigger | string | '@' | 触发字符,支持多字符(如 '@@') |
| placeholder | string | '请输入内容,@提及人员' | 占位文案 |
| disabled | boolean | false | 禁用 |
| readonly | boolean | false | 只读 |
| rows | number | 3 | 文本域行数(弹层纵向跟随光标行) |
| max-height | number | string | 264 | 候选弹层最大高度(超出滚动) |
| filter-method | (query, member) => boolean | — | 自定义过滤;缺省按 name / keyword 不区分大小写子串匹配 |
| empty-text | string | '无匹配人员' | 候选为空文案 |
| loading | boolean | false | 远程检索 loading(弹层内展示搜索中) |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:modelValue | (value: string) | 输入内容变化 |
| change | (value: string) | 同 update:modelValue(含选中插入) |
| search | (query: string) | 唤起候选后查询串变化(远程检索时机) |
| select | (member: BizMentionMember) | 选中候选人(含程序化插入) |
Slots
| 插槽名 | 作用域 | 说明 |
|---|---|---|
| item | { member, active, index } | 候选行接管 |
Expose
| 名称 | 类型 | 说明 |
|---|---|---|
| focus | () => void | 聚焦文本域 |
| blur | () => void | 失焦文本域 |
| clear | () => void | 清空内容 |
| getMentions | () => BizMentionHit[] | 提取已提及人员(member + start / end 位置) |
| insertMention | (member) => void | 程序化末尾追加提及 |
TIP
提及识别按 @姓名 后跟空白或结尾判定,姓名含空格的成员不适合默认插入格式——可经 select 事件自定义回写。其余文本域能力(maxlength / autosize 等)经 attrs 透传至 el-input。