重构知识库相关接口,更新数据结构和命名,移除示例文件,调整组件和视图以支持新命名,优化文档和数据集管理功能。
This commit is contained in:
@@ -13,22 +13,20 @@
|
||||
<div class="document-content">
|
||||
<div class="content-header">
|
||||
<h2>{{ documentInfo.name }}</h2>
|
||||
<div class="content-meta">
|
||||
Size:{{ formatFileSize(documentInfo.fileSize) }} Uploaded Time:{{ documentInfo.createdAt }}
|
||||
</div>
|
||||
<div class="content-meta">Size:{{ formatFileSize(documentInfo.fileSize) }} Uploaded Time:{{ documentInfo.createdAt }}</div>
|
||||
</div>
|
||||
<div class="content-body" v-loading="contentLoading">
|
||||
<pre class="document-text">{{ documentContent }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 右侧:切片结果 -->
|
||||
<div class="chunk-panel">
|
||||
<div class="panel-header">
|
||||
<h3>切片结果</h3>
|
||||
<div class="panel-subtitle">查看用于嵌入和召回的切片段落。</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel-toolbar">
|
||||
<div class="toolbar-tabs">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
@@ -37,13 +35,7 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-input
|
||||
v-model="chunkSearch"
|
||||
placeholder="搜索"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-input v-model="chunkSearch" placeholder="搜索" clearable size="small" style="width: 120px">
|
||||
<template #prefix>
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
</template>
|
||||
@@ -53,17 +45,13 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="chunk-list" v-loading="chunkLoading">
|
||||
<div class="select-all">
|
||||
<el-checkbox v-model="selectAll" @change="onSelectAllChange">选择所有</el-checkbox>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="chunk-item"
|
||||
v-for="chunk in filteredChunks"
|
||||
:key="chunk.id"
|
||||
>
|
||||
|
||||
<div class="chunk-item" v-for="chunk in filteredChunks" :key="chunk.id">
|
||||
<div class="chunk-checkbox">
|
||||
<el-checkbox v-model="chunk.selected" />
|
||||
</div>
|
||||
@@ -74,10 +62,10 @@
|
||||
<el-switch v-model="chunk.enabled" size="small" @change="onChunkStatusChange(chunk)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-empty v-if="filteredChunks.length === 0 && !chunkLoading" description="暂无切片" :image-size="60" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="panel-footer">
|
||||
<span class="total-info">总共 {{ chunkTotal }} 条</span>
|
||||
@@ -112,8 +100,8 @@ import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
datasetId: string;
|
||||
datasetName: string;
|
||||
knowledgeId: string;
|
||||
knowledgeName: string;
|
||||
document: any;
|
||||
}>();
|
||||
|
||||
@@ -150,9 +138,7 @@ const selectAll = ref(false);
|
||||
// 过滤后的切片列表
|
||||
const filteredChunks = computed(() => {
|
||||
if (!chunkSearch.value) return chunkList.value;
|
||||
return chunkList.value.filter((chunk) =>
|
||||
(chunk.content || '').toLowerCase().includes(chunkSearch.value.toLowerCase())
|
||||
);
|
||||
return chunkList.value.filter((chunk) => (chunk.content || '').toLowerCase().includes(chunkSearch.value.toLowerCase()));
|
||||
});
|
||||
|
||||
// 格式化文件大小
|
||||
@@ -179,7 +165,7 @@ const getDocumentDetail = async () => {
|
||||
documentInfo.fileType = props.document?.fileType || 'txt';
|
||||
documentInfo.fileSize = props.document?.fileSize || 10;
|
||||
documentInfo.createdAt = props.document?.createdAt || '22/01/2026 00:53:32';
|
||||
|
||||
|
||||
// 模拟文档内容
|
||||
documentContent.value = '<p>123</p>';
|
||||
} catch (_error) {
|
||||
@@ -215,7 +201,7 @@ const getChunkList = async () => {
|
||||
|
||||
// 全选变化
|
||||
const onSelectAllChange = (val: boolean) => {
|
||||
chunkList.value.forEach(chunk => {
|
||||
chunkList.value.forEach((chunk) => {
|
||||
chunk.selected = val;
|
||||
});
|
||||
};
|
||||
@@ -231,12 +217,15 @@ const onAddChunk = () => {
|
||||
};
|
||||
|
||||
// 监听弹窗打开
|
||||
watch(() => props.modelValue, (val) => {
|
||||
if (val && props.document) {
|
||||
getDocumentDetail();
|
||||
getChunkList();
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val && props.document) {
|
||||
getDocumentDetail();
|
||||
getChunkList();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -250,7 +239,7 @@ watch(() => props.modelValue, (val) => {
|
||||
.drawer-content {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
|
||||
// 左侧文档内容
|
||||
.document-content {
|
||||
flex: 1;
|
||||
@@ -258,30 +247,30 @@ watch(() => props.modelValue, (val) => {
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #ebeef5;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
.content-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
h2 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.content-meta {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.content-body {
|
||||
flex: 1;
|
||||
padding: 16px 20px;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
|
||||
|
||||
.document-text {
|
||||
margin: 0;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
@@ -293,53 +282,53 @@ watch(() => props.modelValue, (val) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 右侧切片面板
|
||||
.chunk-panel {
|
||||
width: 420px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
h3 {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.panel-subtitle {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunk-list {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 12px 20px;
|
||||
|
||||
|
||||
.select-all {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
.chunk-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -348,16 +337,16 @@ watch(() => props.modelValue, (val) => {
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.chunk-checkbox {
|
||||
margin-right: 12px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
|
||||
.chunk-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
|
||||
.chunk-text {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
@@ -365,14 +354,14 @@ watch(() => props.modelValue, (val) => {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunk-actions {
|
||||
margin-left: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.panel-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -380,7 +369,7 @@ watch(() => props.modelValue, (val) => {
|
||||
gap: 12px;
|
||||
padding: 12px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.total-info {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="isEdit ? '编辑数据集' : '新增数据集'"
|
||||
v-model="isShowDialog"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
@close="onCancel"
|
||||
>
|
||||
<el-dialog :title="isEdit ? '编辑数据集' : '新增数据集'" v-model="isShowDialog" width="600px" :close-on-click-modal="false" @close="onCancel">
|
||||
<el-form ref="formRef" :model="ruleForm" :rules="rules" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
@@ -67,7 +61,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'editDataset',
|
||||
name: 'editknowledge',
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -75,10 +69,10 @@ export default {
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { createDataset, updateDataset, getDataset } from '/@/api/knowledge/dataset';
|
||||
import { createknowledge, updateknowledge, getknowledge } from '/@/api/knowledge/knowledge';
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['getDatasetList']);
|
||||
const emit = defineEmits(['getknowledgeList']);
|
||||
|
||||
// 表单ref
|
||||
const formRef = ref<FormInstance>();
|
||||
@@ -121,10 +115,10 @@ const resetForm = () => {
|
||||
const openDialog = async (row?: any) => {
|
||||
resetForm();
|
||||
isEdit.value = !!row;
|
||||
|
||||
|
||||
if (row) {
|
||||
try {
|
||||
const res: any = await getDataset(row.id);
|
||||
const res: any = await getknowledge(row.id);
|
||||
const data = res.data || row;
|
||||
ruleForm.id = data.id || '';
|
||||
ruleForm.name = data.name || '';
|
||||
@@ -145,7 +139,7 @@ const openDialog = async (row?: any) => {
|
||||
ruleForm.charCount = row.charCount || 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isShowDialog.value = true;
|
||||
};
|
||||
|
||||
@@ -159,7 +153,7 @@ const onCancel = () => {
|
||||
const onSubmit = async () => {
|
||||
const form = formRef.value;
|
||||
if (!form) return;
|
||||
|
||||
|
||||
form.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
@@ -172,17 +166,17 @@ const onSubmit = async () => {
|
||||
embeddingModel: ruleForm.embeddingModel,
|
||||
status: 'enable',
|
||||
};
|
||||
|
||||
|
||||
if (isEdit.value) {
|
||||
await updateDataset(data);
|
||||
await updateknowledge(data);
|
||||
ElMessage.success('保存成功');
|
||||
} else {
|
||||
await createDataset(data);
|
||||
await createknowledge(data);
|
||||
ElMessage.success('创建成功');
|
||||
}
|
||||
|
||||
|
||||
isShowDialog.value = false;
|
||||
emit('getDatasetList');
|
||||
emit('getknowledgeList');
|
||||
} catch (_error) {
|
||||
ElMessage.error(isEdit.value ? '保存失败,请重试' : '创建失败,请重试');
|
||||
} finally {
|
||||
@@ -198,5 +192,4 @@ defineExpose({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="knowledge-dataset-page">
|
||||
<div class="knowledge-dataset-container">
|
||||
<div class="knowledge-knowledge-page">
|
||||
<div class="knowledge-knowledge-container">
|
||||
<el-card shadow="hover">
|
||||
<div class="knowledge-dataset-search mb15">
|
||||
<div class="knowledge-knowledge-search mb15">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="数据集名称">
|
||||
<el-input size="default" v-model="tableData.param.keyword" placeholder="请输入数据集名称" clearable style="width: 200px" />
|
||||
@@ -21,7 +21,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button size="default" type="primary" @click="getDatasetList">
|
||||
<el-button size="default" type="primary" @click="getknowledgeList">
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
查询
|
||||
</el-button>
|
||||
@@ -29,7 +29,7 @@
|
||||
<el-icon><ele-Refresh /></el-icon>
|
||||
重置
|
||||
</el-button>
|
||||
<el-button size="default" type="success" @click="onOpenAdd" v-auth="'api/v1/knowledge/dataset/create'">
|
||||
<el-button size="default" type="success" @click="onOpenAdd" v-auth="'api/v1/knowledge/knowledge/create'">
|
||||
<el-icon><ele-Plus /></el-icon>
|
||||
新增
|
||||
</el-button>
|
||||
@@ -63,7 +63,7 @@
|
||||
active-text="启"
|
||||
inactive-text="停"
|
||||
@change="onStatusChange(scope.row)"
|
||||
v-auth="'api/v1/knowledge/dataset/updateStatus'"
|
||||
v-auth="'api/v1/knowledge/knowledge/updateStatus'"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -71,9 +71,9 @@
|
||||
<el-table-column prop="updatedAt" label="更新时间" width="170" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="200" fixed="right" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onEdit(scope.row)" v-auth="'api/v1/knowledge/dataset/update'">编辑</el-button>
|
||||
<el-button size="small" text type="primary" @click="onEdit(scope.row)" v-auth="'api/v1/knowledge/knowledge/update'">编辑</el-button>
|
||||
<el-button size="small" text type="success" @click="onManageDocuments(scope.row)">文档</el-button>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)" v-auth="'api/v1/knowledge/dataset/delete'">删除</el-button>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)" v-auth="'api/v1/knowledge/knowledge/delete'">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -91,13 +91,13 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<EditDataset ref="editDatasetRef" @getDatasetList="getDatasetList" />
|
||||
<Editknowledge ref="editknowledgeRef" @getknowledgeList="getknowledgeList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'knowledgeDataset',
|
||||
name: 'knowledgeknowledge',
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -105,8 +105,8 @@ export default {
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { listDatasets, deleteDataset, updateDatasetStatus } from '/@/api/knowledge/dataset';
|
||||
import EditDataset from './component/editDataset.vue';
|
||||
import { listknowledges, deleteknowledge, updateknowledgeStatus } from '/@/api/knowledge/knowledge';
|
||||
import Editknowledge from './component/editknowledge.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -125,13 +125,13 @@ const tableData = reactive({
|
||||
});
|
||||
|
||||
// 编辑弹窗ref
|
||||
const editDatasetRef = ref();
|
||||
const editknowledgeRef = ref();
|
||||
|
||||
// 获取数据集列表
|
||||
const getDatasetList = async () => {
|
||||
const getknowledgeList = async () => {
|
||||
tableData.loading = true;
|
||||
try {
|
||||
const res: any = await listDatasets(tableData.param);
|
||||
const res: any = await listknowledges(tableData.param);
|
||||
const list = res.data?.list || [];
|
||||
tableData.data = list.map((item: any) => ({
|
||||
...item,
|
||||
@@ -190,27 +190,27 @@ const onResetQuery = () => {
|
||||
tableData.param.type = '';
|
||||
tableData.param.status = undefined;
|
||||
tableData.param.pageNum = 1;
|
||||
getDatasetList();
|
||||
getknowledgeList();
|
||||
};
|
||||
|
||||
// 打开新增弹窗
|
||||
const onOpenAdd = () => {
|
||||
editDatasetRef.value.openDialog();
|
||||
editknowledgeRef.value.openDialog();
|
||||
};
|
||||
|
||||
// 打开编辑弹窗
|
||||
const onEdit = (row: any) => {
|
||||
editDatasetRef.value.openDialog(row);
|
||||
editknowledgeRef.value.openDialog(row);
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const onViewDetail = (row: any) => {
|
||||
router.push({ path: '/knowledge/document', query: { datasetId: row.id, datasetName: row.name } });
|
||||
router.push({ path: '/knowledge/document', query: { knowledgeId: row.id, knowledgeName: row.name } });
|
||||
};
|
||||
|
||||
// 管理文档
|
||||
const onManageDocuments = (row: any) => {
|
||||
router.push({ path: '/knowledge/document', query: { datasetId: row.id, datasetName: row.name } });
|
||||
router.push({ path: '/knowledge/document', query: { knowledgeId: row.id, knowledgeName: row.name } });
|
||||
};
|
||||
|
||||
// 状态切换
|
||||
@@ -218,7 +218,7 @@ const onStatusChange = async (row: any) => {
|
||||
const newStatus = row.statusEnabled ? 'enable' : 'disable';
|
||||
const statusText = row.statusEnabled ? '启用' : '禁用';
|
||||
try {
|
||||
await updateDatasetStatus({ id: row.id, status: newStatus });
|
||||
await updateknowledgeStatus({ id: row.id, status: newStatus });
|
||||
ElMessage.success(`${statusText}成功`);
|
||||
} catch (_error) {
|
||||
row.statusEnabled = !row.statusEnabled;
|
||||
@@ -232,40 +232,42 @@ const onRowDel = (row: any) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await deleteDataset(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
getDatasetList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await deleteknowledge(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
getknowledgeList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 分页大小改变
|
||||
const onSizeChange = (size: number) => {
|
||||
tableData.param.pageSize = size;
|
||||
getDatasetList();
|
||||
getknowledgeList();
|
||||
};
|
||||
|
||||
// 当前页改变
|
||||
const onCurrentChange = (page: number) => {
|
||||
tableData.param.pageNum = page;
|
||||
getDatasetList();
|
||||
getknowledgeList();
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
getDatasetList();
|
||||
getknowledgeList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.knowledge-dataset-page {
|
||||
.knowledge-knowledge-page {
|
||||
padding: 15px;
|
||||
.knowledge-dataset-container {
|
||||
.knowledge-dataset-search {
|
||||
.knowledge-knowledge-container {
|
||||
.knowledge-knowledge-search {
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="isShowDrawer"
|
||||
title="文档分段管理"
|
||||
size="70%"
|
||||
:close-on-click-modal="true"
|
||||
>
|
||||
<el-drawer v-model="isShowDrawer" title="文档分段管理" size="70%" :close-on-click-modal="true">
|
||||
<div class="chunks-container" v-loading="loading">
|
||||
<div class="chunks-header">
|
||||
<div class="doc-info">
|
||||
@@ -12,13 +7,7 @@
|
||||
<el-tag size="small" type="info">共 {{ tableData.total }} 个分段</el-tag>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索分段内容"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="onSearch"
|
||||
>
|
||||
<el-input v-model="searchKeyword" placeholder="搜索分段内容" clearable style="width: 200px" @keyup.enter="onSearch">
|
||||
<template #append>
|
||||
<el-button @click="onSearch">
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
@@ -27,13 +16,9 @@
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="chunks-list">
|
||||
<div
|
||||
class="chunk-item"
|
||||
v-for="chunk in tableData.data"
|
||||
:key="chunk.id"
|
||||
>
|
||||
<div class="chunk-item" v-for="chunk in tableData.data" :key="chunk.id">
|
||||
<div class="chunk-header">
|
||||
<div class="chunk-index">
|
||||
<el-tag size="small"># {{ chunk.chunkIndex + 1 }}</el-tag>
|
||||
@@ -55,22 +40,17 @@
|
||||
{{ chunk.content }}
|
||||
</div>
|
||||
<div class="chunk-edit" v-else>
|
||||
<el-input
|
||||
v-model="chunk.editContent"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入分段内容"
|
||||
/>
|
||||
<el-input v-model="chunk.editContent" type="textarea" :rows="6" placeholder="请输入分段内容" />
|
||||
<div class="edit-actions">
|
||||
<el-button size="small" @click="onCancelEdit(chunk)">取消</el-button>
|
||||
<el-button size="small" type="primary" @click="onSaveChunk(chunk)" :loading="chunk.saving">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-empty v-if="tableData.data.length === 0" description="暂无分段数据" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="chunks-pagination" v-if="tableData.total > 0">
|
||||
<el-pagination
|
||||
@@ -165,7 +145,7 @@ const onSaveChunk = async (chunk: any) => {
|
||||
ElMessage.warning('分段内容不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
chunk.saving = true;
|
||||
try {
|
||||
await updateDocumentChunk({ id: chunk.id, content: chunk.editContent });
|
||||
@@ -186,15 +166,17 @@ const onDeleteChunk = (chunk: any) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await deleteDocumentChunk(chunk.id);
|
||||
ElMessage.success('删除成功');
|
||||
getChunkList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await deleteDocumentChunk(chunk.id);
|
||||
ElMessage.success('删除成功');
|
||||
getChunkList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 分页大小改变
|
||||
@@ -231,66 +213,67 @@ defineExpose({
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
.chunks-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
.doc-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
|
||||
.doc-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunks-list {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
|
||||
.chunk-item {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px 16px;
|
||||
background: #fff;
|
||||
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
|
||||
.chunk-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
|
||||
.chunk-index {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.char-count, .token-count {
|
||||
|
||||
.char-count,
|
||||
.token-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunk-content {
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
.chunk-edit {
|
||||
.edit-actions {
|
||||
margin-top: 10px;
|
||||
@@ -299,7 +282,7 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunks-pagination {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="isShowDrawer"
|
||||
title="文档预览"
|
||||
size="60%"
|
||||
:close-on-click-modal="true"
|
||||
>
|
||||
<el-drawer v-model="isShowDrawer" title="文档预览" size="60%" :close-on-click-modal="true">
|
||||
<div class="preview-container" v-loading="loading">
|
||||
<div class="preview-header">
|
||||
<div class="file-info">
|
||||
@@ -37,7 +32,7 @@
|
||||
<el-tab-pane label="文档信息" name="info">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="文档名称">{{ documentInfo.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属数据集">{{ documentInfo.datasetName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属数据集">{{ documentInfo.knowledgeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件类型">{{ documentInfo.fileType?.toUpperCase() }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小">{{ formatFileSize(documentInfo.fileSize) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="字符数">{{ documentInfo.charCount }}</el-descriptions-item>
|
||||
@@ -81,8 +76,8 @@ const activeTab = ref('content');
|
||||
const documentInfo = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
datasetId: '',
|
||||
datasetName: '',
|
||||
knowledgeId: '',
|
||||
knowledgeName: '',
|
||||
fileType: '',
|
||||
fileSize: 0,
|
||||
filePath: '',
|
||||
@@ -207,15 +202,12 @@ const openDialog = async (row: any) => {
|
||||
documentContent.value = '';
|
||||
activeTab.value = 'content';
|
||||
isShowDrawer.value = true;
|
||||
|
||||
|
||||
// 获取文档详情和内容
|
||||
loading.value = true;
|
||||
try {
|
||||
const [detailRes, contentRes] = await Promise.all([
|
||||
getDocument(row.id),
|
||||
previewDocument(row.id),
|
||||
]);
|
||||
|
||||
const [detailRes, contentRes] = await Promise.all([getDocument(row.id), previewDocument(row.id)]);
|
||||
|
||||
if (detailRes.data) {
|
||||
Object.assign(documentInfo, detailRes.data);
|
||||
}
|
||||
@@ -250,31 +242,31 @@ defineExpose({
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
.preview-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.file-icon {
|
||||
font-size: 48px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
|
||||
.file-detail {
|
||||
h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
|
||||
.meta {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
|
||||
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
@@ -282,11 +274,11 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.preview-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
||||
|
||||
.content-area {
|
||||
padding: 16px;
|
||||
background: #f5f7fa;
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="上传文档"
|
||||
v-model="isShowDialog"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
@close="onCancel"
|
||||
>
|
||||
<el-dialog title="上传文档" v-model="isShowDialog" width="600px" :close-on-click-modal="false" @close="onCancel">
|
||||
<el-form ref="formRef" :model="ruleForm" :rules="rules" label-width="100px">
|
||||
<el-form-item label="所属数据集" prop="datasetId">
|
||||
<el-select v-model="ruleForm.datasetId" placeholder="请选择数据集" clearable style="width: 100%" :disabled="!!currentDatasetId">
|
||||
<el-option v-for="item in datasetOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<el-form-item label="所属数据集" prop="knowledgeId">
|
||||
<el-select v-model="ruleForm.datasetId" placeholder="请选择数据集" clearable style="width: 100%" :disabled="!!currentknowledgeId">
|
||||
<el-option v-for="item in knowledgeOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传文件" prop="files">
|
||||
@@ -25,13 +19,9 @@
|
||||
accept=".pdf,.docx,.doc,.txt,.md,.html"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持 PDF、Word、TXT、Markdown、HTML 格式,单个文件不超过 20MB
|
||||
</div>
|
||||
<div class="el-upload__tip">支持 PDF、Word、TXT、Markdown、HTML 格式,单个文件不超过 20MB</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
@@ -81,8 +71,8 @@ import { uploadDocument } from '/@/api/knowledge/document';
|
||||
|
||||
// 定义props
|
||||
const props = defineProps<{
|
||||
datasetOptions: any[];
|
||||
currentDatasetId?: string;
|
||||
knowledgeOptions: any[];
|
||||
currentknowledgeId?: string;
|
||||
}>();
|
||||
|
||||
// 定义事件
|
||||
@@ -115,7 +105,7 @@ const rules = reactive<FormRules>({
|
||||
|
||||
// 监听当前数据集ID
|
||||
watch(
|
||||
() => props.currentDatasetId,
|
||||
() => props.currentknowledgeId,
|
||||
(val) => {
|
||||
if (val) {
|
||||
ruleForm.datasetId = val;
|
||||
@@ -126,7 +116,7 @@ watch(
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
if (!props.currentDatasetId) {
|
||||
if (!props.currentknowledgeId) {
|
||||
ruleForm.datasetId = '';
|
||||
}
|
||||
ruleForm.chunkSize = 500;
|
||||
@@ -169,14 +159,14 @@ const onCancel = () => {
|
||||
const onSubmit = async () => {
|
||||
const form = formRef.value;
|
||||
if (!form) return;
|
||||
|
||||
|
||||
form.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (fileList.value.length === 0) {
|
||||
ElMessage.warning('请选择要上传的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
submitLoading.value = true;
|
||||
try {
|
||||
// 逐个上传文件
|
||||
@@ -188,10 +178,10 @@ const onSubmit = async () => {
|
||||
formData.append('chunkOverlap', ruleForm.chunkOverlap.toString());
|
||||
formData.append('cleanWhitespace', ruleForm.cleanWhitespace.toString());
|
||||
formData.append('removeHeaders', ruleForm.removeHeaders.toString());
|
||||
|
||||
|
||||
await uploadDocument(formData);
|
||||
}
|
||||
|
||||
|
||||
ElMessage.success(`成功上传 ${fileList.value.length} 个文件`);
|
||||
isShowDialog.value = false;
|
||||
emit('getDocumentList');
|
||||
|
||||
@@ -4,36 +4,34 @@
|
||||
<div class="page-header">
|
||||
<el-breadcrumb separator=">">
|
||||
<el-breadcrumb-item>
|
||||
<span class="back-link" @click="onBackToKnowledge">知识库</span>
|
||||
<span class="back-link" @click="onBackToknowledge">知识库</span>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>
|
||||
<span class="back-link" @click="onBackToDataset">{{ datasetName }}</span>
|
||||
<span class="back-link" @click="onBackToknowledge">{{ knowledgeName }}</span>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>{{ documentInfo.name }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="page-content">
|
||||
<!-- 左侧:文档原文 -->
|
||||
<div class="document-content">
|
||||
<div class="content-header">
|
||||
<h2>{{ documentInfo.name }}</h2>
|
||||
<div class="content-meta">
|
||||
Size:{{ formatFileSize(documentInfo.fileSize) }} Uploaded Time:{{ documentInfo.createdAt }}
|
||||
</div>
|
||||
<div class="content-meta">Size:{{ formatFileSize(documentInfo.fileSize) }} Uploaded Time:{{ documentInfo.createdAt }}</div>
|
||||
</div>
|
||||
<div class="content-body" v-loading="contentLoading">
|
||||
<pre class="document-text">{{ documentContent }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 右侧:切片结果 -->
|
||||
<div class="chunk-panel">
|
||||
<div class="panel-header">
|
||||
<h3>切片结果</h3>
|
||||
<div class="panel-subtitle">查看用于嵌入和召回的切片段落。</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel-toolbar">
|
||||
<div class="toolbar-tabs">
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
@@ -42,13 +40,7 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-input
|
||||
v-model="chunkSearch"
|
||||
placeholder="搜索"
|
||||
clearable
|
||||
size="small"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-input v-model="chunkSearch" placeholder="搜索" clearable size="small" style="width: 120px">
|
||||
<template #prefix>
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
</template>
|
||||
@@ -58,17 +50,13 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="chunk-list" v-loading="chunkLoading">
|
||||
<div class="select-all">
|
||||
<el-checkbox v-model="selectAll" @change="onSelectAllChange">选择所有</el-checkbox>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="chunk-item"
|
||||
v-for="chunk in filteredChunks"
|
||||
:key="chunk.id"
|
||||
>
|
||||
|
||||
<div class="chunk-item" v-for="chunk in filteredChunks" :key="chunk.id">
|
||||
<div class="chunk-checkbox">
|
||||
<el-checkbox v-model="chunk.selected" />
|
||||
</div>
|
||||
@@ -79,10 +67,10 @@
|
||||
<el-switch v-model="chunk.enabled" size="small" @change="onChunkStatusChange(chunk)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-empty v-if="filteredChunks.length === 0 && !chunkLoading" description="暂无切片" :image-size="60" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="panel-footer">
|
||||
<span class="total-info">总共 {{ chunkTotal }} 条</span>
|
||||
@@ -121,8 +109,8 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 路由参数
|
||||
const datasetId = ref('');
|
||||
const datasetName = ref('');
|
||||
const knowledgeId = ref('');
|
||||
const knowledgeName = ref('');
|
||||
const documentId = ref('');
|
||||
|
||||
// 文档信息
|
||||
@@ -151,9 +139,7 @@ const selectAll = ref(false);
|
||||
// 过滤后的切片列表
|
||||
const filteredChunks = computed(() => {
|
||||
if (!chunkSearch.value) return chunkList.value;
|
||||
return chunkList.value.filter((chunk) =>
|
||||
(chunk.content || '').toLowerCase().includes(chunkSearch.value.toLowerCase())
|
||||
);
|
||||
return chunkList.value.filter((chunk) => (chunk.content || '').toLowerCase().includes(chunkSearch.value.toLowerCase()));
|
||||
});
|
||||
|
||||
// 格式化文件大小
|
||||
@@ -171,15 +157,15 @@ const truncateText = (text: string, maxLength: number) => {
|
||||
};
|
||||
|
||||
// 返回知识库列表
|
||||
const onBackToKnowledge = () => {
|
||||
router.push('/knowledge/dataset');
|
||||
const onBackToknowledge = () => {
|
||||
router.push('/knowledge/knowledge');
|
||||
};
|
||||
|
||||
// 返回数据集详情
|
||||
const onBackToDataset = () => {
|
||||
const onBackToknowledge = () => {
|
||||
router.push({
|
||||
path: '/knowledge/document',
|
||||
query: { datasetId: datasetId.value, datasetName: datasetName.value },
|
||||
query: { knowledgeId: knowledgeId.value, knowledgeName: knowledgeName.value },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -195,8 +181,7 @@ const getDocumentDetail = async () => {
|
||||
documentInfo.fileSize = detail.fileSize || 0;
|
||||
documentInfo.createdAt = detail.createdAt || '';
|
||||
const contentData = contentRes.data;
|
||||
documentContent.value =
|
||||
typeof contentData === 'string' ? contentData : contentData?.content || contentData?.text || '';
|
||||
documentContent.value = typeof contentData === 'string' ? contentData : contentData?.content || contentData?.text || '';
|
||||
} catch (_error) {
|
||||
ElMessage.error('获取文档详情失败');
|
||||
documentContent.value = '';
|
||||
@@ -248,12 +233,12 @@ const onAddChunk = () => {
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
datasetId.value = (route.query.datasetId as string) || '';
|
||||
datasetName.value = (route.query.datasetName as string) || '';
|
||||
knowledgeId.value = (route.query.knowledgeId as string) || '';
|
||||
knowledgeName.value = (route.query.knowledgeName as string) || '';
|
||||
documentId.value = (route.query.docId as string) || '';
|
||||
if (!documentId.value) {
|
||||
ElMessage.warning('缺少文档ID,无法查看详情');
|
||||
onBackToDataset();
|
||||
onBackToknowledge();
|
||||
return;
|
||||
}
|
||||
getDocumentDetail();
|
||||
@@ -267,29 +252,29 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f5f7fa;
|
||||
|
||||
|
||||
.page-header {
|
||||
padding: 12px 20px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.back-link {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
|
||||
|
||||
// 左侧文档内容
|
||||
.document-content {
|
||||
flex: 1;
|
||||
@@ -299,30 +284,30 @@ onMounted(() => {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
.content-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
h2 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.content-meta {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.content-body {
|
||||
flex: 1;
|
||||
padding: 16px 20px;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
|
||||
|
||||
.document-text {
|
||||
margin: 0;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
@@ -334,7 +319,7 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 右侧切片面板
|
||||
.chunk-panel {
|
||||
width: 420px;
|
||||
@@ -343,46 +328,46 @@ onMounted(() => {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
h3 {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
|
||||
.panel-subtitle {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.panel-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunk-list {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 12px 20px;
|
||||
|
||||
|
||||
.select-all {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
.chunk-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -391,16 +376,16 @@ onMounted(() => {
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.chunk-checkbox {
|
||||
margin-right: 12px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
|
||||
.chunk-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
|
||||
.chunk-text {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
@@ -408,14 +393,14 @@ onMounted(() => {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.chunk-actions {
|
||||
margin-left: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.panel-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -423,7 +408,7 @@ onMounted(() => {
|
||||
gap: 12px;
|
||||
padding: 12px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
|
||||
|
||||
.total-info {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
<div class="knowledge-document-container">
|
||||
<el-card shadow="hover">
|
||||
<!-- 面包屑导航 -->
|
||||
<div class="breadcrumb-nav mb15" v-if="currentDataset.id">
|
||||
<div class="breadcrumb-nav mb15" v-if="currentknowledge.id">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item :to="{ path: '/knowledge/dataset' }">数据集管理</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>{{ currentDataset.name }}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item :to="{ path: '/knowledge/knowledge' }">数据集管理</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>{{ currentknowledge.name }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="knowledge-document-search mb15">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="所属数据集" v-if="!currentDataset.id">
|
||||
<el-select size="default" v-model="tableData.param.datasetId" placeholder="请选择数据集" clearable style="width: 180px">
|
||||
<el-option v-for="item in datasetOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<el-form-item label="所属数据集" v-if="!currentknowledge.id">
|
||||
<el-select size="default" v-model="tableData.param.knowledgeId" placeholder="请选择数据集" clearable style="width: 180px">
|
||||
<el-option v-for="item in knowledgeOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文档名称">
|
||||
@@ -50,20 +50,20 @@
|
||||
<el-icon><ele-Upload /></el-icon>
|
||||
上传文档
|
||||
</el-button>
|
||||
<el-button size="default" type="danger" @click="onBatchDelete" :disabled="selectedIds.length === 0" v-auth="'api/v1/knowledge/document/batchDelete'">
|
||||
<el-button
|
||||
size="default"
|
||||
type="danger"
|
||||
@click="onBatchDelete"
|
||||
:disabled="selectedIds.length === 0"
|
||||
v-auth="'api/v1/knowledge/document/batchDelete'"
|
||||
>
|
||||
<el-icon><ele-Delete /></el-icon>
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table
|
||||
:data="tableData.data"
|
||||
style="width: 100%"
|
||||
v-loading="tableData.loading"
|
||||
border
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<el-table :data="tableData.data" style="width: 100%" v-loading="tableData.loading" border @selection-change="onSelectionChange">
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="name" label="文档名称" min-width="200" show-overflow-tooltip>
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="datasetName" label="所属数据集" width="150" show-overflow-tooltip v-if="!currentDataset.id" />
|
||||
<el-table-column prop="knowledgeName" label="所属数据集" width="150" show-overflow-tooltip v-if="!currentknowledge.id" />
|
||||
<el-table-column prop="fileType" label="文件类型" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag size="small">{{ scope.row.fileType?.toUpperCase() }}</el-tag>
|
||||
@@ -103,8 +103,18 @@
|
||||
<el-table-column label="操作" width="200" fixed="right" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onPreview(scope.row)">预览</el-button>
|
||||
<el-button size="small" text type="success" @click="onViewChunks(scope.row)" v-auth="'api/v1/knowledge/document/chunks'">分段</el-button>
|
||||
<el-button size="small" text type="warning" @click="onReprocess(scope.row)" v-if="scope.row.status === 'failed'" v-auth="'api/v1/knowledge/document/reprocess'">重试</el-button>
|
||||
<el-button size="small" text type="success" @click="onViewChunks(scope.row)" v-auth="'api/v1/knowledge/document/chunks'"
|
||||
>分段</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
text
|
||||
type="warning"
|
||||
@click="onReprocess(scope.row)"
|
||||
v-if="scope.row.status === 'failed'"
|
||||
v-auth="'api/v1/knowledge/document/reprocess'"
|
||||
>重试</el-button
|
||||
>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)" v-auth="'api/v1/knowledge/document/delete'">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -123,13 +133,18 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 上传文档弹窗 -->
|
||||
<UploadDocument ref="uploadDocumentRef" :datasetOptions="datasetOptions" :currentDatasetId="currentDataset.id" @getDocumentList="getDocumentList" />
|
||||
|
||||
<UploadDocument
|
||||
ref="uploadDocumentRef"
|
||||
:knowledgeOptions="knowledgeOptions"
|
||||
:currentknowledgeId="currentknowledge.id"
|
||||
@getDocumentList="getDocumentList"
|
||||
/>
|
||||
|
||||
<!-- 文档预览弹窗 -->
|
||||
<PreviewDocument ref="previewDocumentRef" />
|
||||
|
||||
|
||||
<!-- 文档分段弹窗 -->
|
||||
<DocumentChunks ref="documentChunksRef" />
|
||||
</div>
|
||||
@@ -146,7 +161,7 @@ import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { listDocuments, deleteDocument, batchDeleteDocuments, reprocessDocument } from '/@/api/knowledge/document';
|
||||
import { listDatasets } from '/@/api/knowledge/dataset';
|
||||
import { listknowledges } from '/@/api/knowledge/knowledge';
|
||||
import UploadDocument from './component/uploadDocument.vue';
|
||||
import PreviewDocument from './component/previewDocument.vue';
|
||||
import DocumentChunks from './component/documentChunks.vue';
|
||||
@@ -154,13 +169,13 @@ import DocumentChunks from './component/documentChunks.vue';
|
||||
const route = useRoute();
|
||||
|
||||
// 当前数据集
|
||||
const currentDataset = reactive({
|
||||
const currentknowledge = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
});
|
||||
|
||||
// 数据集选项
|
||||
const datasetOptions = ref<any[]>([]);
|
||||
const knowledgeOptions = ref<any[]>([]);
|
||||
|
||||
// 选中的文档ID
|
||||
const selectedIds = ref<string[]>([]);
|
||||
@@ -172,7 +187,7 @@ const tableData = reactive({
|
||||
loading: false,
|
||||
param: {
|
||||
keyword: '',
|
||||
datasetId: '',
|
||||
knowledgeId: '',
|
||||
fileType: '',
|
||||
status: undefined as string | undefined,
|
||||
pageNum: 1,
|
||||
@@ -186,12 +201,12 @@ const previewDocumentRef = ref();
|
||||
const documentChunksRef = ref();
|
||||
|
||||
// 获取数据集列表
|
||||
const getDatasetOptions = async () => {
|
||||
const getknowledgeOptions = async () => {
|
||||
try {
|
||||
const res: any = await listDatasets({ pageNum: 1, pageSize: 1000 });
|
||||
datasetOptions.value = res.data?.list || [];
|
||||
const res: any = await listknowledges({ pageNum: 1, pageSize: 1000 });
|
||||
knowledgeOptions.value = res.data?.list || [];
|
||||
} catch (_error) {
|
||||
datasetOptions.value = [];
|
||||
knowledgeOptions.value = [];
|
||||
ElMessage.error('获取数据集列表失败');
|
||||
}
|
||||
};
|
||||
@@ -201,8 +216,8 @@ const getDocumentList = async () => {
|
||||
tableData.loading = true;
|
||||
try {
|
||||
const params = { ...tableData.param };
|
||||
if (currentDataset.id) {
|
||||
params.datasetId = currentDataset.id;
|
||||
if (currentknowledge.id) {
|
||||
params.knowledgeId = currentknowledge.id;
|
||||
}
|
||||
const res: any = await listDocuments(params);
|
||||
tableData.data = res.data?.list || [];
|
||||
@@ -309,8 +324,8 @@ const getIndexStatusText = (status: string) => {
|
||||
// 重置查询
|
||||
const onResetQuery = () => {
|
||||
tableData.param.keyword = '';
|
||||
if (!currentDataset.id) {
|
||||
tableData.param.datasetId = '';
|
||||
if (!currentknowledge.id) {
|
||||
tableData.param.knowledgeId = '';
|
||||
}
|
||||
tableData.param.fileType = '';
|
||||
tableData.param.status = undefined;
|
||||
@@ -346,7 +361,7 @@ const onReprocess = async (row: any) => {
|
||||
|
||||
// 选择变化
|
||||
const onSelectionChange = (selection: any[]) => {
|
||||
selectedIds.value = selection.map(item => item.id);
|
||||
selectedIds.value = selection.map((item) => item.id);
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
@@ -355,15 +370,17 @@ const onBatchDelete = () => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await batchDeleteDocuments(selectedIds.value);
|
||||
ElMessage.success('删除成功');
|
||||
getDocumentList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('批量删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await batchDeleteDocuments(selectedIds.value);
|
||||
ElMessage.success('删除成功');
|
||||
getDocumentList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('批量删除失败');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 删除文档
|
||||
@@ -372,15 +389,17 @@ const onRowDel = (row: any) => {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await deleteDocument(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
getDocumentList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await deleteDocument(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
getDocumentList();
|
||||
} catch (_error) {
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 分页大小改变
|
||||
@@ -399,13 +418,13 @@ const onCurrentChange = (page: number) => {
|
||||
watch(
|
||||
() => route.query,
|
||||
(query) => {
|
||||
if (query.datasetId) {
|
||||
currentDataset.id = query.datasetId as string;
|
||||
currentDataset.name = query.datasetName as string || '';
|
||||
tableData.param.datasetId = currentDataset.id;
|
||||
if (query.knowledgeId) {
|
||||
currentknowledge.id = query.knowledgeId as string;
|
||||
currentknowledge.name = (query.knowledgeName as string) || '';
|
||||
tableData.param.knowledgeId = currentknowledge.id;
|
||||
} else {
|
||||
currentDataset.id = '';
|
||||
currentDataset.name = '';
|
||||
currentknowledge.id = '';
|
||||
currentknowledge.name = '';
|
||||
}
|
||||
getDocumentList();
|
||||
},
|
||||
@@ -414,7 +433,7 @@ watch(
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
getDatasetOptions();
|
||||
getknowledgeOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user