节点新增字段和提示词弹窗
This commit is contained in:
@@ -37,6 +37,8 @@ export interface NodeLibraryItem {
|
||||
nodeName: string;
|
||||
modelType: number;
|
||||
skillOption: boolean;
|
||||
promptOption: boolean;
|
||||
isSaveFile: boolean;
|
||||
formConfig: NodeLibraryFormItem[];
|
||||
modelConfig: NodeLibraryModelConfig[];
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface PromptListParams {
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
keyword?: string;
|
||||
nodeType?: string;
|
||||
}
|
||||
|
||||
export interface CreatePromptParams {
|
||||
@@ -65,6 +66,17 @@ export function getNodeLibraryList() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提示词列表(根据节点类型)
|
||||
*/
|
||||
export function getPromptList(params: PromptListParams) {
|
||||
return request<PromptListResponse>({
|
||||
url: '/ai-agent/node/prompt/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户提示词列表
|
||||
*/
|
||||
|
||||
242
src/views/settings/creation/component/PromptSelector.vue
Normal file
242
src/views/settings/creation/component/PromptSelector.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="选择提示词"
|
||||
width="900px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="prompt-selector-dialog">
|
||||
<div class="prompt-header">
|
||||
<div class="search-bar">
|
||||
<el-input v-model="searchParams.keyword" placeholder="搜索提示词内容" clearable @clear="handleSearch" @keyup.enter="handleSearch">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="prompt-content" v-loading="loading">
|
||||
<el-empty v-if="!loading && promptList.length === 0" description="暂无提示词数据" :image-size="100" />
|
||||
<el-table v-else :data="promptList" height="360" border stripe style="width: 100%" @row-click="handleSelectPrompt">
|
||||
<el-table-column label="节点类型" width="120" prop="nodeType" />
|
||||
<el-table-column label="提示词内容" prop="prompt" show-overflow-tooltip />
|
||||
<el-table-column label="来源" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.sourceType === 1 ? 'success' : 'info'" size="small">
|
||||
{{ row.sourceType === 1 ? '公共' : '自定义' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="选择" width="60" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-icon v-if="selectedPrompt?.id === row.id" class="check-icon" color="#67c23a">
|
||||
<CircleCheck />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-if="shouldRenderPagination" class="pagination-panel">
|
||||
<div class="pagination-summary">共 {{ displayTotal }} 条</div>
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.pageNum"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="displayTotal"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="sizes, prev, pager, next, jumper"
|
||||
background
|
||||
:hide-on-single-page="false"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :disabled="!selectedPrompt" @click="handleConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { Search, CircleCheck } from '@element-plus/icons-vue';
|
||||
import { getPromptList, type PromptItem } from '/@/api/settings/promptManager';
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
defaultPrompt?: PromptItem | null;
|
||||
nodeType?: string;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void;
|
||||
(e: 'confirm', prompt: PromptItem): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
defaultPrompt: null,
|
||||
nodeType: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const promptList = ref<PromptItem[]>([]);
|
||||
const selectedPrompt = ref<PromptItem | null>(null);
|
||||
const searchParams = reactive({ keyword: '' });
|
||||
const pagination = reactive({ pageNum: 1, pageSize: 10, total: 0 });
|
||||
|
||||
const displayTotal = computed(() => {
|
||||
if (pagination.total > 0) return pagination.total;
|
||||
if (promptList.value.length > 0) return promptList.value.length;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const shouldRenderPagination = computed(() => displayTotal.value > 0);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
visible.value = val;
|
||||
if (val) {
|
||||
selectedPrompt.value = props.defaultPrompt || null;
|
||||
pagination.pageNum = 1;
|
||||
fetchPromptList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.nodeType,
|
||||
() => {
|
||||
if (visible.value) {
|
||||
pagination.pageNum = 1;
|
||||
fetchPromptList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(visible, (val) => {
|
||||
if (!val) {
|
||||
emit('update:modelValue', false);
|
||||
}
|
||||
});
|
||||
|
||||
const resolvePromptList = (payload: any): PromptItem[] => {
|
||||
if (Array.isArray(payload?.data?.list)) return payload.data.list;
|
||||
if (Array.isArray(payload?.list)) return payload.list;
|
||||
if (Array.isArray(payload?.rows)) return payload.rows;
|
||||
return [];
|
||||
};
|
||||
|
||||
const resolvePromptTotal = (payload: any, list: PromptItem[]) => {
|
||||
const totalCandidates = [payload?.data?.total, payload?.total, payload?.data?.count, payload?.count];
|
||||
const validTotal = totalCandidates.find((item) => typeof item === 'number' && !Number.isNaN(item));
|
||||
if (typeof validTotal === 'number') return validTotal;
|
||||
return list.length;
|
||||
};
|
||||
|
||||
const fetchPromptList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const params = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
keyword: searchParams.keyword || undefined,
|
||||
nodeType: props.nodeType || undefined,
|
||||
};
|
||||
const res = await getPromptList(params);
|
||||
const list = resolvePromptList(res);
|
||||
promptList.value = list;
|
||||
pagination.total = resolvePromptTotal(res, list);
|
||||
} catch (error) {
|
||||
promptList.value = [];
|
||||
pagination.total = 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.pageNum = 1;
|
||||
fetchPromptList();
|
||||
};
|
||||
|
||||
const handlePageChange = () => {
|
||||
fetchPromptList();
|
||||
};
|
||||
|
||||
const handleSizeChange = (size: number) => {
|
||||
pagination.pageSize = size;
|
||||
pagination.pageNum = 1;
|
||||
fetchPromptList();
|
||||
};
|
||||
|
||||
const handleSelectPrompt = (prompt: PromptItem) => {
|
||||
selectedPrompt.value = prompt;
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!selectedPrompt.value) return;
|
||||
emit('confirm', selectedPrompt.value);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
selectedPrompt.value = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.prompt-selector-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.prompt-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.search-bar :deep(.el-input) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.prompt-content {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.pagination-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.pagination-summary {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination-panel :deep(.el-pagination) {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user