Mention
富文本提及输入框,用于在长文本里快速 @ 成员或话题。
Markup Schema 基础用法
<script lang="ts" setup>
import { createForm } from '@formily/core'
import { FormItem, Mention, Submit } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const form = createForm()
const { SchemaField, SchemaStringField } = createSchemaField({
components: {
FormItem,
Mention,
},
})
const teammateOptions = [
{ value: 'Jasmine', label: 'Jasmine · 产品负责人' },
{ value: 'Leo', label: 'Leo · 交互设计' },
{ value: 'Mia', label: 'Mia · 前端开发' },
{ value: 'Oscar', label: 'Oscar · 测试同学' },
]
const topicOptions = [
{ value: '体验优化', label: '#体验优化' },
{ value: '周版本', label: '#周版本' },
{ value: '性能调优', label: '#性能调优' },
{ value: '风险提示', label: '#风险提示' },
]
async function log(value: any) {
console.log(value)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaStringField
name="status"
title="团队动态"
x-decorator="FormItem"
x-component="Mention"
default="今天和 @Jasmine 对齐了交互稿,准备同步 @Mia 进入联调。"
:x-component-props="{
rows: 3,
placeholder: '输入 @ 召唤同事',
options: teammateOptions,
}"
/>
<SchemaStringField
name="timeline"
title="带话题的更新"
x-decorator="FormItem"
x-component="Mention"
:x-component-props="{
rows: 3,
prefix: ['@', '#'],
split: ' ',
placeholder: '支持同时输入 @ 同事或 # 话题',
options: [...teammateOptions, ...topicOptions],
}"
/>
</SchemaField>
<Submit style="margin-top: 12px" @submit="log">
提交
</Submit>
</FormProvider>
</template>团队动态:
带话题的更新:
查看源码
Markup Schema 进阶能力
<script lang="ts" setup>
import { createForm } from '@formily/core'
import { FormItem, Mention, Submit } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ref } from 'vue'
const form = createForm()
const { SchemaField, SchemaStringField } = createSchemaField({
components: {
FormItem,
Mention,
},
})
const reviewerPool = [
{ value: 'Elena', label: 'Elena · 设计评审' },
{ value: 'Kingsley', label: 'Kingsley · 开发评审' },
{ value: 'Doris', label: 'Doris · QA' },
{ value: 'Nico', label: 'Nico · 产品' },
]
const reviewerOptions = ref([...reviewerPool])
const mentionLoading = ref(false)
const aliasOptions = [
{ id: 'u1001', nickname: 'Alex (后端)', inactive: false },
{ id: 'u1002', nickname: 'Becca (客户端)', inactive: true },
{ id: 'u1003', nickname: 'Chloe (视觉)', inactive: false },
]
function handleSearch(pattern: string) {
mentionLoading.value = true
const keyword = pattern.trim().toLowerCase()
setTimeout(() => {
reviewerOptions.value = keyword ? reviewerPool.filter(option => option.value.toLowerCase().includes(keyword)) : [...reviewerPool]
mentionLoading.value = false
}, 400)
}
function checkReviewer(pattern: string) {
return reviewerPool.some(option => option.value === pattern)
}
async function log(value: any) {
console.log(value)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaStringField
name="review"
title="评审记录"
x-decorator="FormItem"
x-component="Mention"
default="@Elena 帮忙看下交互动效,@Kingsley 关注一下性能回归。"
:x-component-props="{
rows: 3,
placeholder: '输入 @ 选择评审人,支持 Backspace 整体删除',
options: reviewerOptions,
loading: mentionLoading,
whole: true,
checkIsWhole: checkReviewer,
onSearch: handleSearch,
}"
/>
<SchemaStringField
name="alias"
title="别名配置"
x-decorator="FormItem"
x-component="Mention"
:x-component-props="{
rows: 2,
placeholder: '通过 props 映射任意字段,自定义禁用规则',
options: aliasOptions,
props: { value: 'id', label: 'nickname', disabled: 'inactive' },
showArrow: true,
}"
/>
</SchemaField>
<Submit style="margin-top: 12px" @submit="log">
保存
</Submit>
</FormProvider>
</template>评审记录:
别名配置:
查看源码
Template 插槽拓展
<script lang="ts" setup>
import { createForm } from '@formily/core'
import { FormItem, Mention, Submit } from '@silver-formily/element-plus'
import { Field, FormProvider } from '@silver-formily/vue'
const form = createForm()
const mentionOptions = [
{ value: 'UI指南', label: 'UI 指南更新', owner: 'Jasmine' },
{ value: '性能调研', label: '性能调研', owner: 'Kingsley', disabled: true },
{ value: '发布 checklist', label: '发布 checklist', owner: 'Mia' },
]
async function log(value: any) {
console.log(value)
}
</script>
<template>
<FormProvider :form="form">
<Field
name="post"
title="更新内容"
:decorator="[FormItem]"
:component="[Mention, { rows: 3, placeholder: '在此输入 @ 信息并自定义下拉内容', options: mentionOptions }]"
initial-value="@UI指南 已补充视觉规范,@发布 checklist 请补全 QA 入口。"
>
<template #prefix>
<span class="mention-prefix">更新</span>
</template>
<template #suffix>
<span class="mention-suffix">⌘ + Enter 发布</span>
</template>
<template #header="{ field }">
<div class="mention-panel-header">
当前字段:{{ field?.title }}
</div>
</template>
<template #label="{ item, index, field }">
<div class="mention-option">
<span class="mention-option__index">{{ index + 1 }}</span>
<div class="mention-option__body">
<strong>{{ item.label }}</strong>
<small>Owner: {{ item.owner }}</small>
<small v-if="field?.value">源字段值:{{ field?.value.length }}</small>
</div>
</div>
</template>
<template #footer>
<div class="mention-panel-footer">
可以通过 slots 访问 field,并渲染富文本内容
</div>
</template>
</Field>
<Submit style="margin-top: 12px" @submit="log">
保存
</Submit>
</FormProvider>
</template>
<style scoped>
.mention-prefix {
display: inline-flex;
align-items: center;
padding: 0 8px;
font-size: 12px;
color: var(--vp-c-text-2);
}
.mention-suffix {
font-size: 12px;
color: var(--vp-c-text-3);
}
.mention-panel-header,
.mention-panel-footer {
font-size: 12px;
padding: 4px 8px;
color: var(--vp-c-text-2);
}
.mention-option {
display: flex;
gap: 8px;
align-items: center;
}
.mention-option__index {
width: 20px;
height: 20px;
border-radius: 999px;
background-color: var(--vp-c-bg-soft);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.mention-option__body {
display: flex;
flex-direction: column;
font-size: 12px;
color: var(--vp-c-text-2);
}
.mention-option__body strong {
color: var(--vp-c-text-1);
}
.mention-option__body small {
font-size: 11px;
}
</style>更新内容:
更新⌘ + Enter 发布
查看源码
API
参考 https://element-plus.org/zh-CN/component/mention.html
- 组件完整透传 Element Plus Mention 的属性/事件,可通过
dataSource(或直接传入options)管理下拉数据,还可以结合whole、checkIsWhole、prefix等能力控制提及行为。 props属性用于映射自定义字段名,例如远端返回id、nickname等键值时无需手动转换。onSearch(pattern, prefix, field)会在输入触发字符后被调用,第三个参数会注入当前 Formilyfield,方便实现远程搜索时拉起 loading 并更新options。
插槽
| 插槽名 | 描述 | 类型 |
|---|---|---|
| prefix | 输入框前置内容 | -- |
| suffix | 输入框后置内容 | -- |
| prepend | 输入框前缀内容(位于 prefix 之前) | -- |
| append | 输入框后缀内容(位于 suffix 之后) | -- |
| header | 下拉面板头部,额外注入 field 引用 | object |
| footer | 下拉面板底部,额外注入 field 引用 | object |
| label | 自定义选项渲染,除了 item、index 之外还会注入 field | object |
| loading | 自定义加载状态 | -- |