parent
6327866533
commit
05609d63c2
|
@ -26,36 +26,36 @@ export interface DocsVO {
|
||||||
export const DocsApi = {
|
export const DocsApi = {
|
||||||
// 获得文档分页
|
// 获得文档分页
|
||||||
async getDocsPage(params: any) {
|
async getDocsPage(params: any) {
|
||||||
return await request.get({ url: '/aigc/docs/page', params })
|
return await request.get({ url: '/chat/aigc/docs/page', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得文档列表
|
// 获得文档列表
|
||||||
async getDocsList(params: any) {
|
async getDocsList(params: any) {
|
||||||
return await request.get({ url: '/aigc/docs/list', params })
|
return await request.get({ url: '/chat/aigc/docs/list', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得文档详情
|
// 获得文档详情
|
||||||
async getDocsById(id: string) {
|
async getDocsById(id: string) {
|
||||||
return await request.get({ url: `/aigc/docs/${id}` })
|
return await request.get({ url: `/chat/aigc/docs/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 创建文档
|
// 创建文档
|
||||||
async addDocs(data: any) {
|
async addDocs(data: any) {
|
||||||
return await request.post({ url: '/aigc/docs', data })
|
return await request.post({ url: '/chat/aigc/docs', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新文档
|
// 更新文档
|
||||||
async updateDocs(data: any) {
|
async updateDocs(data: any) {
|
||||||
return await request.put({ url: '/aigc/docs', data })
|
return await request.put({ url: '/chat/aigc/docs', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除文档
|
// 删除文档
|
||||||
async deleteDocs(id: string) {
|
async deleteDocs(id: string) {
|
||||||
return await request.delete({ url: `/aigc/docs/${id}` })
|
return await request.delete({ url: `/chat/aigc/docs/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 重新向量化
|
// 重新向量化
|
||||||
async reEmbedDocs(id: string) {
|
async reEmbedDocs(id: string) {
|
||||||
return await request.get({ url: `/aigc/embedding/re-embed/${id}` })
|
return await request.get({ url: `/chat/aigc/embedding/re-embed/${id}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import { AxiosProgressEvent } from 'axios'
|
||||||
export const EmbeddingApi = {
|
export const EmbeddingApi = {
|
||||||
// 文本嵌入
|
// 文本嵌入
|
||||||
embeddingText: async (params: any) => {
|
embeddingText: async (params: any) => {
|
||||||
return await request.post({ url: '/aigc/embedding/text', params })
|
return await request.post({ url: '/chat/aigc/embedding/text', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 嵌入搜索
|
// 嵌入搜索
|
||||||
embeddingSearch: async (data: any) => {
|
embeddingSearch: async (data: any) => {
|
||||||
return await request.post({ url: '/aigc/embedding/search', data })
|
return await request.post({ url: '/chat/aigc/embedding/search', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 文档嵌入
|
// 文档嵌入
|
||||||
|
@ -20,7 +20,7 @@ export const EmbeddingApi = {
|
||||||
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void
|
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void
|
||||||
) => {
|
) => {
|
||||||
return await request.post({
|
return await request.post({
|
||||||
url: `/aigc/embedding/docs/${knowledgeId}`,
|
url: `/chat/aigc/embedding/docs/${knowledgeId}`,
|
||||||
data,
|
data,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
|
|
|
@ -10,31 +10,31 @@ export interface KnowledgeVO {
|
||||||
export const KnowledgeApi = {
|
export const KnowledgeApi = {
|
||||||
// 获得知识库列表
|
// 获得知识库列表
|
||||||
getKnowledgeList: async (params: any) => {
|
getKnowledgeList: async (params: any) => {
|
||||||
return await request.get({ url: '/aigc/knowledge/list', params })
|
return await request.get({ url: '/chat/aigc/knowledge/list', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得知识库分页
|
// 获得知识库分页
|
||||||
getKnowledgePage: async (params: any) => {
|
getKnowledgePage: async (params: any) => {
|
||||||
return await request.get({ url: '/aigc/knowledge/page', params })
|
return await request.get({ url: '/chat/aigc/knowledge/page', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得知识库详情
|
// 获得知识库详情
|
||||||
getKnowledge: async (id: string) => {
|
getKnowledge: async (id: string) => {
|
||||||
return await request.get({ url: `/aigc/knowledge/${id}` })
|
return await request.get({ url: `/chat/aigc/knowledge/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 创建知识库
|
// 创建知识库
|
||||||
createKnowledge: async (data: any) => {
|
createKnowledge: async (data: any) => {
|
||||||
return await request.post({ url: '/aigc/knowledge', data })
|
return await request.post({ url: '/chat/aigc/knowledge', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新知识库
|
// 更新知识库
|
||||||
updateKnowledge: async (data: any) => {
|
updateKnowledge: async (data: any) => {
|
||||||
return await request.put({ url: '/aigc/knowledge', data })
|
return await request.put({ url: '/chat/aigc/knowledge', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除知识库
|
// 删除知识库
|
||||||
deleteKnowledge: async (id: string) => {
|
deleteKnowledge: async (id: string) => {
|
||||||
return await request.delete({ url: `/aigc/knowledge/${id}` })
|
return await request.delete({ url: `/chat/aigc/knowledge/${id}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -26,31 +26,31 @@ export interface SliceVO {
|
||||||
export const SliceApi = {
|
export const SliceApi = {
|
||||||
// 获得切片分页
|
// 获得切片分页
|
||||||
getSlicePage: async (params: any) => {
|
getSlicePage: async (params: any) => {
|
||||||
return await request.get({ url: '/aigc/docs/slice/page', params })
|
return await request.get({ url: '/chat/aigc/docs/slice/page', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得切片列表
|
// 获得切片列表
|
||||||
getSliceList: async (params: any) => {
|
getSliceList: async (params: any) => {
|
||||||
return await request.get({ url: '/aigc/docs/slice/list', params })
|
return await request.get({ url: '/chat/aigc/docs/slice/list', params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得切片详情
|
// 获得切片详情
|
||||||
getSlice: async (id: string) => {
|
getSlice: async (id: string) => {
|
||||||
return await request.get({ url: `/aigc/docs/slice/${id}` })
|
return await request.get({ url: `/chat/aigc/docs/slice/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 创建切片
|
// 创建切片
|
||||||
createSlice: async (data: any) => {
|
createSlice: async (data: any) => {
|
||||||
return await request.post({ url: '/aigc/docs/slice', data })
|
return await request.post({ url: '/chat/aigc/docs/slice', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新切片
|
// 更新切片
|
||||||
updateSlice: async (data: any) => {
|
updateSlice: async (data: any) => {
|
||||||
return await request.put({ url: '/aigc/docs/slice', data })
|
return await request.put({ url: '/chat/aigc/docs/slice', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除切片
|
// 删除切片
|
||||||
deleteSlice: async (id: string) => {
|
deleteSlice: async (id: string) => {
|
||||||
return await request.delete({ url: `/aigc/docs/slice/${id}` })
|
return await request.delete({ url: `/chat/aigc/docs/slice/${id}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="visible" title="创建知识库" width="800px" :close-on-click-modal="!loading" :close-on-press-escape="!loading" @close="handleClose">
|
||||||
|
<Form :schema="formSchemas" v-if="visible" :model="formData" ref="formRef" @register="register"/>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FormSchema } from '@/types/form';
|
||||||
|
import {Form} from '@/components/Form'
|
||||||
|
import { useForm } from '@/hooks/web/useForm';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { KnowledgeApi } from '@/api/new-ai/knowledge';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const { register, methods, elFormRef } = useForm();
|
||||||
|
const visible = ref(false);
|
||||||
|
const loading = ref(false);
|
||||||
|
const formRef = ref();
|
||||||
|
const formData = ref({})
|
||||||
|
const emits = defineEmits(['reload'])
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await elFormRef.value?.validate()
|
||||||
|
const data = await methods.getFormData();
|
||||||
|
loading.value = true;
|
||||||
|
await KnowledgeApi.createKnowledge(data).finally(() => loading.value = false);
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
handleClose();
|
||||||
|
emits('reload');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建失败:', error);
|
||||||
|
ElMessage.error('创建失败');
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false;
|
||||||
|
nextTick(() => {
|
||||||
|
formRef.value?.clearForm()
|
||||||
|
formRef.value?.setSchema([])
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const formSchemas = computed(() => [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
component: 'Input',
|
||||||
|
label: '知识库名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入知识库名称',
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请输入知识库名称', trigger: ['blur'] }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'embedStoreId',
|
||||||
|
label: '向量数据库',
|
||||||
|
component: 'Input',
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请选择关联向量数据库', trigger: ['blur'] }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'embedModelId',
|
||||||
|
label: '向量模型',
|
||||||
|
component: 'Input',
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请选择关联向量模型', trigger: ['blur'] }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'des',
|
||||||
|
component: 'Input',
|
||||||
|
label: '知识库描述',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入知识库描述',
|
||||||
|
type: 'textarea',
|
||||||
|
autosize: {
|
||||||
|
minRows: 3,
|
||||||
|
maxRows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请输入知识库描述', trigger: ['blur'] }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const open = async () => {
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
// 对外暴露方法
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script></script>
|
|
@ -1,121 +1,117 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useTable} from "@/hooks/web/useTable";
|
import { useTable } from '@/hooks/web/useTable'
|
||||||
import {FormSchema} from "@/types/form";
|
import { FormSchema } from '@/types/form'
|
||||||
import {ref} from 'vue'
|
import { ref } from 'vue'
|
||||||
import {SliceApi} from '@/api/new-ai/slice'
|
import { SliceApi } from '@/api/new-ai/slice'
|
||||||
import {ElMessage} from 'element-plus'
|
import { ElMessage, ElTag } from 'element-plus'
|
||||||
|
import { DocsApi } from '@/api/new-ai/docs'
|
||||||
const props = defineProps({
|
import { Delete } from '@element-plus/icons-vue'
|
||||||
knowledgeData: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
const elTableRef = ref()
|
const docsList = ref([])
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
label: '文档名称',
|
label: '文档名称',
|
||||||
field: 'text',
|
field: 'name',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '字符数',
|
label: '字符数',
|
||||||
field: 'text',
|
field: 'wordNum',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '切片内容',
|
label: '切片内容',
|
||||||
field: 'text',
|
field: 'content',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '切片状态',
|
label: '切片状态',
|
||||||
field: 'text',
|
field: 'status',
|
||||||
key: 'text',
|
|
||||||
width: 200,
|
width: 200,
|
||||||
|
formatter(row) {
|
||||||
|
return h(
|
||||||
|
ElTag,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: row.status == true ? 'success' : 'info'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => (row.status == true ? '已训练' : '未训练')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '切片时间',
|
label: '切片时间',
|
||||||
field: 'text',
|
field: 'createTime'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '操作',
|
label: '操作',
|
||||||
field: 'text',
|
field: 'action',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const {tableObject, register, tableMethods} = useTable({
|
const { tableObject, register, tableMethods } = useTable({
|
||||||
getListApi: async (params) => {
|
getListApi: SliceApi.getSlicePage,
|
||||||
const res = await SliceApi.getSlicePage({
|
delListApi: SliceApi.deleteSlice,
|
||||||
...params,
|
defaultParams: {
|
||||||
knowledgeId: props.knowledgeData.id
|
knowledgeId: useRoute().params.id
|
||||||
})
|
|
||||||
return {
|
|
||||||
list: res.data.list || [],
|
|
||||||
total: res.data.total || 0
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
delListApi: async (ids) => {
|
|
||||||
await Promise.all(ids.map(id => SliceApi.deleteSlice(id)))
|
|
||||||
ElMessage.success('删除成功')
|
|
||||||
},
|
|
||||||
response: {
|
|
||||||
list: 'list',
|
|
||||||
total: 'total'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const schema = ref<FormSchema[]>([
|
const schema = computed(() => {
|
||||||
{
|
return [
|
||||||
label: '所属文档',
|
{
|
||||||
field: 'docId',
|
label: '所属文档',
|
||||||
component: 'Select',
|
field: 'docId',
|
||||||
componentProps: {
|
component: 'Select',
|
||||||
style: {
|
componentProps: {
|
||||||
width: '150px'
|
style: {
|
||||||
},
|
width: '150px'
|
||||||
placeholder: '请选择所属文档'
|
},
|
||||||
|
placeholder: '请选择所属文档',
|
||||||
|
options: docsList.value.map((item: any) => {
|
||||||
|
return {
|
||||||
|
label: item.name,
|
||||||
|
value: item.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
])
|
})
|
||||||
const pagination = computed(() => {
|
const pagination = computed(() => {
|
||||||
return {
|
return {
|
||||||
pageSize: tableObject.pageSize,
|
pageSize: tableObject.pageSize,
|
||||||
currentPage: tableObject.currentPage,
|
currentPage: tableObject.currentPage,
|
||||||
total: tableObject.total,
|
total: tableObject.total
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
register(tableRef,elTableRef,)
|
tableMethods.getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<Search :schema="schema" @search="tableMethods.getList()" />
|
||||||
<Table
|
<Table
|
||||||
|
class="mt-20px"
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
v-model:table="tableObject"
|
border
|
||||||
|
:data="tableObject.tableList"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
:label-width="100"
|
:label-width="100"
|
||||||
:search-table-height="true">
|
:pagination="pagination"
|
||||||
|
@register="register"
|
||||||
|
:search-table-height="true"
|
||||||
|
>
|
||||||
<template #action="{ row }">
|
<template #action="{ row }">
|
||||||
<el-button
|
<el-button text :icon="Delete" type="danger" @click="tableMethods.delList(row.id, false)" />
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="tableMethods.delList([row.id], false)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss"></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,84 +1,93 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useTable} from "@/hooks/web/useTable";
|
import { useTable } from '@/hooks/web/useTable'
|
||||||
import {FormSchema} from "@/types/form";
|
import { FormSchema } from '@/types/form'
|
||||||
import {ref} from 'vue'
|
import { ref } from 'vue'
|
||||||
import {DocsApi} from '@/api/new-ai/docs'
|
import { DocsApi } from '@/api/new-ai/docs'
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
const props = defineProps({
|
import { Refresh, Delete, Edit } from '@element-plus/icons-vue'
|
||||||
knowledgeData: {
|
import EditDoc from './edit-doc.vue'
|
||||||
type: Object,
|
const editDocRef = ref()
|
||||||
required: true
|
const route = useRoute()
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const tableRef = ref()
|
const tableRef = ref()
|
||||||
const elTableRef = ref()
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
label: '文档名称',
|
label: '文档名称',
|
||||||
field: 'text',
|
field: 'name',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '文档链接',
|
label: '文档链接',
|
||||||
field: 'text',
|
field: 'url',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '文档来源',
|
label: '文档来源',
|
||||||
field: 'text',
|
field: 'type',
|
||||||
key: 'text',
|
|
||||||
width: 200,
|
width: 200,
|
||||||
|
formatter(row) {
|
||||||
|
return h(
|
||||||
|
ElTag,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: row.type == 'UPLOAD' ? 'success' : 'info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => (row.type == 'UPLOAD' ? '上传' : '录入'),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '切片数量',
|
label: '切片数量',
|
||||||
field: 'text',
|
field: 'sliceNum',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '切片状态',
|
label: '切片状态',
|
||||||
field: 'text',
|
field: 'status',
|
||||||
key: 'text',
|
|
||||||
width: 200,
|
width: 200,
|
||||||
|
formatter(row) {
|
||||||
|
return h(
|
||||||
|
ElTag,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: row.sliceStatus == true ? 'success' : 'info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => (row.sliceStatus == true ? '已训练' : '未训练'),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '文件大小',
|
label: '文件大小',
|
||||||
field: 'text',
|
field: 'size',
|
||||||
|
formatter(rowData) {
|
||||||
|
return (Number(rowData.size) / 1000000).toFixed(2) + ' MB';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '操作',
|
label: '操作',
|
||||||
field: 'text',
|
field: 'action',
|
||||||
key: 'text',
|
width: 200
|
||||||
width: 200,
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const {tableObject, register, tableMethods} = useTable({
|
const { tableObject, register, tableMethods } = useTable({
|
||||||
getListApi: async (params) => {
|
getListApi: DocsApi.getDocsPage,
|
||||||
const res = await DocsApi.getDocsPage({
|
delListApi: DocsApi.deleteDocs,
|
||||||
...params,
|
defaultParams: {
|
||||||
knowledgeId: props.knowledgeData.id
|
knowledgeId: route.params.id
|
||||||
})
|
}
|
||||||
return {
|
})
|
||||||
list: res.data.list || [],
|
const pagination = computed(() => {
|
||||||
total: res.data.total || 0
|
return {
|
||||||
}
|
total: tableObject.total,
|
||||||
},
|
pageSize: tableObject.pageSize,
|
||||||
delListApi: async (ids) => {
|
currentPage: tableObject.currentPage
|
||||||
await Promise.all(ids.map(id => DocsApi.deleteDocs(id)))
|
|
||||||
ElMessage.success('删除成功')
|
|
||||||
},
|
|
||||||
response: {
|
|
||||||
list: 'list',
|
|
||||||
total: 'total'
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const schema = ref<FormSchema[]>([
|
const schema = ref<FormSchema[]>([
|
||||||
{
|
{
|
||||||
label: '文档名称',
|
label: '文档名称',
|
||||||
|
@ -106,37 +115,37 @@ const handleReEmbed = async (row) => {
|
||||||
console.error('Failed to re-embed:', error)
|
console.error('Failed to re-embed:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const openEdit = (row) => {
|
||||||
// 注册表格
|
editDocRef.value.show(route.params.id, row.id) // docId可选,用于编辑模式
|
||||||
register(tableRef as any ,elTableRef as any)
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
tableMethods.getList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<Search :schema="schema" @search="tableMethods.getList()" />
|
||||||
<Table
|
<Table
|
||||||
|
class="mt-20px"
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
v-model:table="tableObject"
|
border
|
||||||
|
:data="tableObject.tableList"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
:label-width="100"
|
:label-width="100"
|
||||||
:search-table-height="true">
|
:pagination="pagination"
|
||||||
|
:search-table-height="true"
|
||||||
|
@register="register"
|
||||||
|
>
|
||||||
<template #action="{ row }">
|
<template #action="{ row }">
|
||||||
<el-button
|
<el-button text type="primary" :icon="Refresh" @click="handleReEmbed(row)" />
|
||||||
link
|
<el-button text type="primary" :icon="Edit" @click="openEdit(row)" />
|
||||||
type="primary"
|
<el-button text :icon="Delete" type="danger" @click="tableMethods.delList(row.id, false)" />
|
||||||
@click="handleReEmbed(row)">
|
|
||||||
重新向量化
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
link
|
|
||||||
type="danger"
|
|
||||||
@click="tableMethods.delList([row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</Table>
|
</Table>
|
||||||
|
<edit-doc ref="editDocRef" @reload="tableMethods.getList()" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,48 +1,79 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { Management } from '@element-plus/icons-vue'
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Document } from '@element-plus/icons-vue'
|
||||||
|
import { EmbeddingApi } from '@/api/new-ai/embedding'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
const { embeddingSearch, embeddingText } = EmbeddingApi
|
||||||
|
const route = useRoute()
|
||||||
|
const content = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref<any>([])
|
||||||
|
|
||||||
|
async function onSearch(flag = false) {
|
||||||
|
if (content.value === '' && flag) {
|
||||||
|
list.value = []
|
||||||
|
ElMessage.warning('请先输入搜索内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
list.value = await embeddingSearch({
|
||||||
|
content: content.value,
|
||||||
|
knowledgeId: route.params.id
|
||||||
|
})
|
||||||
|
console.log(list.value)
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
onSearch()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap wrap">
|
<div class="h-full w-full">
|
||||||
<el-card class="mb-20px ">
|
<div class="flex gap-10px my-10px flex-wrap">
|
||||||
<template #header>
|
<el-card class="w-[calc(25%-10px)]">
|
||||||
<el-button class="w-full">向量搜索</el-button>
|
<el-button class="w-full mb-10px" :loading="loading" type="primary" @click="onSearch(true)">向量搜索</el-button>
|
||||||
|
<el-input
|
||||||
|
v-model="content"
|
||||||
|
placeholder="请输入关键词查询向量文本"
|
||||||
|
:rows="10"
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
<template v-if="list.length > 0">
|
||||||
|
<el-card
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.index"
|
||||||
|
class="rounded-lg cursor-pointer w-[calc(25%-10px)] bg-[var(--el-bg-color-pages)]"
|
||||||
|
shadow="hover"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<el-icon>
|
||||||
|
<Document />
|
||||||
|
</el-icon>
|
||||||
|
<el-text class="truncate">{{ item.docsName }}</el-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-scrollbar height="200px" class="text-14px">
|
||||||
|
{{ item.text }}
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
<el-input rows="10" type="textarea" />
|
<el-empty v-if="list.length === 0" class="my-4 flex-1" />
|
||||||
</el-card>
|
</div>
|
||||||
<el-card v-for="index in 5" :key="index" class="mb-20px ">
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<el-icon class="mr-10px ">
|
|
||||||
<Management class="text-18px color-[var(--el-color-primary)]"/>
|
|
||||||
</el-icon>
|
|
||||||
<span class="">首席信息</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="inner"></div>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped>
|
||||||
.wrap {
|
//a{
|
||||||
&>div {
|
// color: var(--el-bg-color-page);
|
||||||
width: calc((100% - 40px) / 3);
|
|
||||||
margin-right: 20px;
|
|
||||||
&:nth-child(3n) {
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.inner {
|
|
||||||
height: 220px;
|
|
||||||
}
|
|
||||||
.el-card:not(:first-child) {
|
|
||||||
background-color: mix(grey, #fff, 10%);
|
|
||||||
}
|
|
||||||
//div {
|
|
||||||
// display: grid;
|
|
||||||
// grid-template-columns: repeat(3, 1fr);
|
|
||||||
// gap: 20px;
|
|
||||||
//}
|
//}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,11 +1,97 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {UploadFile} from '@/components/UploadFile'
|
import { ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Upload } from '@element-plus/icons-vue'
|
||||||
|
import { EmbeddingApi } from '@/api/new-ai/embedding'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const fileList = ref<any[]>([])
|
||||||
|
const uploadRef = ref()
|
||||||
|
|
||||||
|
const handleUpload = async (file: File) => {
|
||||||
|
const knowledgeId = route.params.id as string
|
||||||
|
try {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
await EmbeddingApi.embeddingDocs(knowledgeId, formData, (event: any) => {
|
||||||
|
if (event.lengthComputable) {
|
||||||
|
const progress = Math.round((event.loaded * 100) / event.total)
|
||||||
|
console.log('Upload progress:', progress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ElMessage.success('上传成功,文档解析中...')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Upload failed:', error)
|
||||||
|
ElMessage.error('上传失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExceed = () => {
|
||||||
|
ElMessage.warning('每次只能上传一个文件')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UploadFile drag />
|
<div class="upload-container">
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
class="upload-dragger"
|
||||||
|
drag
|
||||||
|
:auto-upload="false"
|
||||||
|
:limit="1"
|
||||||
|
accept=".doc,.docx,.pdf,.txt,.md"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:on-change="(file) => handleUpload(file.raw as File)"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><Upload /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
拖拽文件到此处或 <em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">
|
||||||
|
支持的文件格式:.txt、.md、.docx、.doc、.pdf
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
|
.upload-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-dragger {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-upload-dragger) {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon--upload {
|
||||||
|
font-size: 48px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__text {
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload__text em {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
<!--
|
||||||
|
- Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||||
|
-
|
||||||
|
- Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||||
|
- you may not use this file except in compliance with the License.
|
||||||
|
- You may obtain a copy of the License at
|
||||||
|
-
|
||||||
|
- https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
-
|
||||||
|
- Unless required by applicable law or agreed to in writing, software
|
||||||
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
- See the License for the specific language governing permissions and
|
||||||
|
- limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { nextTick, ref } from 'vue'
|
||||||
|
import { DocsApi } from '@/api/new-ai/docs'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { FormSchema } from '@/types/form'
|
||||||
|
import { isNullOrWhitespace } from '@/utils/is'
|
||||||
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
const emit = defineEmits(['reload'])
|
||||||
|
const showModal = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const { methods, elFormRef, register } = useForm()
|
||||||
|
const formRef = ref()
|
||||||
|
const editFlag = ref(false)
|
||||||
|
const formSchema = ref<FormSchema[]>([
|
||||||
|
{
|
||||||
|
label: '文档名称',
|
||||||
|
field: 'name',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入文档名称'
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请输入文档名称', trigger: ['blur'] }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '文档内容',
|
||||||
|
field: 'content',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入文档内容',
|
||||||
|
type: 'textarea',
|
||||||
|
autosize: {
|
||||||
|
minRows: 10,
|
||||||
|
maxRows: 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请输入文档内容', trigger: ['blur'] }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
async function show(knowledgeId: string, id?: string) {
|
||||||
|
showModal.value = true
|
||||||
|
await nextTick()
|
||||||
|
if (id) {
|
||||||
|
editFlag.value = true
|
||||||
|
const res = await DocsApi.getDocsById(id)
|
||||||
|
methods.setValues(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
await elFormRef.value?.validate()
|
||||||
|
const formData =await methods.getFormData() as Record<string, any>
|
||||||
|
console.log(formData)
|
||||||
|
formData.knowledgeId = route.params.id as string
|
||||||
|
if (!formData.name) {
|
||||||
|
ElMessage.error('请完善表单')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isNullOrWhitespace(formData.id)) {
|
||||||
|
await DocsApi.addDocs(formData)
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
} else {
|
||||||
|
await DocsApi.updateDocs(formData)
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
}
|
||||||
|
showModal.value = false
|
||||||
|
emit('reload')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ show })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="showModal"
|
||||||
|
:title="editFlag ? '编辑文档' : '新增文档'"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="!loading"
|
||||||
|
:close-on-press-escape="!loading"
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<Form :schema="formSchema" ref="formRef" @register="register" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showModal = false" :loading="loading">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -8,11 +8,24 @@ import DataDocument from "@/views/knowledge/dataset-form/components/data-documen
|
||||||
import DataEmbedding from "@/views/knowledge/dataset-form/components/data-embedding.vue";
|
import DataEmbedding from "@/views/knowledge/dataset-form/components/data-embedding.vue";
|
||||||
import {KnowledgeApi} from '@/api/new-ai/knowledge'
|
import {KnowledgeApi} from '@/api/new-ai/knowledge'
|
||||||
import {ElMessage} from 'element-plus'
|
import {ElMessage} from 'element-plus'
|
||||||
|
interface Knowledge {
|
||||||
|
id: string
|
||||||
|
embedStore?: {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
embedModel?: {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
}
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const active = ref('1')
|
const active = ref('1')
|
||||||
const knowledgeData = ref({})
|
const loading = ref(false)
|
||||||
|
const knowledgeData = ref<Knowledge>({
|
||||||
|
id: '',
|
||||||
|
})
|
||||||
|
|
||||||
const tabs = ref([
|
const tabs = ref([
|
||||||
{
|
{
|
||||||
|
@ -42,11 +55,13 @@ const tabs = ref([
|
||||||
])
|
])
|
||||||
|
|
||||||
const loadKnowledge = async () => {
|
const loadKnowledge = async () => {
|
||||||
const id = route.query.id as string
|
const id = route.params.id as string
|
||||||
|
console.log(id)
|
||||||
if (id) {
|
if (id) {
|
||||||
try {
|
try {
|
||||||
const res = await KnowledgeApi.getKnowledge(id)
|
loading.value = true;
|
||||||
knowledgeData.value = res.data
|
const res = await KnowledgeApi.getKnowledge(id).finally(() => loading.value = false)
|
||||||
|
knowledgeData.value = res
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load knowledge:', error)
|
console.error('Failed to load knowledge:', error)
|
||||||
ElMessage.error('加载知识库失败')
|
ElMessage.error('加载知识库失败')
|
||||||
|
@ -60,14 +75,14 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="children flex">
|
<div class="children flex" v-loading="loading">
|
||||||
<div class="flex flex-col mr-30px w-350px">
|
<div class="flex flex-col mr-30px w-350px bg-white pt-10px">
|
||||||
<el-button
|
<el-button
|
||||||
class="w-full mb-10px" plain type="primary"
|
class="w-full mb-10px" plain type="primary"
|
||||||
@click="router.push({ path: '/ai/console/knowledge' })">知识库列表
|
@click="router.push({ path: '/ai/console/knowledge' })">知识库列表
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-scrollbar class="flex-1">
|
<el-scrollbar class="flex-1 ">
|
||||||
<div class="py-20px flex items-center border-b-solid border-gray border-1 mb-10px">
|
<div class="py-20px flex items-center border-b-solid border-gray border-1 mb-10px px-10px">
|
||||||
<div
|
<div
|
||||||
class="icon-bg bg-[var(--el-color-primary-light-7)] w-40px lh-40px text-center mr-10px">
|
class="icon-bg bg-[var(--el-color-primary-light-7)] w-40px lh-40px text-center mr-10px">
|
||||||
<el-icon class="font-bold">
|
<el-icon class="font-bold">
|
||||||
|
@ -76,20 +91,22 @@ onMounted(() => {
|
||||||
</div>
|
</div>
|
||||||
<h3>本地测试</h3>
|
<h3>本地测试</h3>
|
||||||
</div>
|
</div>
|
||||||
<el-form label-position="top">
|
<el-form label-position="top" class=" px-10px">
|
||||||
<el-form-item label="知识库ID">
|
<el-form-item label="知识库ID">
|
||||||
<el-input />
|
<el-input v-model="knowledgeData.id" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="关联向量数据库">
|
<el-form-item label="关联向量数据库">
|
||||||
<el-input />
|
<el-input v-model="knowledgeData.embedStore.name" readonly v-if="knowledgeData.embedStore"/>
|
||||||
|
<el-text type="danger" v-else>未关联</el-text>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="关联向量化模型">
|
<el-form-item label="关联向量化模型">
|
||||||
<el-input />
|
<el-input v-model="knowledgeData.embedModel.name" readonly v-if="knowledgeData.embedModel"/>
|
||||||
|
<el-text type="danger" v-else>未关联</el-text>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1 bg-white ">
|
||||||
<el-tabs v-model="active" class="h-full">
|
<el-tabs v-model="active" class="h-full">
|
||||||
<el-tab-pane class="h-full" v-for="(item, index) in tabs" :key="index" :label="item.label" :name="item.name">
|
<el-tab-pane class="h-full" v-for="(item, index) in tabs" :key="index" :label="item.label" :name="item.name">
|
||||||
<template #label>
|
<template #label>
|
||||||
|
@ -100,7 +117,7 @@ onMounted(() => {
|
||||||
<span>{{item.label}}</span>
|
<span>{{item.label}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-scrollbar class="inner overflow-y-auto">
|
<el-scrollbar class="inner overflow-y-auto p-10px" v-if="active === item.name">
|
||||||
<component :is="item.component" />
|
<component :is="item.component" />
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="knowledge-base-container">
|
<div class="knowledge-base-container">
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<el-card class="create-card" shadow="hover" @click="toDatasetForm">
|
<el-card class="create-card" shadow="hover" @click="handleCreate">
|
||||||
<div class="create-content">
|
<div class="create-content">
|
||||||
<el-icon class="create-icon"><Plus /></el-icon>
|
<el-icon class="create-icon"><Plus /></el-icon>
|
||||||
<span class="create-text">创建知识库</span>
|
<span class="create-text">创建知识库</span>
|
||||||
|
@ -11,7 +11,8 @@
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card v-for="item in tableData" :key="item.id" class="document-card" shadow="hover"
|
<el-card
|
||||||
|
v-for="item in tableData" :key="item.id" class="document-card" shadow="hover"
|
||||||
@click="toDataset(item.id)">
|
@click="toDataset(item.id)">
|
||||||
<div class="document-header">
|
<div class="document-header">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
|
@ -20,12 +21,12 @@
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="document-info">
|
<div class="document-info">
|
||||||
<el-tag size="small">{{ item.documentCount || 0 }} 文档</el-tag>
|
<el-tag size="small">{{ item.docsNum || 0 }} 文档</el-tag>
|
||||||
<el-tag size="small" type="info">{{ item.characterCount || 0 }} 千字符</el-tag>
|
<el-tag size="small" type="info">{{ (Number(item.totalSize) / 1000000).toFixed(2) }} MB</el-tag>
|
||||||
<el-tag size="small" type="warning">{{ item.appCount || 0 }} 关联应用</el-tag>
|
<el-tag size="small" type="warning" v-if="item.embedStore"> {{ item.embedStore.name }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<p class="document-description">
|
<p class="document-description">
|
||||||
{{ item.description || '暂无描述' }}
|
{{ item.des || '暂无描述' }}
|
||||||
</p>
|
</p>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<create ref="createRef" @reload="loadData"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -52,21 +54,21 @@ import {ref, onMounted} from 'vue'
|
||||||
import {Plus, Folder} from '@element-plus/icons-vue'
|
import {Plus, Folder} from '@element-plus/icons-vue'
|
||||||
import {useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import {KnowledgeApi} from '@/api/new-ai/knowledge'
|
import {KnowledgeApi} from '@/api/new-ai/knowledge'
|
||||||
|
import Create from './components/create.vue'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const tableData = ref([])
|
const tableData = ref([])
|
||||||
|
const createRef = ref()
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await KnowledgeApi.getKnowledgePage({
|
const res = await KnowledgeApi.getKnowledgePage({
|
||||||
pageNo: currentPage.value,
|
pageNo: currentPage.value,
|
||||||
pageSize: pageSize.value
|
pageSize: pageSize.value
|
||||||
})
|
})
|
||||||
tableData.value = res.data.list || []
|
tableData.value = res.list || []
|
||||||
total.value = res.data.total || 0
|
total.value = res.total || 0
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load knowledge list:', error)
|
console.error('Failed to load knowledge list:', error)
|
||||||
}
|
}
|
||||||
|
@ -86,10 +88,10 @@ const toDataset = (id: string) => {
|
||||||
router.push(`/ai/console/knowledge/${id}`)
|
router.push(`/ai/console/knowledge/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const toDatasetForm = () => {
|
|
||||||
router.push('/ai/console/knowledge/1')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
createRef.value.open()
|
||||||
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue