Files
admin-ui/src/views/settings/promptManager/index.vue

315 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="prompt-manager-container">
<!-- 搜索栏 -->
<div class="search-bar">
<el-input v-model="searchQuery" placeholder="搜索提示词内容..." prefix-icon="Search" style="width: 300px" clearable @input="handleSearch" />
<el-button type="primary" @click="showCreateDialog = true">
<el-icon><Plus /></el-icon>
新增提示词
</el-button>
</div>
<!-- 列表 -->
<div class="table-container">
<el-table :data="filteredList" v-loading="loading" border style="width: 100%">
<el-table-column prop="nodeType" label="节点类型" width="150" />
<el-table-column prop="prompt" label="提示词内容" min-width="350">
<template #default="{ row }">
<el-tooltip v-if="row.prompt.length > 80" content="hover" :enterable="false">
<template #content>
<div style="max-width: 400px; white-space: pre-wrap">{{ row.prompt }}</div>
</template>
<span>{{ row.prompt.slice(0, 80) }}...</span>
</el-tooltip>
<span v-else>{{ row.prompt }}</span>
</template>
</el-table-column>
<el-table-column prop="sourceType" label="来源" width="100">
<template #default="{ row }">
<el-tag v-if="row.sourceType === 1" type="success">管理员</el-tag>
<el-tag v-else-if="row.sourceType === 2">用户</el-tag>
<el-tag v-else type="info">{{ row.sourceType }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="creator" label="创建者" width="120" />
<el-table-column prop="createdAt" label="创建时间" width="170" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDeleteConfirm(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.size"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, ->, prev, pager, next, jumper"
:total="pagination.total"
/>
</div>
</div>
<!-- 新增/编辑弹窗 -->
<el-dialog v-model="showCreateDialog" :title="isEdit ? '编辑提示词' : '新增提示词'" width="600px" :close-on-click-modal="false">
<el-form ref="createFormRef" :model="createForm" label-width="100px">
<el-form-item label="节点类型" required>
<el-select v-model="createForm.nodeType" placeholder="请选择节点类型" style="width: 100%" clearable v-loading="nodeLibraryLoading">
<el-option v-for="option in nodeOptions" :key="option.value" :label="option.label" :value="option.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="提示词内容" required>
<el-input v-model="createForm.prompt" type="textarea" :rows="8" placeholder="请输入提示词内容..." clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="cancelCreate"> </el-button>
<el-button type="primary" @click="handleSubmitCreate" :loading="submitLoading">
{{ isEdit ? '保存修改' : '确 定' }}
</el-button>
</span>
</template>
</el-dialog>
<!-- 删除确认弹窗 -->
<el-dialog v-model="showDeleteDialog" title="确认删除" width="400px" :close-on-click-modal="false">
<div>确定要删除这个提示词吗</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDeleteDialog = false"> </el-button>
<el-button type="primary" @click="handleDelete" :loading="deleteLoading"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="promptManager">
import { ref, reactive, computed, onMounted } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { getNodeLibraryList, getMyPromptList, createPrompt, updatePrompt, deletePrompt, type NodeLibraryGroup, type NodeLibraryNode, type PromptItem } from '/@/api/settings/promptManager/index';
const searchQuery = ref('');
const loading = ref(false);
const list = ref<PromptItem[]>([]);
const showCreateDialog = ref(false);
const isEdit = ref(false);
const submitLoading = ref(false);
const createFormRef = ref();
const editId = ref<number | string | null>(null);
const nodeLibraryLoading = ref(false);
const nodeLibraryGroups = ref<NodeLibraryGroup[]>([]);
const showDeleteDialog = ref(false);
const deleteLoading = ref(false);
const deleteId = ref<number | string | null>(null);
const nodeOptions = computed(() => {
const options: Array<{ label: string; value: string }> = [];
nodeLibraryGroups.value.forEach((group: NodeLibraryGroup) => {
group.items.forEach((item: NodeLibraryNode) => {
// 只显示支持提示词配置的节点
if (item.promptOption) {
options.push({
label: item.nodeName,
value: item.nodeCode,
});
}
});
});
return options;
});
const createForm = reactive({
nodeType: '',
prompt: '',
sourceType: 2 as number,
});
// 加载节点库
const loadNodeLibrary = async () => {
nodeLibraryLoading.value = true;
try {
const res = await getNodeLibraryList();
if (res && res.data && res.data.groups) {
nodeLibraryGroups.value = res.data.groups;
}
} catch (e) {
console.error('加载节点库失败:', e);
ElMessage.error('加载节点类型失败');
} finally {
nodeLibraryLoading.value = false;
}
};
const pagination = reactive({
current: 1,
size: 10,
total: 0,
});
// 过滤后的列表
const filteredList = computed(() => {
if (!searchQuery.value) {
return list.value;
}
const query = searchQuery.value.toLowerCase();
return list.value.filter(
(item) => item.nodeType.toLowerCase().includes(query) || item.prompt.toLowerCase().includes(query) || item.creator?.toLowerCase().includes(query)
);
});
// 加载列表
const loadList = async () => {
loading.value = true;
try {
const res = await getMyPromptList({
pageNum: pagination.current,
pageSize: pagination.size,
});
if (res && res.data && res.data.list) {
list.value = res.data.list;
pagination.total = res.data.total || 0;
}
} catch (e) {
console.error(e);
ElMessage.error('加载失败');
} finally {
loading.value = false;
}
};
// 搜索
const handleSearch = () => {
pagination.current = 1;
loadList();
};
// 新增
const handleSubmitCreate = async () => {
// 验证
if (!createForm.nodeType?.trim()) {
ElMessage.warning('请选择节点类型');
return;
}
if (!createForm.prompt?.trim()) {
ElMessage.warning('请输入提示词内容');
return;
}
submitLoading.value = true;
try {
if (isEdit.value && editId.value) {
await updatePrompt({
id: editId.value,
...createForm,
});
ElMessage.success('修改成功');
} else {
await createPrompt({
nodeType: createForm.nodeType.trim(),
prompt: createForm.prompt.trim(),
sourceType: createForm.sourceType,
});
ElMessage.success('创建成功');
}
showCreateDialog.value = false;
cancelCreate();
loadList();
} catch (e) {
console.error(e);
ElMessage.error('操作失败');
} finally {
submitLoading.value = false;
}
};
// 编辑
const handleEdit = (row: PromptItem) => {
isEdit.value = true;
editId.value = row.id;
createForm.nodeType = row.nodeType || '';
createForm.prompt = row.prompt || '';
createForm.sourceType = row.sourceType || 0;
showCreateDialog.value = true;
};
// 打开删除确认弹窗
const handleDeleteConfirm = (id: number | string) => {
deleteId.value = id;
showDeleteDialog.value = true;
};
// 删除
const handleDelete = async () => {
if (!deleteId.value) return;
deleteLoading.value = true;
try {
await deletePrompt(deleteId.value);
ElMessage.success('删除成功');
showDeleteDialog.value = false;
loadList();
} catch (e) {
console.error(e);
ElMessage.error('删除失败');
} finally {
deleteLoading.value = false;
}
};
// 取消
const cancelCreate = () => {
createFormRef.value?.resetFields();
isEdit.value = false;
editId.value = null;
createForm.nodeType = '';
createForm.prompt = '';
createForm.sourceType = 2;
};
// 分页
const handleSizeChange = (size: number) => {
pagination.size = size;
pagination.current = 1;
loadList();
};
const handleCurrentChange = (current: number) => {
pagination.current = current;
loadList();
};
onMounted(() => {
loadNodeLibrary();
loadList();
});
</script>
<style scoped lang="scss">
.prompt-manager-container {
padding: 16px;
background-color: #fff;
border-radius: 4px;
.search-bar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.table-container {
.pagination-container {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
}
}
</style>