每个组件的真实字段配置、前端代码与后端适配器示例——代码区块由 npm run build:website 从真实源文件(playground.html / examples/demo)构建期抽取,非手抄
可枚举字段的选择组件:选项来自配置或数据库去重扫描,本地字面匹配过滤,自动区分精确/模糊。 在线体验 → · props 详情 →
{
field: 'department', label: '部门', componentType: 'smart_option',
options: ['研究院'],
optionSource: { type: 'distinct_scan' },
componentProps: { multiple: true },
}
{
field: 'dept',
label: '部门',
componentType: 'smart_option',
// distinct_scan:选项来自 mockApi.getFieldOptions,配置项去重补充(灰色)
optionSource: { type: 'distinct_scan' },
options: ['总经办'],
componentProps: { multiple: true },
placeholder: '输入或选择部门',
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
distinct_scan 选项集。真实 REST 后端用 SELECT DISTINCT 返回 { options: string[] } 即可。
/**
* 智能选项的选项集(挂载时一次性加载):返回该字段的 distinct 值
*/
async getFieldOptions(field) {
const options = [...new Set(EMPLOYEES.map((e) => String(e[field] ?? '')).filter(Boolean))]
return delay({ options })
}
// 多选 · 全部选项内值
{ field: 'department', componentType: 'smart_option', operator: 'in', value: ['研发部', '市场部'] }
// 多选 · 混合(点选 + 回车自定义)
{ field: 'department', componentType: 'smart_option', operator: 'mixed',
value: { exact: ['研发部'], fuzzy: ['总部'] } }
smart_option 的单选独立类型:单值互斥(下拉点选精确 / 输入文本 LIKE),后端协议与适配器和 smart_option 完全一致。 在线体验 → · props 详情 →
{
field: 'leader', label: '直属上级', componentType: 'smart_option_single',
options: ['待定'],
optionSource: { type: 'distinct_scan' },
}
{
// smart_option_single:单值互斥(下拉点选精确 / 输入文本 LIKE),与 smart_option 同协议
field: 'leader',
label: '直属上级',
componentType: 'smart_option_single',
options: ['王芳', '李强', '陈静', '刘洋'],
optionSource: { type: 'static' },
placeholder: '选择或输入上级姓名',
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
选项来源与 smart_option 完全相同(static / distinct_scan / api)。真实 REST 后端用 SELECT DISTINCT 返回 { options: string[] } 即可。
/**
* 智能选项的选项集(挂载时一次性加载):返回该字段的 distinct 值
*/
async getFieldOptions(field) {
const options = [...new Set(EMPLOYEES.map((e) => String(e[field] ?? '')).filter(Boolean))]
return delay({ options })
}
// 下拉点选 → 精确
{ field: 'leader', componentType: 'smart_option_single', operator: 'eq', value: '王芳' }
// 输入文本 → 模糊
{ field: 'leader', componentType: 'smart_option_single', operator: 'like', value: '张' }
文本 LIKE 模糊搜索:数据库联想返回候选文本,前端截取上下文片段并高亮关键词,点击上屏。 在线体验 → · props 详情 →
{
field: 'name', label: '姓名', componentType: 'text_input',
placeholder: '输入姓名关键词,如:张伟',
}
{
field: 'name',
label: '姓名',
componentType: 'text_input',
placeholder: '输入姓名(数据库联想)',
componentProps: { selectable: true, suggestLimit: 5 },
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
数据库 LIKE 联想。真实 REST 后端按 LIKE '%keyword%' 查询并返回 { suggestions: string[] };片段截取与关键词高亮在前端完成,后端返回完整文本即可。
/**
* 智能文本参考结果(数据库 LIKE 实时搜索片段)
* 对员工数据中对应字段做包含匹配,截取 前 contextBefore 字 + 匹配词 + 后 contextAfter 字
*/
async suggestFieldValues(field, keyword, { limit = 5, contextBefore = 3, contextAfter = 8 }
{ field: 'name', componentType: 'text_input', operator: 'like', value: '张' }
关键词经 AI 扩充为正向词(包含)与反向词(排除)标签集合,适合"一个词难以概括"的文本字段。 在线体验 → · props 详情 →
{
field: 'skills', label: '技能', componentType: 'semantic_search',
placeholder: '输入关键词,如:Vue',
}
{
field: 'intro',
label: '简介',
componentType: 'semantic_search',
placeholder: '输入关键词(如:开朗),AI 自动扩充',
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
语义扩充。真实后端转发 LLM:走"关键词 → 同义/反义词集合"的 prompt,返回 { positive: [], negative: [] }(均可为空数组)。
/**
* 语义搜索 AI 扩充:关键词 → 正反向词集(固定示例词)
*/
async semanticExpand(keyword, field, fieldLabel) {
if (!keyword) return delay({ positive: [], negative: [] })
return delay({
positive: [keyword, '开朗', '热情', '健谈'],
negative: ['内向', '沉默', '孤僻'],
})
}
{ field: 'skills', componentType: 'semantic_search', operator: 'like',
value: { positive: ['Vue', 'Vue.js', '前端框架'], negative: ['实习'] } }
把"年龄 ≥ 35"这类虚拟字段条件换算成真实日期列区间;AI 对话 / 规则 / 直接三模式,三精度(year / month / date)。 在线体验 → · props 详情 → · 内部运作专题 →
// 年精度(dateType: year)
{
field: 'age', label: '年龄', componentType: 'time_calc',
operator: 'gte',
targetField: 'birth_date', targetFieldLabel: '出生年份',
componentProps: { dateType: 'year', inputMode: 'ai_dialog' },
}
// 年月精度(dateType: month,默认)
{
field: 'age', label: '年龄', componentType: 'time_calc',
operator: 'gte',
targetField: 'birth_date', targetFieldLabel: '出生年月',
componentProps: { dateType: 'month', inputMode: 'ai_dialog' },
}
// 年月日精度(dateType: date)
{
field: 'age', label: '年龄', componentType: 'time_calc',
operator: 'gte',
targetField: 'birth_date', targetFieldLabel: '出生日期',
componentProps: { dateType: 'date', inputMode: 'ai_dialog' },
}
{
// time_calc 双标签字段:虚拟字段"年龄" ⇄ 真实字段"出生日期"
field: 'age',
label: '年龄',
componentType: 'time_calc',
targetField: 'birth_date',
targetFieldLabel: '出生日期',
placeholder: '如:30岁以上',
componentProps: { dateType: 'year', inputMode: 'ai_dialog' },
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
模糊自然语言 → 日期范围。真实后端转发 LLM,按 dateType 精度返回 { start, end, description };注意:简单表达式组件本地解析,不会调到这个函数,"规则 → 对话"回填也是本地确定性转换,无需反向接口。
/**
* 时间计算 AI 正向解析:自然语言 → 日期范围
* 支持:"30岁以上" / "大于30岁" / "30岁以下" / "20到30岁" / "30岁"
* fieldLabel 为虚拟字段(如"年龄"),返回真实日期字段的边界
*/
async timeParse(text, fieldLabel, dateType = 'month') {
const label = fieldLabel || '数值'
let m
// 区间:20到30岁 / 20~30 / 20-30岁
m = text.match(/(\d+)\s*(?:到|至|~|-|—)\s*(\d+)/)
if (m) {
const [, low, high] = m
return delay({
start: fmt(yearsAgo(+high), dateType),
end: fmt(yearsAgo(+low), dateType),
description: `${label} ${low} ~ ${high}`,
})
}
// 大于等于:30岁以上 / 大于等于30 / 不小于30 / 超过30
m = text.match(/(\d+)\s*岁?\s*(?:以上|及以上)/) ||
text.match(/(?:大于等于|不小于|至少|超过)\s*(\d+)/) ||
text.match(/大于\s*(\d+)/)
if (m) {
const n = +m[1]
return delay({
start: null,
end: fmt(yearsAgo(n), dateType),
description: `${label} ≥ ${n}`,
})
}
// 小于等于:30岁以下 / 小于等于30 / 不超过30 / 不足30
m = text.match(/(\d+)\s*岁?\s*(?:以下|及以下|以内)/) ||
text.match(/(?:小于等于|不超过|至多|不足)\s*(\d+)/) ||
text.match(/小于\s*(\d+)/)
if (m) {
const n = +m[1]
return delay({
start: fmt(yearsAgo(n), dateType),
end: null,
description: `${label} ≤ ${n}`,
})
}
// 等于:30岁 / 等于30
m = text.match(/(\d+)/)
if (m) {
const n = +m[1]
return delay({
start: fmt(yearsAgo(n + 1), dateType),
end: fmt(yearsAgo(n), dateType),
description: `${label} = ${n}`,
})
}
return delay(null)
}
// 年龄 ≥ 35(节点恒为 V 侧语义;resolved 附带 D 侧日期区间)
{ field: 'age', componentType: 'time_calc', operator: 'gte', value: 35,
targetField: 'birth_date', fieldLabel: '年龄',
resolved: { field: 'birth_date', start: null, end: '1991-07' } }
数值比较筛选:规则模式(算符 + 数值)与 AI 对话模式一键切换;面板算符可点击循环、数值可编辑。 在线体验 → · props 详情 →
{
field: 'perf_score', label: '绩效分', componentType: 'number_calc',
operator: 'gte',
componentProps: { inputMode: 'expression' },
}
{
field: 'perf_score',
label: '绩效分',
componentType: 'number_calc',
placeholder: '如:大于80',
componentProps: { inputMode: 'ai_dialog' },
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
模糊自然语言 → 算符 + 数值。真实后端转发 LLM,返回 { operator, value, value2? }(value2 仅 between);简单表达式同样由组件本地解析,不调此函数。
/**
* 数值计算 AI 解析:自然语言 → 算符 + 数值
* 支持:"大于30" / "大于等于30" / "小于30" / "20到40" / "等于30"
*/
async numberParse(text, field, fieldLabel) {
let m
m = text.match(/(\d+(?:\.\d+)?)\s*(?:到|至|~|-|—)\s*(\d+(?:\.\d+)?)/)
if (m) {
return delay({ operator: 'between', value: +m[1], value2: +m[2] })
}
m = text.match(/(?:大于等于|不小于|至少)\s*(\d+(?:\.\d+)?)/) || text.match(/(\d+(?:\.\d+)?)\s*(?:以上|及以上)/)
if (m) {
return delay({ operator: 'gte', value: +m[1] })
}
m = text.match(/(?:小于等于|不超过|至多)\s*(\d+(?:\.\d+)?)/) || text.match(/(\d+(?:\.\d+)?)\s*(?:以下|及以下|以内)/)
if (m) {
return delay({ operator: 'lte', value: +m[1] })
}
m = text.match(/(?:大于|超过)\s*(\d+(?:\.\d+)?)/)
if (m) {
return delay({ operator: 'gt', value: +m[1] })
}
m = text.match(/(?:小于|不足)\s*(\d+(?:\.\d+)?)/)
if (m) {
return delay({ operator: 'lt', value: +m[1] })
}
m = text.match(/(\d+(?:\.\d+)?)/)
if (m) {
return delay({ operator: 'eq', value: +m[1] })
}
return delay(null)
}
// 非区间
{ field: 'perf_score', componentType: 'number_calc', operator: 'gte', value: 80 }
// 区间
{ field: 'perf_score', componentType: 'number_calc', operator: 'between',
value: { start: 60, end: 80 } }
二值字段的三态开关:开 / 关 / 未设置(不参与筛选),标签可自定义。 在线体验 → · props 详情 →
{
field: 'status', label: '在职状态', componentType: 'boolean_switch',
componentProps: { trueLabel: '在职', falseLabel: '离职' },
}
{
field: 'status',
label: '在职状态',
componentType: 'boolean_switch',
componentProps: { trueLabel: '在职', falseLabel: '离职' },
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
本组件为纯前端交互(开关切换直接产出 eq 节点),不需要任何适配器函数。后端按 Criteria 节点的 eq + 标签字符串生成查询条件即可。
{ field: 'status', componentType: 'boolean_switch', operator: 'eq', value: '在职' }
单个标识码精确查询:一个可清空的输入框,固定 eq。 在线体验 → · props 详情 →
{
field: 'emp_no', label: '工号', componentType: 'id_input',
placeholder: '输入工号,如:E100231',
}
{
field: 'emp_no',
label: '工号',
componentType: 'id_input',
placeholder: '输入工号(精确匹配,如 EMP-1001)',
}
// 模板(全部组件统一用法):
<FilterField
:field="f"
v-model="values[f.field]"
@criteria-change="onCriteriaChange(f.field, $event)"
@ai-loading="aiLoadingField = $event ? f.field : ''"
@ai-error="(e) => console.error(f.field, e)"
/>
本组件为纯前端交互(输入即产出 eq 节点),不需要任何适配器函数。
{ field: 'emp_no', componentType: 'id_input', operator: 'eq', value: 'E100231' }