feat(admin): WallAdvanced 高级折叠区(正则/提醒迁移)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
<!--
|
||||
高级折叠区(Task 9):引擎级规则编辑——price/reask 正则行编辑器(双形态
|
||||
行编辑:pattern + flags + comment)+ triggers/zero_number 单正则 +
|
||||
reminders 六类话术,自 WallPane 原样迁入(复制不移动——WallPane 旧块
|
||||
Task 10 才删,两处并存无冲突)。默认全部折叠;数据经 props.model 直改
|
||||
reactive(与 ScenarioCard 同契约)。
|
||||
-->
|
||||
<template>
|
||||
<div class="wall-advanced">
|
||||
<ElAlert
|
||||
type="warning"
|
||||
:closable="false"
|
||||
class="wa-warn"
|
||||
title="引擎级规则——改错可能导致拦截失灵或 AI 行为异常"
|
||||
description="以下正则与指令由引擎直接消费;不确定的不要动;改坏可用版本历史回滚。"
|
||||
/>
|
||||
|
||||
<ElCollapse v-model="openSections" class="wa-collapse">
|
||||
<!-- 正则列表区:价格墙 / 再索要墙(双形态行编辑器) -->
|
||||
<ElCollapseItem
|
||||
v-for="section in patternListSections"
|
||||
:key="section.key"
|
||||
:name="section.key"
|
||||
:title="section.title"
|
||||
>
|
||||
<p class="section-note">{{ section.plain }}</p>
|
||||
<p class="section-note">{{ section.note }}</p>
|
||||
<div
|
||||
v-for="(row, i) in section.rows.value"
|
||||
:key="i"
|
||||
class="regex-row"
|
||||
>
|
||||
<div class="regex-line">
|
||||
<span class="row-index">{{ i + 1 }}</span>
|
||||
<ElInput
|
||||
v-model="row.pattern"
|
||||
placeholder="正则表达式(Python re 语法)"
|
||||
class="mono-input pattern-input"
|
||||
/>
|
||||
<ElSelect v-model="row.flags" class="flags-select" title="标志位">
|
||||
<ElOption value="" label="无" />
|
||||
<ElOption value="i" label="i 忽略大小写" />
|
||||
</ElSelect>
|
||||
<ElButton
|
||||
:type="row.comment ? 'primary' : 'default'"
|
||||
link
|
||||
title="设计备注(comment,随词表保存但不参与编译)"
|
||||
@click="row.showComment = !row.showComment"
|
||||
>
|
||||
备注{{ row.comment ? '●' : '○' }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
type="danger"
|
||||
link
|
||||
title="删除此条"
|
||||
@click="section.remove(i)"
|
||||
>
|
||||
删
|
||||
</ElButton>
|
||||
</div>
|
||||
<ElInput
|
||||
v-if="row.showComment"
|
||||
v-model="row.comment"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 6 }"
|
||||
placeholder="comment:设计说明(防误杀断言等),保存为条目 comment 字段"
|
||||
class="comment-input"
|
||||
/>
|
||||
<p v-else-if="row.comment" class="comment-view mono">{{ row.comment }}</p>
|
||||
</div>
|
||||
<ElButton size="small" @click="section.add()">+ 添加一条</ElButton>
|
||||
</ElCollapseItem>
|
||||
|
||||
<!-- 单正则区:triggers 三键 / zero_number 两键(合并为一个折叠项) -->
|
||||
<ElCollapseItem name="singles" title="触发词与零号码规则(triggers / zero_number)">
|
||||
<p class="section-note">识别边界话题/预算/索要联系方式,以及号码与邮箱。</p>
|
||||
<div v-for="section in singlePatternSections" :key="section.key" class="wa-single">
|
||||
<p class="wa-single-title">{{ section.title }}</p>
|
||||
<p class="section-note">{{ section.note }}</p>
|
||||
<div class="regex-row">
|
||||
<div class="regex-line">
|
||||
<ElInput
|
||||
v-model="model.singles[section.key].pattern"
|
||||
placeholder="正则表达式(Python re 语法)"
|
||||
class="mono-input pattern-input"
|
||||
/>
|
||||
<ElSelect
|
||||
v-model="model.singles[section.key].flags"
|
||||
class="flags-select"
|
||||
title="标志位"
|
||||
>
|
||||
<ElOption value="" label="无" />
|
||||
<ElOption value="i" label="i 忽略大小写" />
|
||||
</ElSelect>
|
||||
</div>
|
||||
<p
|
||||
v-if="model.singles[section.key].comment"
|
||||
class="comment-view mono"
|
||||
>
|
||||
{{ model.singles[section.key].comment }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ElCollapseItem>
|
||||
|
||||
<!-- reminders 六类话术 -->
|
||||
<ElCollapseItem name="reminders" title="AI 提醒指令(reminders 六类)">
|
||||
<p class="section-note">
|
||||
按场景注入 AI 的系统指令文案;{shown}/{intl} 等占位符由系统回填。
|
||||
</p>
|
||||
<div v-for="(variants, cat) in REMINDER_VARIANTS" :key="cat" class="reminder-cat">
|
||||
<p class="reminder-cat-title">{{ reminderCatLabel(cat) }}</p>
|
||||
<div v-for="variant in variants" :key="variant" class="reminder-variant">
|
||||
<span class="variant-label mono">{{ cat }}.{{ variant }}</span>
|
||||
<ElInput
|
||||
v-model="model.reminders[cat][variant]"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 8 }"
|
||||
:placeholder="variantLabel(cat, variant)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ElCollapseItem>
|
||||
</ElCollapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { REMINDER_VARIANTS, type WallEditModel } from '../model'
|
||||
|
||||
defineOptions({ name: 'WallAdvanced' })
|
||||
|
||||
const props = defineProps<{
|
||||
/** 词表编辑模型(直改 reactive——与 ScenarioCard 同契约) */
|
||||
model: WallEditModel
|
||||
}>()
|
||||
|
||||
// ---- 区块展开态(默认全部收起——引擎级规则按需展开) -------------------------------
|
||||
|
||||
const openSections = ref<string[]>([])
|
||||
|
||||
// ---- 正则列表区(价格墙 / 再索要墙)--------------------------------------------------
|
||||
|
||||
const patternListSections = [
|
||||
{
|
||||
key: 'price_patterns',
|
||||
title: '价格识别正则(price_patterns)',
|
||||
plain: '识别客户在谈价格的句式。',
|
||||
note: '每条一行;flags 仅支持 i(忽略大小写);comment 为设计说明(不参与编译)。空 pattern 行保存时自动剔除。',
|
||||
rows: toRef(() => props.model.priceRows),
|
||||
add: () => props.model.priceRows.push(blankRegexRow()),
|
||||
remove: (i: number) => props.model.priceRows.splice(i, 1)
|
||||
},
|
||||
{
|
||||
key: 'reask_patterns',
|
||||
title: '追问识别正则(reask_patterns)',
|
||||
plain: '识别客户再次追问的句式。',
|
||||
note: 'AI 输出侧防重复索要:命中且客户已留联系方式 → 静默丢弃该句(无替换文案)。',
|
||||
rows: toRef(() => props.model.reaskRows),
|
||||
add: () => props.model.reaskRows.push(blankRegexRow()),
|
||||
remove: (i: number) => props.model.reaskRows.splice(i, 1)
|
||||
}
|
||||
]
|
||||
|
||||
function blankRegexRow() {
|
||||
return { pattern: '', flags: '' as const, comment: '', showComment: true }
|
||||
}
|
||||
|
||||
// ---- 单正则区(triggers / zero_number)----------------------------------------------
|
||||
|
||||
const singlePatternSections = [
|
||||
{
|
||||
key: 'boundary',
|
||||
title: '边界触发器 triggers.boundary',
|
||||
note: '客户侧触发词大表(价格/折扣/交期/样品等)——命中注入边界提醒话术。'
|
||||
},
|
||||
{
|
||||
key: 'budget',
|
||||
title: '预算触发器 triggers.budget',
|
||||
note: '客户报预算触发词(与 reminders.boundary.budget 话术联动)。'
|
||||
},
|
||||
{
|
||||
key: 'contact_ask',
|
||||
title: '索要联系方式触发器 triggers.contact_ask',
|
||||
note: '客户索要我方联系方式(最高意向信号)——命中注入对应 source 话术。'
|
||||
},
|
||||
{
|
||||
key: 'long_number',
|
||||
title: '零号码墙 zero_number.long_number',
|
||||
note: '≥10 位数字串(号码红线)——命中整句替换为 no_contact 对应句。'
|
||||
},
|
||||
{
|
||||
key: 'any_email',
|
||||
title: '零号码墙 zero_number.any_email',
|
||||
note: '邮箱地址(号码红线同款)——命中整句替换。'
|
||||
}
|
||||
] as const
|
||||
|
||||
// ---- 提醒标签 -------------------------------------------------------------------------
|
||||
|
||||
const REMINDER_CAT_LABELS: Record<string, string> = {
|
||||
boundary: '职能边界',
|
||||
contact_ask: '索要联系方式',
|
||||
language_follow: '语言跟随',
|
||||
lead_given: '已留线索',
|
||||
cc_guide: '国家代码指引',
|
||||
email_light: '邮箱轻补'
|
||||
}
|
||||
|
||||
function reminderCatLabel(cat: string): string {
|
||||
return REMINDER_CAT_LABELS[cat] ?? cat
|
||||
}
|
||||
|
||||
function variantLabel(cat: string, variant: string): string {
|
||||
const map: Record<string, string> = {
|
||||
'boundary.budget': '客户报预算求推荐的边界提醒',
|
||||
'boundary.normal': '常规价格/交期询问的边界提醒',
|
||||
'contact_ask.app_already': 'App 会话且已留资',
|
||||
'contact_ask.app': 'App 会话(无页面按钮)',
|
||||
'contact_ask.web': 'Web 会话(指路页面按钮)'
|
||||
}
|
||||
return map[`${cat}.${variant}`] ?? variant
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wall-advanced {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wa-warn {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-note {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--art-gray-400);
|
||||
}
|
||||
|
||||
// ---- 正则行 -------------------------------------------------------------------------
|
||||
|
||||
.regex-row {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.regex-line {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
.row-index {
|
||||
width: 20px;
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: var(--art-gray-400);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pattern-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.flags-select {
|
||||
width: 130px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
margin: 4px 0 0 26px;
|
||||
}
|
||||
|
||||
.comment-view {
|
||||
margin: 4px 0 0 26px;
|
||||
padding: 6px 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--art-gray-500);
|
||||
background: var(--art-main-bg-color, var(--el-fill-color-lighter));
|
||||
border-radius: 4px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||
}
|
||||
|
||||
:deep(.mono-input textarea),
|
||||
:deep(.mono-input input) {
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
// ---- 单正则子块(五键合并展示) -------------------------------------------------------
|
||||
|
||||
.wa-single {
|
||||
margin-bottom: 14px;
|
||||
|
||||
.wa-single-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 提醒六类 -------------------------------------------------------------------------
|
||||
|
||||
.reminder-cat {
|
||||
margin-bottom: 14px;
|
||||
|
||||
.reminder-cat-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.reminder-variant {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.variant-label {
|
||||
width: 190px;
|
||||
flex-shrink: 0;
|
||||
padding-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--art-gray-500);
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user