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