Merge branch 'main' of github.com:maxkb-dev/maxkb
This commit is contained in:
commit
33a6347227
@ -103,22 +103,24 @@ const putDocument: (dataset_id: string, document_id: string, data: any) => Promi
|
|||||||
* 删除文档
|
* 删除文档
|
||||||
* @param 参数 dataset_id, document_id,
|
* @param 参数 dataset_id, document_id,
|
||||||
*/
|
*/
|
||||||
const delDocument: (dataset_id: string, document_id: string) => Promise<Result<boolean>> = (
|
const delDocument: (
|
||||||
dataset_id,
|
dataset_id: string,
|
||||||
document_id
|
document_id: string,
|
||||||
) => {
|
loading?: Ref<boolean>
|
||||||
return del(`${prefix}/${dataset_id}/document/${document_id}`)
|
) => Promise<Result<boolean>> = (dataset_id, document_id, loading) => {
|
||||||
|
return del(`${prefix}/${dataset_id}/document/${document_id}`, loading)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 批量删除文档
|
||||||
|
* @param 参数 dataset_id,
|
||||||
|
*/
|
||||||
|
const delMulDocument: (
|
||||||
|
dataset_id: string,
|
||||||
|
data: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
|
||||||
|
return del(`${prefix}/${dataset_id}/document/_bach`, undefined, { id_list: data }, loading)
|
||||||
}
|
}
|
||||||
// /**
|
|
||||||
// * 批量删除文档
|
|
||||||
// * @param 参数 dataset_id, document_id,
|
|
||||||
// */
|
|
||||||
// const delDocument: (dataset_id: string, document_id: string) => Promise<Result<boolean>> = (
|
|
||||||
// dataset_id,
|
|
||||||
// document_id
|
|
||||||
// ) => {
|
|
||||||
// return del(`${prefix}/${dataset_id}/document/${document_id}`)
|
|
||||||
// }
|
|
||||||
/**
|
/**
|
||||||
* 文档详情
|
* 文档详情
|
||||||
* @param 参数 dataset_id
|
* @param 参数 dataset_id
|
||||||
@ -152,6 +154,37 @@ const putDocumentRefresh: (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量同步文档
|
||||||
|
* @param 参数 dataset_id,
|
||||||
|
*/
|
||||||
|
const delMulSyncDocument: (
|
||||||
|
dataset_id: string,
|
||||||
|
data: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
|
||||||
|
return put(`${prefix}/${dataset_id}/document/_bach`, { id_list: data }, undefined, loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Web站点文档
|
||||||
|
* @param 参数
|
||||||
|
* {
|
||||||
|
"source_url_list": [
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"selector": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const postWebDocument: (
|
||||||
|
dataset_id: string,
|
||||||
|
data: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<any>> = (dataset_id, data, loading) => {
|
||||||
|
return post(`${prefix}/${dataset_id}/document/web`, data, undefined, loading)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
postSplitDocument,
|
postSplitDocument,
|
||||||
getDocument,
|
getDocument,
|
||||||
@ -159,7 +192,10 @@ export default {
|
|||||||
postDocument,
|
postDocument,
|
||||||
putDocument,
|
putDocument,
|
||||||
delDocument,
|
delDocument,
|
||||||
|
delMulDocument,
|
||||||
getDocumentDetail,
|
getDocumentDetail,
|
||||||
listSplitPattern,
|
listSplitPattern,
|
||||||
putDocumentRefresh
|
putDocumentRefresh,
|
||||||
|
delMulSyncDocument,
|
||||||
|
postWebDocument
|
||||||
}
|
}
|
||||||
|
|||||||
4
ui/src/assets/icon_web.svg.svg
Normal file
4
ui/src/assets/icon_web.svg.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.5 2H21.5C22.0523 2 22.5 2.44772 22.5 3V17C22.5 17.5523 22.0523 18 21.5 18H2.5C1.94772 18 1.5 17.5523 1.5 17V3C1.5 2.44772 1.94772 2 2.5 2ZM3.5 14V16H20.5V14H3.5Z" fill="white"/>
|
||||||
|
<path d="M6.5 20H17.5C17.7761 20 18 20.2239 18 20.5V21.5C18 21.7761 17.7761 22 17.5 22H6.5C6.22386 22 6 21.7761 6 21.5V20.5C6 20.2239 6.22386 20 6.5 20Z" fill="#D8D8D8"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 464 B |
@ -17,7 +17,6 @@
|
|||||||
class="paragraph-source-card cursor mb-8"
|
class="paragraph-source-card cursor mb-8"
|
||||||
:class="item.is_active ? '' : 'disabled'"
|
:class="item.is_active ? '' : 'disabled'"
|
||||||
:showIcon="false"
|
:showIcon="false"
|
||||||
@click="editParagraph(item)"
|
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AppAvatar :name="index + 1 + ''" class="mr-12 avatar-light" :size="22" />
|
<AppAvatar :name="index + 1 + ''" class="mr-12 avatar-light" :size="22" />
|
||||||
@ -45,13 +44,11 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<ParagraphDialog ref="ParagraphDialogRef" title="分段详情" @refresh="refresh" />
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, nextTick } from 'vue'
|
import { ref, watch, nextTick } from 'vue'
|
||||||
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
const ParagraphDialogRef = ref()
|
const ParagraphDialogRef = ref()
|
||||||
@ -64,21 +61,14 @@ watch(dialogVisible, (bool) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const open = (data: any) => {
|
const open = (data: any, id?: string) => {
|
||||||
detail.value = data
|
detail.value = cloneDeep(data)
|
||||||
|
detail.value.paragraph_list = id
|
||||||
|
? detail.value.paragraph_list.filter((v) => v.dataset_id === id)
|
||||||
|
: detail.value.paragraph_list
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function editParagraph(row: any) {
|
|
||||||
ParagraphDialogRef.value.open(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
function refresh(data: any) {
|
|
||||||
if (data) {
|
|
||||||
const index = detail.value.paragraph_list.findIndex((v) => v.id === data.id)
|
|
||||||
detail.value.paragraph_list.splice(index, 1, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,29 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="同步知识库"
|
title="导入文档"
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
width="600px"
|
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:close-on-press-escape="false"
|
:close-on-press-escape="false"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<p class="mb-8">同步方式</p>
|
<el-form
|
||||||
<el-radio-group v-model="method" class="card__radio">
|
label-position="top"
|
||||||
<el-card shadow="never" class="mb-16" :class="method === 'replace' ? 'active' : ''">
|
ref="webFormRef"
|
||||||
<el-radio label="replace" size="large">
|
:rules="rules"
|
||||||
<p class="mb-4">替换同步</p>
|
:model="form"
|
||||||
<el-text type="info">重新获取 Web 站点文档,覆盖替换本地知识库中的文档</el-text>
|
require-asterisk-position="right"
|
||||||
</el-radio>
|
>
|
||||||
</el-card>
|
<el-form-item label="文档地址" prop="source_url" v-if="isImport">
|
||||||
|
<el-input
|
||||||
<el-card shadow="never" class="mb-16" :class="method === 'complete' ? 'active' : ''">
|
v-model="form.source_url"
|
||||||
<el-radio label="complete" size="large">
|
placeholder="请输入文档地址,一行一个,地址不正确文档会导入失败。"
|
||||||
<p class="mb-4">整体同步</p>
|
:rows="10"
|
||||||
<el-text type="info">先删除本地知识库所有文档,重新获取 Web 站点文档</el-text>
|
type="textarea"
|
||||||
</el-radio>
|
/>
|
||||||
</el-card>
|
</el-form-item>
|
||||||
</el-radio-group>
|
<el-form-item v-else label="文档地址" prop="source_url">
|
||||||
<p class="danger">注意:所有同步都会删除已有数据重新获取新数据,请谨慎操作。</p>
|
<el-input v-model="form.source_url" placeholder="请输入文档地址" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择器">
|
||||||
|
<el-input
|
||||||
|
v-model="form.selector"
|
||||||
|
placeholder="默认为 body,可输入 .classname/#idname/tagname"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
|
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
|
||||||
@ -33,56 +40,61 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import documentApi from '@/api/document'
|
||||||
import { MsgSuccess } from '@/utils/message'
|
import { MsgSuccess } from '@/utils/message'
|
||||||
|
|
||||||
import useStore from '@/stores'
|
const route = useRoute()
|
||||||
const { dataset } = useStore()
|
const {
|
||||||
|
params: { id }
|
||||||
|
} = route as any
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
const emit = defineEmits(['refresh'])
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
const method = ref('replace')
|
const isImport = ref<boolean>(false)
|
||||||
const datasetId = ref('')
|
const form = ref<any>({
|
||||||
|
source_url: '',
|
||||||
|
selector: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
source_url: [{ required: true, message: '请输入 Web 根地址', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
const dialogVisible = ref<boolean>(false)
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
|
||||||
watch(dialogVisible, (bool) => {
|
watch(dialogVisible, (bool) => {
|
||||||
if (!bool) {
|
if (!bool) {
|
||||||
method.value = 'replace'
|
form.value = {
|
||||||
|
source_url: '',
|
||||||
|
selector: ''
|
||||||
|
}
|
||||||
|
isImport.value = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const open = (id: string) => {
|
const open = (row: any) => {
|
||||||
datasetId.value = id
|
if (row) {
|
||||||
|
isImport.value = false
|
||||||
|
} else {
|
||||||
|
isImport.value = true
|
||||||
|
}
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
dataset.asyncSyncDateset(datasetId.value, method.value, loading).then((res: any) => {
|
const obj = {
|
||||||
// MsgSuccess('删除成功')
|
source_url_list: form.value.source_url.split('\n'),
|
||||||
emit('refresh', res.data)
|
selector: form.value.selector
|
||||||
|
}
|
||||||
|
documentApi.postWebDocument(id, obj, loading).then((res: any) => {
|
||||||
|
MsgSuccess('导入成功')
|
||||||
|
emit('refresh')
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
.select-provider {
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(100, 106, 115, 1);
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.active-breadcrumb {
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(31, 35, 41, 1);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -10,9 +10,18 @@
|
|||||||
@click="router.push({ path: '/dataset/upload', query: { id: id } })"
|
@click="router.push({ path: '/dataset/upload', query: { id: id } })"
|
||||||
>上传文档</el-button
|
>上传文档</el-button
|
||||||
>
|
>
|
||||||
<el-button v-if="datasetDetail.type === '1'" type="primary">导入文档</el-button>
|
<el-button v-if="datasetDetail.type === '1'" type="primary" @click="importDoc"
|
||||||
<!-- <el-button v-if="datasetDetail.type === '1'">批量同步</el-button> -->
|
>导入文档</el-button
|
||||||
<el-button :disabled="multipleSelection.length === 0">批量删除</el-button>
|
>
|
||||||
|
<el-button
|
||||||
|
@click="syncMulDocument"
|
||||||
|
:disabled="multipleSelection.length === 0"
|
||||||
|
v-if="datasetDetail.type === '1'"
|
||||||
|
>批量同步</el-button
|
||||||
|
>
|
||||||
|
<el-button @click="deleteMulDocument" :disabled="multipleSelection.length === 0"
|
||||||
|
>批量删除</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-input
|
<el-input
|
||||||
@ -119,12 +128,14 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span @click.stop>
|
<span @click.stop>
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link cursor">
|
<el-button text>
|
||||||
<el-icon><MoreFilled /></el-icon>
|
<el-icon><MoreFilled /></el-icon>
|
||||||
</span>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item icon="Setting">设置</el-dropdown-item>
|
<el-dropdown-item icon="Setting" @click="settingDoc(row)"
|
||||||
|
>设置</el-dropdown-item
|
||||||
|
>
|
||||||
<el-dropdown-item icon="Delete" @click.stop="deleteDocument(row)"
|
<el-dropdown-item icon="Delete" @click.stop="deleteDocument(row)"
|
||||||
>删除</el-dropdown-item
|
>删除</el-dropdown-item
|
||||||
>
|
>
|
||||||
@ -137,6 +148,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</app-table>
|
</app-table>
|
||||||
</div>
|
</div>
|
||||||
|
<ImportDocumentDialog ref="ImportDocumentDialogRef" :title="title" @refresh="refresh" />
|
||||||
</div>
|
</div>
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
</template>
|
</template>
|
||||||
@ -145,6 +157,7 @@ import { ref, onMounted, reactive, onBeforeUnmount } from 'vue'
|
|||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ElTable } from 'element-plus'
|
import { ElTable } from 'element-plus'
|
||||||
import documentApi from '@/api/document'
|
import documentApi from '@/api/document'
|
||||||
|
import ImportDocumentDialog from './component/ImportDocumentDialog.vue'
|
||||||
import { numberFormat } from '@/utils/utils'
|
import { numberFormat } from '@/utils/utils'
|
||||||
import { datetimeFormat } from '@/utils/time'
|
import { datetimeFormat } from '@/utils/time'
|
||||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||||
@ -169,8 +182,19 @@ const paginationConfig = reactive({
|
|||||||
total: 0
|
total: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ImportDocumentDialogRef = ref()
|
||||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||||
const multipleSelection = ref<any[]>([])
|
const multipleSelection = ref<any[]>([])
|
||||||
|
const title = ref('')
|
||||||
|
|
||||||
|
function importDoc() {
|
||||||
|
title.value = '导入文档'
|
||||||
|
ImportDocumentDialogRef.value.open()
|
||||||
|
}
|
||||||
|
function settingDoc(row: any) {
|
||||||
|
title.value = '设置'
|
||||||
|
ImportDocumentDialogRef.value.open(row)
|
||||||
|
}
|
||||||
|
|
||||||
const handleSelectionChange = (val: any[]) => {
|
const handleSelectionChange = (val: any[]) => {
|
||||||
multipleSelection.value = val
|
multipleSelection.value = val
|
||||||
@ -199,9 +223,22 @@ const closeInterval = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function refreshDocument(row: any) {
|
function refreshDocument(row: any) {
|
||||||
documentApi.putDocumentRefresh(row.dataset_id, row.id).then((res) => {
|
if (row.type === '1') {
|
||||||
getList()
|
MsgConfirm(`确认同步文档?`, `同步将删除已有数据重新获取新数据,请谨慎操作。`, {
|
||||||
})
|
confirmButtonText: '同步',
|
||||||
|
confirmButtonClass: 'danger'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
documentApi.putDocumentRefresh(row.dataset_id, row.id).then((res) => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
} else {
|
||||||
|
documentApi.putDocumentRefresh(row.dataset_id, row.id).then((res) => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rowClickHandle(row: any) {
|
function rowClickHandle(row: any) {
|
||||||
@ -225,6 +262,32 @@ function creatQuickHandle(val: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncMulDocument() {
|
||||||
|
const arr: string[] = []
|
||||||
|
multipleSelection.value.map((v) => {
|
||||||
|
if (v) {
|
||||||
|
arr.push(v.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
documentApi.delMulSyncDocument(id, arr, loading).then(() => {
|
||||||
|
MsgSuccess('批量同步成功')
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteMulDocument() {
|
||||||
|
const arr: string[] = []
|
||||||
|
multipleSelection.value.map((v) => {
|
||||||
|
if (v) {
|
||||||
|
arr.push(v.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
documentApi.delMulDocument(id, arr, loading).then(() => {
|
||||||
|
MsgSuccess('批量删除成功')
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function deleteDocument(row: any) {
|
function deleteDocument(row: any) {
|
||||||
MsgConfirm(
|
MsgConfirm(
|
||||||
`是否删除文档:${row.name} ?`,
|
`是否删除文档:${row.name} ?`,
|
||||||
@ -235,16 +298,10 @@ function deleteDocument(row: any) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = true
|
documentApi.delDocument(id, row.id, loading).then(() => {
|
||||||
documentApi
|
MsgSuccess('删除成功')
|
||||||
.delDocument(id, row.id)
|
getList()
|
||||||
.then(() => {
|
})
|
||||||
MsgSuccess('删除成功')
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
loading.value = false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
@ -314,6 +371,11 @@ function getDetail() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
paginationConfig.current_page = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDetail()
|
getDetail()
|
||||||
getList()
|
getList()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user