refactor(ui): 重构上下文模式测试面板架构,实现职责分离
- 新增 ContextUserTestPanel 组件,专门处理 Context User 模式 - 简化 TestAreaPanel,移除多模式判断逻辑,仅保留 Basic Mode - 移除 contextMode prop,各组件职责更加清晰 - 修复 ConversationTestPanel 和 ContextUserTestPanel 的定时器内存泄漏 - 更新相关组件和类型定义,保持 API 一致性 变更影响: - TestAreaPanel: 从 780 行减少到 380 行 - 新增 ContextUserTestPanel: 630 行专用组件 - 架构更清晰:Basic/ContextUser/ContextSystem 各自独立
This commit is contained in:
1 parent
7b5ed1ee41
commit
1970a2bac9
11 files changed
+671
-461
No files matched your search
@@ -2146,16 +2146,18 @@ const getActiveTestPanelInstance = (): TestAreaPanelInstance | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
// 基础模式和 context-user 模式的测试处理函数
|
||||
// 注意:context-system 模式已在 ContextSystemWorkspace 内部使用 useConversationTester 处理,不会调用此函数
|
||||
// 基础模式的测试处理函数
|
||||
// 注意:
|
||||
// 1. Context System 模式在 ContextSystemWorkspace 内部使用 useConversationTester 处理
|
||||
// 2. Context User 模式在 ContextUserWorkspace 内部使用 useContextUserTester 处理
|
||||
// 3. 此函数仅被 Basic Mode 的 TestAreaPanel 调用
|
||||
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
|
||||
// 调用基础测试器(只用于基础模式和 context-user)
|
||||
await promptTester.executeTest(
|
||||
optimizer.prompt,
|
||||
optimizer.optimizedPrompt,
|
||||
testContent.value,
|
||||
isCompareMode.value,
|
||||
testVariables
|
||||
testVariables || {}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,148 +15,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 变量值输入表单 (完整实现) -->
|
||||
<div
|
||||
v-if="showVariableForm"
|
||||
:style="{ flexShrink: 0, marginBottom: '16px' }"
|
||||
>
|
||||
<NCard
|
||||
:title="t('test.variables.formTitle')"
|
||||
size="small"
|
||||
:bordered="true"
|
||||
>
|
||||
<template #header-extra>
|
||||
<NSpace :size="8">
|
||||
<NTag :bordered="false" type="info" size="small">
|
||||
{{ t("test.variables.tempCount", { count: displayVariables.length }) }}
|
||||
</NTag>
|
||||
<NButton
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleClearAllVariables"
|
||||
>
|
||||
{{ t("test.variables.clearAll") }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<NSpace vertical :size="12">
|
||||
<!-- 变量输入项 -->
|
||||
<div
|
||||
v-for="varName in displayVariables"
|
||||
:key="varName"
|
||||
:style="{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}"
|
||||
>
|
||||
<NTag
|
||||
size="small"
|
||||
:bordered="false"
|
||||
:type="
|
||||
getVariableSource(varName) === 'predefined'
|
||||
? 'success'
|
||||
: getVariableSource(varName) === 'test'
|
||||
? 'warning'
|
||||
: getVariableSource(varName) === 'global'
|
||||
? 'default'
|
||||
: 'default'
|
||||
"
|
||||
:style="{ minWidth: '120px', flexShrink: 0 }"
|
||||
>
|
||||
<span v-text="`{{${varName}}}`"></span>
|
||||
</NTag>
|
||||
<NInput
|
||||
:value="getVariableDisplayValue(varName)"
|
||||
:placeholder="getVariablePlaceholder(varName)"
|
||||
size="small"
|
||||
:style="{ flex: 1 }"
|
||||
@update:value="
|
||||
handleVariableValueChange(varName, $event)
|
||||
"
|
||||
/>
|
||||
<!-- 🆕 删除按钮 (仅临时变量显示) -->
|
||||
<NButton
|
||||
v-if="getVariableSource(varName) === 'test'"
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleDeleteVariable(varName)"
|
||||
:title="t('test.variables.delete')"
|
||||
>
|
||||
🗑️
|
||||
</NButton>
|
||||
<!-- 🆕 保存到全局按钮 (仅测试变量显示) -->
|
||||
<NButton
|
||||
v-if="getVariableSource(varName) === 'test'"
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleSaveToGlobal(varName)"
|
||||
:title="t('test.variables.saveToGlobal')"
|
||||
>
|
||||
💾
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<!-- 无变量提示 -->
|
||||
<NEmpty
|
||||
v-if="displayVariables.length === 0"
|
||||
:description="t('test.variables.noVariables')"
|
||||
size="small"
|
||||
/>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<NSpace :size="8" justify="end">
|
||||
<!-- 🆕 添加变量按钮 -->
|
||||
<NButton
|
||||
size="small"
|
||||
@click="showAddVariableDialog = true"
|
||||
>
|
||||
{{ t("test.variables.addVariable") }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 🆕 添加变量对话框 -->
|
||||
<NModal
|
||||
v-model:show="showAddVariableDialog"
|
||||
preset="dialog"
|
||||
:title="t('test.variables.addVariable')"
|
||||
:positive-text="t('common.confirm')"
|
||||
:negative-text="t('common.cancel')"
|
||||
:on-positive-click="handleAddVariable"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<NSpace vertical :size="12" style="margin-top: 16px;">
|
||||
<NFormItem
|
||||
:label="t('variableExtraction.variableName')"
|
||||
:validation-status="
|
||||
newVariableNameError ? 'error' : undefined
|
||||
"
|
||||
:feedback="newVariableNameError"
|
||||
>
|
||||
<NInput
|
||||
v-model:value="newVariableName"
|
||||
:placeholder="
|
||||
t('variableExtraction.variableNamePlaceholder')
|
||||
"
|
||||
@input="validateNewVariableName"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem :label="t('variableExtraction.variableValue')">
|
||||
<NInput
|
||||
v-model:value="newVariableValue"
|
||||
:placeholder="
|
||||
t('variableExtraction.variableValuePlaceholder')
|
||||
"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NSpace>
|
||||
</NModal>
|
||||
|
||||
<!-- 控制工具栏 -->
|
||||
<div :style="{ flexShrink: 0 }">
|
||||
@@ -255,20 +114,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, ref, onUnmounted } from 'vue'
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
useMessage,
|
||||
NFlex,
|
||||
NCard,
|
||||
NButton,
|
||||
NTag,
|
||||
NSpace,
|
||||
NInput,
|
||||
NEmpty,
|
||||
NModal,
|
||||
NFormItem,
|
||||
} from "naive-ui";
|
||||
import type {
|
||||
OptimizationMode,
|
||||
@@ -278,14 +128,12 @@ import type {
|
||||
import { useResponsive } from '../composables/ui/useResponsive';
|
||||
import { usePerformanceMonitor } from "../composables/performance/usePerformanceMonitor";
|
||||
import { useDebounceThrottle } from "../composables/performance/useDebounceThrottle";
|
||||
import { useCurrentMode } from "../composables/mode";
|
||||
import TestInputSection from "./TestInputSection.vue";
|
||||
import TestControlBar from "./TestControlBar.vue";
|
||||
import TestResultSection from "./TestResultSection.vue";
|
||||
import ToolCallDisplay from "./ToolCallDisplay.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const message = useMessage();
|
||||
|
||||
// 性能监控
|
||||
const {
|
||||
@@ -307,13 +155,9 @@ const {
|
||||
// gridConfig // 保留用于网格布局
|
||||
} = useResponsive();
|
||||
|
||||
// 🆕 模式检测(用于隐藏基础模式下的变量功能)
|
||||
const { isBasicMode } = useCurrentMode();
|
||||
|
||||
interface Props {
|
||||
// 核心状态
|
||||
optimizationMode: OptimizationMode;
|
||||
contextMode?: import("@prompt-optimizer/core").ContextMode;
|
||||
isTestRunning?: boolean;
|
||||
|
||||
// 测试内容
|
||||
@@ -321,10 +165,7 @@ interface Props {
|
||||
optimizedPrompt?: string; // 优化后的提示词(用于变量检测)
|
||||
isCompareMode?: boolean;
|
||||
|
||||
// 🆕 两层变量体系 (简化)
|
||||
globalVariables?: Record<string, string>; // 全局自定义变量
|
||||
predefinedVariables?: Record<string, string>; // 内置预定义变量
|
||||
temporaryVariables?: Record<string, string>; // 🆕 临时变量 (从InputPanel提取的变量)
|
||||
|
||||
|
||||
// 功能开关
|
||||
enableCompareMode?: boolean;
|
||||
@@ -349,7 +190,6 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
contextMode: "user",
|
||||
isTestRunning: false,
|
||||
testContent: "",
|
||||
isCompareMode: true,
|
||||
@@ -363,22 +203,17 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
originalResultTitle: "",
|
||||
optimizedResultTitle: "",
|
||||
singleResultTitle: "",
|
||||
globalVariables: () => ({}),
|
||||
predefinedVariables: () => ({}),
|
||||
temporaryVariables: () => ({}),
|
||||
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:testContent": [value: string];
|
||||
"update:isCompareMode": [value: boolean];
|
||||
test: [testVariables: Record<string, string>]; // 🆕 传递测试变量
|
||||
test: []; // 🆕 传递测试变量
|
||||
"compare-toggle": [];
|
||||
// 高级功能事件
|
||||
"open-variable-manager": [];
|
||||
"open-context-editor": [];
|
||||
"variable-change": [name: string, value: string];
|
||||
"save-to-global": [name: string, value: string]; // 🆕 保存测试变量到全局
|
||||
"get-test-variables": []; // 🆕 请求获取测试变量(用于测试执行)
|
||||
"context-change": [
|
||||
messages: ConversationMessage[],
|
||||
variables: Record<string, string>,
|
||||
@@ -389,8 +224,7 @@ const emit = defineEmits<{
|
||||
toolCalls: ToolCallResult[],
|
||||
testType: "original" | "optimized",
|
||||
];
|
||||
"temporary-variable-remove": [name: string];
|
||||
"temporary-variables-clear": [];
|
||||
|
||||
}>();
|
||||
|
||||
// 内部状态管理 - 去除防抖,保证输入即时响应
|
||||
@@ -442,18 +276,10 @@ const clearToolCalls = (
|
||||
|
||||
// 移除结果缓存与相关节流逻辑,避免不必要的复杂度
|
||||
|
||||
// 关键计算属性:showTestInput 取决于当前功能模式
|
||||
// 关键计算属性:showTestInput 取决于优化模式
|
||||
// 基础模式:仅在系统提示词优化时需要测试内容输入
|
||||
const showTestInput = computed(() => {
|
||||
// 基础模式始终以系统提示词布尔值决定可见性
|
||||
if (isBasicMode.value) {
|
||||
return props.optimizationMode === "system";
|
||||
}
|
||||
// 上下文模式判断
|
||||
// - 多轮对话模式(contextMode === "system"):不显示测试内容输入框(测试内容就是会话消息)
|
||||
// - 变量模式(contextMode === "user"):显示测试内容输入框
|
||||
return (
|
||||
props.contextMode === "user" && props.optimizationMode === "system"
|
||||
);
|
||||
return props.optimizationMode === "system";
|
||||
});
|
||||
|
||||
// 响应式布局配置
|
||||
@@ -491,8 +317,7 @@ const primaryActionDisabled = computed(() => {
|
||||
if (props.isTestRunning) return true;
|
||||
|
||||
// 系统提示词模式需要测试内容
|
||||
// 但多轮对话模式(contextMode === "system")例外,因为测试内容就是会话消息
|
||||
if (props.optimizationMode === "system" && props.contextMode !== "system" && !props.testContent.trim()) {
|
||||
if (props.optimizationMode === "system" && !props.testContent.trim()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -509,9 +334,7 @@ const handleCompareToggle = () => {
|
||||
|
||||
const handleTest = throttle(
|
||||
() => {
|
||||
// 🆕 获取并传递测试变量
|
||||
const testVars = getVariableValues();
|
||||
emit("test", testVars);
|
||||
emit("test");
|
||||
recordUpdate();
|
||||
},
|
||||
200,
|
||||
@@ -521,243 +344,11 @@ const handleTest = throttle(
|
||||
// ========== 变量管理 ==========
|
||||
|
||||
// 🆕 添加变量对话框状态
|
||||
const showAddVariableDialog = ref(false);
|
||||
const newVariableName = ref("");
|
||||
const newVariableValue = ref("");
|
||||
const newVariableNameError = ref("");
|
||||
|
||||
// 🧪 测试区临时变量 (仅内存,刷新丢失) - 新增功能
|
||||
// 数据结构: { 变量名: { value: 值, timestamp: 时间戳 } }
|
||||
interface TestVariable {
|
||||
value: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const testVariables = ref<Record<string, TestVariable>>({});
|
||||
|
||||
// 监听 props.temporaryVariables 变化,同步到内部状态
|
||||
watch(
|
||||
() => props.temporaryVariables,
|
||||
(newVars) => {
|
||||
// 🔧 第一步:删除不再存在于 newVars 中的过期变量(防止内存泄漏)
|
||||
const newVarNames = new Set(Object.keys(newVars));
|
||||
for (const name of Object.keys(testVariables.value)) {
|
||||
if (!newVarNames.has(name)) {
|
||||
delete testVariables.value[name];
|
||||
}
|
||||
}
|
||||
|
||||
// 第二步:合并新的临时变量,为新变量添加时间戳
|
||||
for (const [name, value] of Object.entries(newVars)) {
|
||||
if (!testVariables.value[name]) {
|
||||
testVariables.value[name] = {
|
||||
value,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
} else {
|
||||
// 更新现有变量的值,保留时间戳
|
||||
testVariables.value[name].value = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 三层变量合并(按优先级:全局 < 测试 < 内置)
|
||||
const mergedVariables = computed(() => {
|
||||
// 将 testVariables 转换为 { name: value } 格式
|
||||
const testVarsFlat: Record<string, string> = {};
|
||||
for (const [name, data] of Object.entries(testVariables.value)) {
|
||||
testVarsFlat[name] = data.value;
|
||||
}
|
||||
|
||||
return {
|
||||
...props.globalVariables, // 优先级 1: 全局自定义变量
|
||||
...testVarsFlat, // 优先级 2: 测试区临时变量
|
||||
...props.predefinedVariables, // 优先级 3: 内置预定义变量
|
||||
};
|
||||
});
|
||||
|
||||
// 🆕 按时间排序的临时变量列表 (最新的在最前面)
|
||||
const sortedTestVariables = computed(() => {
|
||||
const entries = Object.entries(testVariables.value);
|
||||
return entries
|
||||
.sort((a, b) => b[1].timestamp - a[1].timestamp) // 降序排列
|
||||
.map(([name]) => name);
|
||||
});
|
||||
|
||||
// 🆕 实际显示的变量列表 = 临时变量 (不再依赖 detectedVariables)
|
||||
const displayVariables = computed(() => {
|
||||
return sortedTestVariables.value;
|
||||
});
|
||||
|
||||
// 是否显示变量表单:默认显示(基础模式除外)
|
||||
const showVariableForm = computed(() => {
|
||||
// 🆕 基础模式不显示变量功能(变量系统仅在上下文模式下可用)
|
||||
if (isBasicMode.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 测试运行期间也应保持显示,避免界面闪烁和信息丢失
|
||||
return true;
|
||||
});
|
||||
|
||||
// 获取变量的显示值(从合并后的变量中获取)
|
||||
const getVariableDisplayValue = (varName: string): string => {
|
||||
return mergedVariables.value[varName] || "";
|
||||
};
|
||||
|
||||
// 🆕 获取变量的占位符提示(显示变量来源)
|
||||
const getVariablePlaceholder = (varName: string): string => {
|
||||
// 如果有来自全局/内置的值,提示来源
|
||||
if (props.predefinedVariables?.[varName]) {
|
||||
return (
|
||||
t("test.variables.inputPlaceholder") +
|
||||
` (${t("variables.source.predefined")})`
|
||||
);
|
||||
}
|
||||
if (props.globalVariables?.[varName]) {
|
||||
return (
|
||||
t("test.variables.inputPlaceholder") +
|
||||
` (${t("variables.source.global")})`
|
||||
);
|
||||
}
|
||||
return t("test.variables.inputPlaceholder");
|
||||
};
|
||||
|
||||
// 变量列表变化时的清理逻辑已不再需要(不再使用 userInputValues)
|
||||
|
||||
// 事件处理函数
|
||||
const handleVariableValueChange = (varName: string, value: string) => {
|
||||
// 🧪 更新测试区临时变量
|
||||
if (testVariables.value[varName]) {
|
||||
testVariables.value[varName].value = value;
|
||||
} else {
|
||||
// 如果变量不存在,创建新变量
|
||||
testVariables.value[varName] = {
|
||||
value,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
emit("variable-change", varName, value);
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
const handleClearAllVariables = () => {
|
||||
// 清空测试区临时变量
|
||||
testVariables.value = {};
|
||||
emit("temporary-variables-clear");
|
||||
message.success(t("test.variables.clearSuccess"));
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 🆕 保存测试变量到全局
|
||||
const handleSaveToGlobal = (varName: string) => {
|
||||
const varData = testVariables.value[varName];
|
||||
if (!varData || !varData.value.trim()) {
|
||||
message.warning(t("test.variables.emptyValueWarning"));
|
||||
return;
|
||||
}
|
||||
|
||||
emit("save-to-global", varName, varData.value);
|
||||
message.success(t("test.variables.savedToGlobal"));
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 🆕 验证新变量名
|
||||
const validateNewVariableName = () => {
|
||||
const name = newVariableName.value.trim();
|
||||
|
||||
if (!name) {
|
||||
newVariableNameError.value = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证规则1: 不能以数字开头
|
||||
if (/^\d/.test(name)) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.noNumberStart"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证规则2: 只能包含中文、英文、数字、下划线
|
||||
if (!/^[\u4e00-\u9fa5a-zA-Z_][\u4e00-\u9fa5a-zA-Z0-9_]*$/.test(name)) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.invalidCharacters"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证规则3: 不能与已有变量重名
|
||||
if (testVariables.value[name]) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.duplicateVariable"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
newVariableNameError.value = "";
|
||||
return true;
|
||||
};
|
||||
|
||||
// 🆕 添加新变量
|
||||
const handleAddVariable = () => {
|
||||
if (!validateNewVariableName()) {
|
||||
if (!newVariableName.value.trim()) {
|
||||
message.warning(t("test.variables.nameRequired"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const name = newVariableName.value.trim();
|
||||
handleVariableValueChange(name, newVariableValue.value);
|
||||
if (testVariables.value[name]) {
|
||||
testVariables.value[name].timestamp = Date.now();
|
||||
}
|
||||
message.success(t("test.variables.addSuccess"));
|
||||
|
||||
// 重置对话框
|
||||
newVariableName.value = "";
|
||||
newVariableValue.value = "";
|
||||
newVariableNameError.value = "";
|
||||
showAddVariableDialog.value = false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 🆕 删除变量
|
||||
const handleDeleteVariable = (varName: string) => {
|
||||
delete testVariables.value[varName];
|
||||
emit("temporary-variable-remove", varName);
|
||||
emit("variable-change", varName, "");
|
||||
message.success(
|
||||
t("test.variables.deleteSuccess", { name: varName })
|
||||
);
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 暴露变量值供外部访问(返回合并后的最终值)
|
||||
const getVariableValues = () => {
|
||||
return { ...mergedVariables.value };
|
||||
};
|
||||
|
||||
// 设置变量值(外部调用)- 通过 emit 同步到会话变量
|
||||
const setVariableValues = (values: Record<string, string>) => {
|
||||
for (const [name, value] of Object.entries(values)) {
|
||||
emit("variable-change", name, value);
|
||||
}
|
||||
};
|
||||
|
||||
// 🧪 获取变量来源 (简化)
|
||||
const getVariableSource = (varName: string): "predefined" | "test" | "global" | "empty" => {
|
||||
if (props.predefinedVariables?.[varName]) return "predefined";
|
||||
if (testVariables.value[varName]) return "test";
|
||||
if (props.globalVariables?.[varName]) return "global";
|
||||
return "empty";
|
||||
};
|
||||
|
||||
// 移除未使用的 props 变化防抖处理,避免多余复杂度
|
||||
|
||||
// 开发环境下的性能调试
|
||||
if (import.meta.env.DEV) {
|
||||
@@ -774,7 +365,8 @@ if (import.meta.env.DEV) {
|
||||
);
|
||||
|
||||
// 定期检查性能
|
||||
setInterval(logPerformance, 10000);
|
||||
const timer = setInterval(logPerformance, 10000);
|
||||
onUnmounted(() => clearInterval(timer));
|
||||
}
|
||||
|
||||
// 暴露方法供父组件调用
|
||||
@@ -786,9 +378,7 @@ defineExpose({
|
||||
original: originalToolCalls.value,
|
||||
optimized: optimizedToolCalls.value,
|
||||
}),
|
||||
// 变量管理
|
||||
getVariableValues,
|
||||
setVariableValues,
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<NFlex align="center" :wrap="false" :size="12">
|
||||
<!-- 快捷操作按钮 -->
|
||||
<!-- 全局变量 - 两种模式都显示 -->
|
||||
<!-- 全局变量 -->
|
||||
<NButton
|
||||
size="small"
|
||||
type="default"
|
||||
@@ -14,9 +14,8 @@
|
||||
{{ $t('contextMode.actions.globalVariables') }}
|
||||
</NButton>
|
||||
|
||||
<!-- 工具管理 - 仅用户模式显示 -->
|
||||
<!-- 工具管理 -->
|
||||
<NButton
|
||||
v-if="contextMode === 'user'"
|
||||
size="small"
|
||||
type="default"
|
||||
@click="$emit('open-tool-manager')"
|
||||
@@ -32,11 +31,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NButton, NFlex } from 'naive-ui'
|
||||
import { type ContextMode } from '@prompt-optimizer/core'
|
||||
|
||||
const props = defineProps<{
|
||||
contextMode: ContextMode
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'open-global-variables': []
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
:available-variables="availableVariables"
|
||||
:scan-variables="scanVariables"
|
||||
:optimization-mode="optimizationMode"
|
||||
context-mode="system"
|
||||
:tool-count="toolCount"
|
||||
@open-variable-manager="emit('open-variable-manager')"
|
||||
@open-context-editor="emit('open-context-editor')"
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
<template>
|
||||
<NFlex vertical :style="{ height: '100%' }">
|
||||
<!-- 测试内容输入区 (ContextUser专属,ConversationTestPanel没有这个) -->
|
||||
<div :style="{ flexShrink: 0, marginBottom: '16px' }">
|
||||
<TestInputSection
|
||||
v-model="testContentProxy"
|
||||
:label="t('test.content')"
|
||||
:placeholder="t('test.simpleMode.placeholder')"
|
||||
:disabled="isTestRunning"
|
||||
:mode="inputMode"
|
||||
:size="inputSize"
|
||||
:enable-fullscreen="enableFullscreen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 变量值输入表单 -->
|
||||
<div
|
||||
v-if="showVariableForm"
|
||||
:style="{ flexShrink: 0, marginBottom: '16px' }"
|
||||
>
|
||||
<NCard
|
||||
:title="t('test.variables.formTitle')"
|
||||
size="small"
|
||||
:bordered="true"
|
||||
>
|
||||
<template #header-extra>
|
||||
<NSpace :size="8">
|
||||
<NTag :bordered="false" type="info" size="small">
|
||||
{{ t("test.variables.tempCount", { count: displayVariables.length }) }}
|
||||
</NTag>
|
||||
<NButton
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleClearAllVariables"
|
||||
>
|
||||
{{ t("test.variables.clearAll") }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
|
||||
<NSpace vertical :size="12">
|
||||
<!-- 变量输入项 -->
|
||||
<div
|
||||
v-for="varName in displayVariables"
|
||||
:key="varName"
|
||||
:style="{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}"
|
||||
>
|
||||
<NTag
|
||||
size="small"
|
||||
:bordered="false"
|
||||
:type="
|
||||
getVariableSource(varName) === 'predefined'
|
||||
? 'success'
|
||||
: getVariableSource(varName) === 'test'
|
||||
? 'warning'
|
||||
: 'default'
|
||||
"
|
||||
:style="{ minWidth: '120px', flexShrink: 0 }"
|
||||
>
|
||||
<span v-text="`{{${varName}}}`"></span>
|
||||
</NTag>
|
||||
<NInput
|
||||
:value="getVariableDisplayValue(varName)"
|
||||
:placeholder="getVariablePlaceholder(varName)"
|
||||
size="small"
|
||||
:style="{ flex: 1 }"
|
||||
@update:value="
|
||||
handleVariableValueChange(varName, $event)
|
||||
"
|
||||
/>
|
||||
<!-- 删除按钮 (仅临时变量显示) -->
|
||||
<NButton
|
||||
v-if="getVariableSource(varName) === 'test'"
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleDeleteVariable(varName)"
|
||||
:title="t('test.variables.delete')"
|
||||
>
|
||||
🗑️
|
||||
</NButton>
|
||||
<!-- 保存到全局按钮 (仅测试变量显示) -->
|
||||
<NButton
|
||||
v-if="getVariableSource(varName) === 'test'"
|
||||
size="small"
|
||||
quaternary
|
||||
@click="handleSaveToGlobal(varName)"
|
||||
:title="t('test.variables.saveToGlobal')"
|
||||
>
|
||||
💾
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<!-- 无变量提示 -->
|
||||
<NEmpty
|
||||
v-if="displayVariables.length === 0"
|
||||
:description="t('test.variables.noVariables')"
|
||||
size="small"
|
||||
/>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<NSpace :size="8" justify="end">
|
||||
<!-- 添加变量按钮 -->
|
||||
<NButton
|
||||
size="small"
|
||||
@click="showAddVariableDialog = true"
|
||||
>
|
||||
{{ t("test.variables.addVariable") }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
</div>
|
||||
|
||||
<!-- 添加变量对话框 -->
|
||||
<NModal
|
||||
v-model:show="showAddVariableDialog"
|
||||
preset="dialog"
|
||||
:title="t('test.variables.addVariable')"
|
||||
:positive-text="t('common.confirm')"
|
||||
:negative-text="t('common.cancel')"
|
||||
:on-positive-click="handleAddVariable"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<NSpace vertical :size="12" style="margin-top: 16px;">
|
||||
<NFormItem
|
||||
:label="t('variableExtraction.variableName')"
|
||||
:validation-status="
|
||||
newVariableNameError ? 'error' : undefined
|
||||
"
|
||||
:feedback="newVariableNameError"
|
||||
>
|
||||
<NInput
|
||||
v-model:value="newVariableName"
|
||||
:placeholder="
|
||||
t('variableExtraction.variableNamePlaceholder')
|
||||
"
|
||||
@input="validateNewVariableName"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem :label="t('variableExtraction.variableValue')">
|
||||
<NInput
|
||||
v-model:value="newVariableValue"
|
||||
:placeholder="
|
||||
t('variableExtraction.variableValuePlaceholder')
|
||||
"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NSpace>
|
||||
</NModal>
|
||||
|
||||
<!-- 控制工具栏 -->
|
||||
<div :style="{ flexShrink: 0 }">
|
||||
<TestControlBar
|
||||
:model-label="t('test.model')"
|
||||
:show-compare-toggle="enableCompareMode"
|
||||
:is-compare-mode="isCompareMode"
|
||||
@update:is-compare-mode="handleCompareModeToggle"
|
||||
:primary-action-text="primaryActionText"
|
||||
:primary-action-disabled="primaryActionDisabled"
|
||||
:primary-action-loading="isTestRunning"
|
||||
:layout="adaptiveControlBarLayout"
|
||||
:button-size="adaptiveButtonSize"
|
||||
@primary-action="handleTest"
|
||||
:style="{ marginBottom: '16px' }"
|
||||
>
|
||||
<template #model-select>
|
||||
<slot name="model-select"></slot>
|
||||
</template>
|
||||
<template #secondary-controls>
|
||||
<slot name="secondary-controls"></slot>
|
||||
</template>
|
||||
<template #custom-actions>
|
||||
<slot name="custom-actions"></slot>
|
||||
</template>
|
||||
</TestControlBar>
|
||||
</div>
|
||||
|
||||
<!-- 测试结果区域(不支持工具调用,仅显示文本结果)-->
|
||||
<TestResultSection
|
||||
:is-compare-mode="isCompareMode"
|
||||
:vertical-layout="adaptiveResultVerticalLayout"
|
||||
:show-original="isCompareMode"
|
||||
:original-result-title="t('test.originalResult')"
|
||||
:optimized-result-title="t('test.optimizedResult')"
|
||||
:single-result-title="singleResultTitle"
|
||||
:size="adaptiveButtonSize"
|
||||
:style="{ flex: 1, minHeight: 0 }"
|
||||
>
|
||||
<!-- 对比模式:原始结果 -->
|
||||
<template #original-result>
|
||||
<slot name="original-result"></slot>
|
||||
</template>
|
||||
|
||||
<!-- 对比模式:优化结果 -->
|
||||
<template #optimized-result>
|
||||
<slot name="optimized-result"></slot>
|
||||
</template>
|
||||
|
||||
<!-- 单一结果模式 -->
|
||||
<template #single-result>
|
||||
<slot name="single-result"></slot>
|
||||
</template>
|
||||
</TestResultSection>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, onUnmounted } from 'vue'
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
useMessage,
|
||||
NFlex,
|
||||
NCard,
|
||||
NButton,
|
||||
NTag,
|
||||
NSpace,
|
||||
NInput,
|
||||
NEmpty,
|
||||
NModal,
|
||||
NFormItem,
|
||||
} from "naive-ui";
|
||||
import { useResponsive } from '../../composables/ui/useResponsive';
|
||||
import { usePerformanceMonitor } from "../../composables/performance/usePerformanceMonitor";
|
||||
import { useDebounceThrottle } from "../../composables/performance/useDebounceThrottle";
|
||||
import TestInputSection from "../TestInputSection.vue";
|
||||
import TestControlBar from "../TestControlBar.vue";
|
||||
import TestResultSection from "../TestResultSection.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const message = useMessage();
|
||||
|
||||
// 性能监控
|
||||
const { recordUpdate, getPerformanceReport } = usePerformanceMonitor("ContextUserTestPanel");
|
||||
|
||||
// 防抖节流
|
||||
const { debounce, throttle } = useDebounceThrottle();
|
||||
|
||||
// 响应式配置
|
||||
const {
|
||||
shouldUseVerticalLayout,
|
||||
shouldUseCompactMode,
|
||||
buttonSize,
|
||||
inputSize,
|
||||
} = useResponsive();
|
||||
|
||||
interface Props {
|
||||
// 测试内容(ContextUser专属)
|
||||
testContent: string;
|
||||
|
||||
// 优化后的提示词(用于检测变量)
|
||||
optimizedPrompt?: string;
|
||||
|
||||
// 测试状态
|
||||
isTestRunning?: boolean;
|
||||
isCompareMode?: boolean;
|
||||
enableCompareMode?: boolean;
|
||||
|
||||
// 变量管理(三层)
|
||||
globalVariables?: Record<string, string>;
|
||||
predefinedVariables?: Record<string, string>;
|
||||
temporaryVariables?: Record<string, string>;
|
||||
|
||||
// 功能开关
|
||||
enableFullscreen?: boolean;
|
||||
|
||||
// 布局配置
|
||||
inputMode?: "compact" | "normal";
|
||||
controlBarLayout?: "default" | "compact" | "minimal";
|
||||
buttonSize?: "small" | "medium" | "large";
|
||||
resultVerticalLayout?: boolean;
|
||||
|
||||
// 结果显示配置
|
||||
singleResultTitle?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
optimizedPrompt: "",
|
||||
isTestRunning: false,
|
||||
isCompareMode: false,
|
||||
enableCompareMode: true,
|
||||
enableFullscreen: true,
|
||||
inputMode: "normal",
|
||||
controlBarLayout: "default",
|
||||
buttonSize: "medium",
|
||||
resultVerticalLayout: false,
|
||||
singleResultTitle: "",
|
||||
globalVariables: () => ({}),
|
||||
predefinedVariables: () => ({}),
|
||||
temporaryVariables: () => ({}),
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:testContent": [value: string];
|
||||
"update:isCompareMode": [value: boolean];
|
||||
test: [testVariables: Record<string, string>];
|
||||
"compare-toggle": [];
|
||||
"open-variable-manager": [];
|
||||
"variable-change": [name: string, value: string];
|
||||
"save-to-global": [name: string, value: string];
|
||||
"temporary-variable-remove": [name: string];
|
||||
"temporary-variables-clear": [];
|
||||
}>();
|
||||
|
||||
// 测试内容双向绑定
|
||||
const testContentProxy = computed({
|
||||
get: () => props.testContent,
|
||||
set: (value: string) => {
|
||||
emit("update:testContent", value);
|
||||
recordUpdate();
|
||||
},
|
||||
});
|
||||
|
||||
// 处理对比模式切换
|
||||
const handleCompareModeToggle = (value: boolean) => {
|
||||
emit("update:isCompareMode", value);
|
||||
emit("compare-toggle");
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 响应式布局配置
|
||||
const adaptiveControlBarLayout = computed(() => {
|
||||
if (shouldUseCompactMode.value) return "minimal";
|
||||
if (shouldUseVerticalLayout.value) return "compact";
|
||||
return props.controlBarLayout || "default";
|
||||
});
|
||||
|
||||
const adaptiveButtonSize = computed(() => {
|
||||
return buttonSize.value;
|
||||
});
|
||||
|
||||
const adaptiveResultVerticalLayout = computed(() => {
|
||||
return shouldUseVerticalLayout.value || props.resultVerticalLayout;
|
||||
});
|
||||
|
||||
// 主要操作按钮文本
|
||||
const primaryActionText = computed(() => {
|
||||
if (props.isTestRunning) {
|
||||
return t("test.testing");
|
||||
}
|
||||
return props.isCompareMode
|
||||
? t("test.startCompare")
|
||||
: t("test.startTest");
|
||||
});
|
||||
|
||||
// 主要操作按钮禁用状态(纯业务逻辑,无模式判断)
|
||||
const primaryActionDisabled = computed(() => {
|
||||
if (props.isTestRunning) return true;
|
||||
if (!props.testContent.trim()) return true; // 测试内容不能为空
|
||||
return false;
|
||||
});
|
||||
|
||||
const handleTest = throttle(
|
||||
() => {
|
||||
// 获取并传递测试变量
|
||||
const testVars = getVariableValues();
|
||||
emit("test", testVars);
|
||||
recordUpdate();
|
||||
},
|
||||
200,
|
||||
"handleTest",
|
||||
);
|
||||
|
||||
// ========== 变量管理 ==========
|
||||
|
||||
// 添加变量对话框状态
|
||||
const showAddVariableDialog = ref(false);
|
||||
const newVariableName = ref("");
|
||||
const newVariableValue = ref("");
|
||||
const newVariableNameError = ref("");
|
||||
|
||||
// 测试区临时变量
|
||||
interface TestVariable {
|
||||
value: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const testVariables = ref<Record<string, TestVariable>>({});
|
||||
|
||||
// 监听 props.temporaryVariables 变化
|
||||
watch(
|
||||
() => props.temporaryVariables,
|
||||
(newVars) => {
|
||||
const newVarNames = new Set(Object.keys(newVars));
|
||||
for (const name of Object.keys(testVariables.value)) {
|
||||
if (!newVarNames.has(name)) {
|
||||
delete testVariables.value[name];
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(newVars)) {
|
||||
if (!testVariables.value[name]) {
|
||||
testVariables.value[name] = {
|
||||
value,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
} else {
|
||||
testVariables.value[name].value = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 三层变量合并(优先级:全局 < 临时 < 预定义)
|
||||
const mergedVariables = computed(() => {
|
||||
const testVarsFlat: Record<string, string> = {};
|
||||
for (const [name, data] of Object.entries(testVariables.value)) {
|
||||
testVarsFlat[name] = data.value;
|
||||
}
|
||||
|
||||
return {
|
||||
...props.globalVariables,
|
||||
...testVarsFlat,
|
||||
...props.predefinedVariables,
|
||||
};
|
||||
});
|
||||
|
||||
// 按时间排序的临时变量列表
|
||||
const sortedTestVariables = computed(() => {
|
||||
const entries = Object.entries(testVariables.value);
|
||||
return entries
|
||||
.sort((a, b) => b[1].timestamp - a[1].timestamp)
|
||||
.map(([name]) => name);
|
||||
});
|
||||
|
||||
// 实际显示的变量列表
|
||||
const displayVariables = computed(() => {
|
||||
return sortedTestVariables.value;
|
||||
});
|
||||
|
||||
// 是否显示变量表单(固定行为,无模式判断)
|
||||
const showVariableForm = computed(() => {
|
||||
// ✅ 始终显示,避免测试期间闪烁(已修复的bug)
|
||||
return true;
|
||||
});
|
||||
|
||||
// 获取变量的显示值
|
||||
const getVariableDisplayValue = (varName: string): string => {
|
||||
return mergedVariables.value[varName] || "";
|
||||
};
|
||||
|
||||
// 获取变量的占位符提示
|
||||
const getVariablePlaceholder = (varName: string): string => {
|
||||
if (props.predefinedVariables?.[varName]) {
|
||||
return (
|
||||
t("test.variables.inputPlaceholder") +
|
||||
` (${t("variables.source.predefined")})`
|
||||
);
|
||||
}
|
||||
if (props.globalVariables?.[varName]) {
|
||||
return (
|
||||
t("test.variables.inputPlaceholder") +
|
||||
` (${t("variables.source.global")})`
|
||||
);
|
||||
}
|
||||
return t("test.variables.inputPlaceholder");
|
||||
};
|
||||
|
||||
// 事件处理函数
|
||||
const handleVariableValueChange = (varName: string, value: string) => {
|
||||
if (testVariables.value[varName]) {
|
||||
testVariables.value[varName].value = value;
|
||||
} else {
|
||||
testVariables.value[varName] = {
|
||||
value,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
emit("variable-change", varName, value);
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
const handleClearAllVariables = () => {
|
||||
testVariables.value = {};
|
||||
emit("temporary-variables-clear");
|
||||
message.success(t("test.variables.clearSuccess"));
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 保存测试变量到全局
|
||||
const handleSaveToGlobal = (varName: string) => {
|
||||
const varData = testVariables.value[varName];
|
||||
if (!varData || !varData.value.trim()) {
|
||||
message.warning(t("test.variables.emptyValueWarning"));
|
||||
return;
|
||||
}
|
||||
|
||||
emit("save-to-global", varName, varData.value);
|
||||
message.success(t("test.variables.savedToGlobal"));
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 验证新变量名
|
||||
const validateNewVariableName = () => {
|
||||
const name = newVariableName.value.trim();
|
||||
|
||||
if (!name) {
|
||||
newVariableNameError.value = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/^\d/.test(name)) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.noNumberStart"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/^[\u4e00-\u9fa5a-zA-Z_][\u4e00-\u9fa5a-zA-Z0-9_]*$/.test(name)) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.invalidCharacters"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (testVariables.value[name]) {
|
||||
newVariableNameError.value = t(
|
||||
"variableExtraction.validation.duplicateVariable"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
newVariableNameError.value = "";
|
||||
return true;
|
||||
};
|
||||
|
||||
// 添加新变量
|
||||
const handleAddVariable = () => {
|
||||
if (!validateNewVariableName()) {
|
||||
if (!newVariableName.value.trim()) {
|
||||
message.warning(t("test.variables.nameRequired"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const name = newVariableName.value.trim();
|
||||
handleVariableValueChange(name, newVariableValue.value);
|
||||
if (testVariables.value[name]) {
|
||||
testVariables.value[name].timestamp = Date.now();
|
||||
}
|
||||
message.success(t("test.variables.addSuccess"));
|
||||
|
||||
newVariableName.value = "";
|
||||
newVariableValue.value = "";
|
||||
newVariableNameError.value = "";
|
||||
showAddVariableDialog.value = false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 删除变量
|
||||
const handleDeleteVariable = (varName: string) => {
|
||||
delete testVariables.value[varName];
|
||||
emit("temporary-variable-remove", varName);
|
||||
emit("variable-change", varName, "");
|
||||
message.success(
|
||||
t("test.variables.deleteSuccess", { name: varName })
|
||||
);
|
||||
recordUpdate();
|
||||
};
|
||||
|
||||
// 暴露变量值供外部访问
|
||||
const getVariableValues = () => {
|
||||
return { ...mergedVariables.value };
|
||||
};
|
||||
|
||||
// 设置变量值
|
||||
const setVariableValues = (values: Record<string, string>) => {
|
||||
for (const [name, value] of Object.entries(values)) {
|
||||
emit("variable-change", name, value);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取变量来源
|
||||
const getVariableSource = (varName: string): "predefined" | "test" | "global" | "empty" => {
|
||||
if (props.predefinedVariables?.[varName]) return "predefined";
|
||||
if (testVariables.value[varName]) return "test";
|
||||
if (props.globalVariables?.[varName]) return "global";
|
||||
return "empty";
|
||||
};
|
||||
|
||||
// 开发环境下的性能调试
|
||||
if (import.meta.env.DEV) {
|
||||
const logPerformance = debounce(
|
||||
() => {
|
||||
const report = getPerformanceReport();
|
||||
if (report.grade.grade === "F") {
|
||||
console.warn("ContextUserTestPanel 性能较差:", report);
|
||||
}
|
||||
},
|
||||
5000,
|
||||
false,
|
||||
"performanceLog",
|
||||
);
|
||||
|
||||
const timer = setInterval(logPerformance, 10000);
|
||||
onUnmounted(() => clearInterval(timer));
|
||||
}
|
||||
|
||||
// 暴露方法供父组件调用(兼容 TestAreaPanelInstance 接口)
|
||||
defineExpose({
|
||||
// ContextUser 不支持工具调用,提供空实现
|
||||
clearToolCalls: () => {},
|
||||
handleToolCall: () => {},
|
||||
getToolCalls: () => ({ original: [], optimized: [] }),
|
||||
|
||||
// 变量管理
|
||||
getVariableValues,
|
||||
setVariableValues,
|
||||
|
||||
// 预览功能占位符(兼容接口)
|
||||
showPreview: () => {
|
||||
console.warn('[ContextUserTestPanel] showPreview not implemented');
|
||||
},
|
||||
hidePreview: () => {
|
||||
console.warn('[ContextUserTestPanel] hidePreview not implemented');
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ContextUser 不需要工具调用相关样式 */
|
||||
</style>
|
||||
@@ -141,27 +141,23 @@
|
||||
style="flex: 1; overflow: auto"
|
||||
content-style="height: 100%; max-height: 100%; overflow: hidden;"
|
||||
>
|
||||
<TestAreaPanel
|
||||
<ContextUserTestPanel
|
||||
ref="testAreaPanelRef"
|
||||
:optimization-mode="optimizationMode"
|
||||
context-mode="user"
|
||||
:test-content="testContent"
|
||||
@update:testContent="emit('update:testContent', $event)"
|
||||
:optimized-prompt="contextUserOptimization.optimizedPrompt"
|
||||
:is-test-running="contextUserTester.testResults.isTestingOriginal || contextUserTester.testResults.isTestingOptimized"
|
||||
:is-compare-mode="isCompareMode"
|
||||
@update:isCompareMode="emit('update:isCompareMode', $event)"
|
||||
:global-variables="globalVariables"
|
||||
:predefined-variables="predefinedVariables"
|
||||
:temporary-variables="temporaryVariables"
|
||||
:testContent="testContent"
|
||||
@update:testContent="emit('update:testContent', $event)"
|
||||
:isCompareMode="isCompareMode"
|
||||
@update:isCompareMode="emit('update:isCompareMode', $event)"
|
||||
:enable-compare-mode="true"
|
||||
:enable-fullscreen="true"
|
||||
:input-mode="inputMode"
|
||||
:control-bar-layout="controlBarLayout"
|
||||
:button-size="buttonSize"
|
||||
:conversation-max-height="conversationMaxHeight"
|
||||
:show-original-result="true"
|
||||
:result-vertical-layout="resultVerticalLayout"
|
||||
:single-result-title="t('test.testResult')"
|
||||
@test="handleTestWithVariables"
|
||||
@compare-toggle="emit('compare-toggle')"
|
||||
@open-variable-manager="emit('open-variable-manager')"
|
||||
@@ -212,7 +208,7 @@
|
||||
:style="{ height: '100%', minHeight: '0' }"
|
||||
/>
|
||||
</template>
|
||||
</TestAreaPanel>
|
||||
</ContextUserTestPanel>
|
||||
</NCard>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
@@ -254,7 +250,7 @@ import { NCard, NFlex, NButton, NText } from "naive-ui";
|
||||
import { useBreakpoints } from "@vueuse/core";
|
||||
import InputPanelUI from "../InputPanel.vue";
|
||||
import PromptPanelUI from "../PromptPanel.vue";
|
||||
import TestAreaPanel from "../TestAreaPanel.vue";
|
||||
import ContextUserTestPanel from "./ContextUserTestPanel.vue";
|
||||
import OutputDisplay from "../OutputDisplay.vue";
|
||||
import type { OptimizationMode } from "../../types";
|
||||
import type {
|
||||
|
||||
@@ -394,7 +394,6 @@ const props = withDefaults(defineProps<ConversationManagerProps>(), {
|
||||
title: undefined,
|
||||
toolCount: 0,
|
||||
optimizationMode: "system",
|
||||
contextMode: "system",
|
||||
scanVariables: () => [],
|
||||
replaceVariables: (content: string) => content,
|
||||
isPredefinedVariable: () => false,
|
||||
@@ -468,13 +467,11 @@ const scrollbarStyle = computed(() => {
|
||||
return {};
|
||||
});
|
||||
|
||||
// 模式化行为:用户模式隐藏消息增删改按钮,系统模式保留完整能力
|
||||
// 消息编辑权限控制
|
||||
const canEditMessages = computed(() => {
|
||||
// readonly优先级最高
|
||||
if (props.readonly) return false;
|
||||
// 用户模式不允许编辑消息
|
||||
if (props.contextMode === "user") return false;
|
||||
// 系统模式允许编辑
|
||||
// 允许编辑
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, ref, watch, onUnmounted } from 'vue'
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
@@ -656,7 +656,8 @@ if (import.meta.env.DEV) {
|
||||
"performanceLog",
|
||||
);
|
||||
|
||||
setInterval(logPerformance, 10000);
|
||||
const timer = setInterval(logPerformance, 10000);
|
||||
onUnmounted(() => clearInterval(timer));
|
||||
}
|
||||
|
||||
// 暴露方法供父组件调用(兼容 TestAreaPanelInstance 接口)
|
||||
|
||||
@@ -99,6 +99,8 @@ export { default as ContextModeActions } from "./components/context-mode/Context
|
||||
export { default as PromptPreviewPanel } from "./components/PromptPreviewPanel.vue";
|
||||
export { default as ContextSystemWorkspace } from "./components/context-mode/ContextSystemWorkspace.vue";
|
||||
export { default as ContextUserWorkspace } from "./components/context-mode/ContextUserWorkspace.vue";
|
||||
export { default as ContextUserTestPanel } from "./components/context-mode/ContextUserTestPanel.vue";
|
||||
export { default as ConversationTestPanel } from "./components/context-mode/ConversationTestPanel.vue";
|
||||
|
||||
// 导出 Naive UI 组件 (解决组件解析问题)
|
||||
export {
|
||||
|
||||
@@ -85,8 +85,6 @@ export interface ConversationManagerProps extends BaseComponentProps {
|
||||
availableVariables: Record<string, string>
|
||||
/** 优化模式(用于模板分类) */
|
||||
optimizationMode?: 'system' | 'user'
|
||||
/** 上下文模式(用于控制UI行为) */
|
||||
contextMode?: import('@prompt-optimizer/core').ContextMode
|
||||
/** 变量扫描函数(标准化注入) */
|
||||
scanVariables?: (content: string) => string[]
|
||||
/** 变量替换函数(标准化注入) */
|
||||
|
||||
@@ -2146,16 +2146,18 @@ const getActiveTestPanelInstance = (): TestAreaPanelInstance | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
// 基础模式和 context-user 模式的测试处理函数
|
||||
// 注意:context-system 模式已在 ContextSystemWorkspace 内部使用 useConversationTester 处理,不会调用此函数
|
||||
// 基础模式的测试处理函数
|
||||
// 注意:
|
||||
// 1. Context System 模式在 ContextSystemWorkspace 内部使用 useConversationTester 处理
|
||||
// 2. Context User 模式在 ContextUserWorkspace 内部使用 useContextUserTester 处理
|
||||
// 3. 此函数仅被 Basic Mode 的 TestAreaPanel 调用
|
||||
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
|
||||
// 调用基础测试器(只用于基础模式和 context-user)
|
||||
await promptTester.executeTest(
|
||||
optimizer.prompt,
|
||||
optimizer.optimizedPrompt,
|
||||
testContent.value,
|
||||
isCompareMode.value,
|
||||
testVariables
|
||||
testVariables || {}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user