feat: paragraph

This commit is contained in:
wangdan-fit2cloud 2025-06-19 20:32:02 +08:00
parent ba078a9aab
commit 72d9833038
12 changed files with 213 additions and 156 deletions

View File

@ -14,13 +14,29 @@ Object.defineProperty(prefix, 'value', {
})
/**
*
*
* @param knowledge_id,
* param {
" name": "string",
}
*/
const getDocumentList: (knowledge_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
knowledge_id,
loading,
) => {
return get(`${prefix.value}/${knowledge_id}/document`, undefined, loading)
}
/**
*
* @param knowledge_id,
* param {
"name": "string",
folder_id: "string",
}
*/
const getDocumentPage: (
knowledge_id: string,
page: pageRequest,
@ -549,15 +565,9 @@ const importLarkDocument: (
return post(`${prefix.value}/lark/${knowledge_id}/import`, data, null, loading)
}
// todo
const getAllDocument: (knowledge_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
knowledge_id,
loading,
) => {
return get(`${prefix.value}/${knowledge_id}/document`, undefined, loading)
}
export default {
getDocumentList,
getDocumentPage,
getDocumentDetail,
putDocument,

View File

@ -239,6 +239,11 @@ const putBatchGenerateRelated: (
/**
*
* @param knowledge_id,target_knowledge_id,
* {
"id_list": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
]
}
*/
const putMigrateMulParagraph: (
knowledge_id: string,

View File

@ -5,6 +5,7 @@
width="650"
:close-on-click-modal="false"
:close-on-press-escape="false"
@click.stop
>
<div class="content-height">
<el-form

View File

@ -4,15 +4,16 @@ export default {
syncDocument: 'Sync Document',
selected: 'Selected',
items: 'Items',
migrateDocument: 'Migrate to',
searchBar: {
placeholder: 'Search by document name'
placeholder: 'Search by document name',
},
setting: {
migration: 'Move',
cancelGenerateQuestion: 'Cancel Generating Questions',
cancelVectorization: 'Cancel Vectorization',
cancelGenerate: 'Cancel Generation',
export: 'Export to'
export: 'Export to',
},
tip: {
saveMessage: 'Current changes have not been saved. Confirm exit?',
@ -21,7 +22,7 @@ export default {
vectorizationSuccess: 'Successful',
nameMessage: 'Document name cannot be empty!',
importMessage: 'Successful',
migrationSuccess: 'Successful'
migrationSuccess: 'Successful',
},
upload: {
selectFile: 'Select File',
@ -34,71 +35,71 @@ export default {
errorMessage3: 'File cannot be empty',
errorMessage4: 'Up to 50 files can be uploaded at once',
template: 'Template',
download: 'Download'
download: 'Download',
},
fileType: {
txt: {
label: 'Text File',
tip1: '1. It is recommended to standardize the segment markers in the file before uploading.',
tip2: '2. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.'
tip2: '2. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.',
},
table: {
label: 'Table',
tip1: '1. Click to download the corresponding template and complete the information:',
tip2: '2. The first row must be column headers, and the column headers must be meaningful terms. Each record in the table will be treated as a segment.',
tip3: '3. Each sheet in the uploaded spreadsheet file will be treated as a document, with the sheet name as the document name.',
tip4: '4. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.'
tip4: '4. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.',
},
QA: {
label: 'QA Pairs',
tip1: '1. Click to download the corresponding template and complete the information:',
tip2: '2. Each sheet in the uploaded spreadsheet file will be treated as a document, with the sheet name as the document name.',
tip3: '3. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.'
}
tip3: '3. Up to 50 files can be uploaded at once, with each file not exceeding 100MB.',
},
},
setRules: {
title: {
setting: 'Set Segment Rules',
preview: 'Preview'
preview: 'Preview',
},
intelligent: {
label: 'Automatic Segmentation (Recommended)',
text: 'If you are unsure how to set segmentation rules, it is recommended to use automatic segmentation.'
text: 'If you are unsure how to set segmentation rules, it is recommended to use automatic segmentation.',
},
advanced: {
label: 'Advanced Segmentation',
text: 'Users can customize segmentation delimiters, segment length, and cleaning rules based on document standards.'
text: 'Users can customize segmentation delimiters, segment length, and cleaning rules based on document standards.',
},
patterns: {
label: 'Segment Delimiters',
tooltip:
'Recursively split according to the selected symbols in order. If the split result exceeds the segment length, it will be truncated to the segment length.',
placeholder: 'Please select'
placeholder: 'Please select',
},
limit: {
label: 'Segment Length'
label: 'Segment Length',
},
with_filter: {
label: 'Auto Clean',
text: 'Remove duplicate extra symbols, spaces, blank lines, and tab words.'
text: 'Remove duplicate extra symbols, spaces, blank lines, and tab words.',
},
checkedConnect: {
label: 'Add "Related Questions" section for question-based QA pairs during import.'
}
label: 'Add "Related Questions" section for question-based QA pairs during import.',
},
},
buttons: {
prev: 'Previous',
next: 'Next',
import: 'Start Import',
preview: 'Apply'
preview: 'Apply',
},
table: {
name: 'Document Name',
char_length: 'Character',
paragraph: 'Segment',
all: 'All',
updateTime: 'Update Time'
updateTime: 'Update Time',
},
fileStatus: {
label: 'File Status',
@ -109,12 +110,12 @@ export default {
GENERATE: 'Generating',
SYNC: 'Syncing',
REVOKE: 'Cancelling',
finish: 'Finish'
finish: 'Finish',
},
enableStatus: {
label: 'Status',
enable: 'Enabled',
close: 'Disabled'
close: 'Disabled',
},
sync: {
label: 'Sync',
@ -122,7 +123,7 @@ export default {
confirmMessage1:
'Syncing will delete existing data and retrieve new data. Please proceed with caution.',
confirmMessage2: 'Cannot sync, please set the document URL first.',
successMessage: 'Successful'
successMessage: 'Successful',
},
delete: {
confirmTitle1: 'Confirm batch deletion of',
@ -132,31 +133,31 @@ export default {
successMessage: 'Successful',
confirmTitle3: 'Confirm deleting document:',
confirmMessage1: 'Under this document',
confirmMessage2: 'All segments will be deleted, please operate with caution. '
confirmMessage2: 'All segments will be deleted, please operate with caution. ',
},
form: {
source_url: {
label: 'Document URL',
placeholder: 'Enter document URL, one per line. Incorrect URL will cause import failure.',
requiredMessage: 'Please enter a document URL'
requiredMessage: 'Please enter a document URL',
},
selector: {
label: 'Selector',
placeholder: 'Default is body, you can input .classname/#idname/tagname'
placeholder: 'Default is body, you can input .classname/#idname/tagname',
},
hit_handling_method: {
label: 'Retrieve-Respond',
tooltip: 'When user asks a question, handle matched segments according to the set method.'
tooltip: 'When user asks a question, handle matched segments according to the set method.',
},
similarity: {
label: 'Similarity Higher Than',
placeholder: 'Directly return segment content',
requiredMessage: 'Please enter similarity value'
}
requiredMessage: 'Please enter similarity value',
},
},
hitHandlingMethod: {
optimization: 'Model optimization',
directly_return: 'Respond directly'
directly_return: 'Respond directly',
},
generateQuestion: {
title: 'Generate Questions',
@ -167,12 +168,12 @@ export default {
tip4: 'The generation effect depends on the selected model and prompt. Users can adjust to achieve the best effect.',
prompt1:
'Content: {data}\n \n Please summarize the above and generate 5 questions based on the summary. \nAnswer requirements: \n - Please output only questions; \n - Please place each question in',
prompt2: 'tag.'
prompt2: 'tag.',
},
feishu: {
selectDocument: 'Select Document',
tip1: 'Only documents and tables are supported. Documents will be segmented based on titles, and tables will be converted to Markdown format before segmentation.',
tip2: 'The system does not store the original document. Before importing the document, it is recommended to standardize the document segmentation markers.',
allCheck: 'Select All'
}
allCheck: 'Select All',
},
}

View File

@ -4,6 +4,7 @@ export default {
syncDocument: '同步文档',
selected: '已选',
items: '项',
migrateDocument:'文档迁移到',
searchBar: {
placeholder: '按 文档名称 搜索'
},

View File

@ -4,8 +4,9 @@ export default {
syncDocument: '同步文檔',
selected: '已選',
items: '項',
migrateDocument: '文檔遷移到',
searchBar: {
placeholder: '按 文檔名稱 搜索'
placeholder: '按 文檔名稱 搜索',
},
setting: {
migration: '遷移',
@ -21,7 +22,7 @@ export default {
vectorizationSuccess: '批量向量化成功',
nameMessage: '文件名稱不能为空!',
importMessage: '導入成功',
migrationSuccess: '遷移成功'
migrationSuccess: '遷移成功',
},
upload: {
selectFile: '選擇文件',
@ -34,70 +35,70 @@ export default {
errorMessage3: '文件不能为空',
errorMessage4: '每次最多上傳50個文件',
template: '模板',
download: '下載'
download: '下載',
},
fileType: {
txt: {
label: '文本文件',
tip1: '1、文件上傳前建議規範文件的分段標識',
tip2: '2、每次最多上傳 50 個文件,每個文件不超过 100MB'
tip2: '2、每次最多上傳 50 個文件,每個文件不超过 100MB',
},
table: {
label: '表格',
tip1: '1、點擊下載對應模板並完善信息',
tip2: '2、第一行必須是列標題且列標題必須是有意義的術語表中每條記錄將作為一個分段',
tip3: '3、上傳的表格文件中每個 sheet 會作為一個文檔sheet 名稱為文檔名稱',
tip4: '4、每次最多上傳 50 個文件,每個文件不超过 100MB'
tip4: '4、每次最多上傳 50 個文件,每個文件不超过 100MB',
},
QA: {
label: 'QA 問答對',
tip1: '1、點擊下載對應模板並完善信息',
tip2: '2、上傳的表格文件中每個 sheet 會作為一個文檔sheet 名稱為文檔名稱',
tip3: '3、每次最多上傳 50 個文件,每個文件不超过 100MB'
}
tip3: '3、每次最多上傳 50 個文件,每個文件不超过 100MB',
},
},
setRules: {
title: {
setting: '設置分段規則',
preview: '分段預覽'
preview: '分段預覽',
},
intelligent: {
label: '智能分段(推薦)',
text: '不了解如何設置分段規則推薦使用智能分段'
text: '不了解如何設置分段規則推薦使用智能分段',
},
advanced: {
label: '高級分段',
text: '用戶可根據文檔規範自行設置分段標識符、分段長度以及清洗規則'
text: '用戶可根據文檔規範自行設置分段標識符、分段長度以及清洗規則',
},
patterns: {
label: '分段標識',
tooltip: '按照所選符號先後順序做遞歸分割,分割結果超出分段長度將截取至分段長度。',
placeholder: '請選擇'
placeholder: '請選擇',
},
limit: {
label: '分段長度'
label: '分段長度',
},
with_filter: {
label: '自動清洗',
text: '去掉重複多餘符號空格、空行、制表符'
text: '去掉重複多餘符號空格、空行、制表符',
},
checkedConnect: {
label: '導入時添加分段標題為關聯問題(適用於標題為問題的問答對)'
}
label: '導入時添加分段標題為關聯問題(適用於標題為問題的問答對)',
},
},
buttons: {
prev: '上一步',
next: '下一步',
import: '開始導入',
preview: '生成預覽'
preview: '生成預覽',
},
table: {
name: '文件名稱',
char_length: '字符數',
paragraph: '分段',
all: '全部',
updateTime: '更新時間'
updateTime: '更新時間',
},
fileStatus: {
label: '文件狀態',
@ -108,19 +109,19 @@ export default {
GENERATE: '生成中',
SYNC: '同步中',
REVOKE: '取消中',
finish: '完圓'
finish: '完圓',
},
enableStatus: {
label: '啟用狀態',
enable: '開啟',
close: '關閉'
close: '關閉',
},
sync: {
label: '同步',
confirmTitle: '確認同步文檔?',
confirmMessage1: '同步將刪除已有數據重新獲取新數據,請謹慎操作。',
confirmMessage2: '無法同步,請先去設置文檔 URL地址',
successMessage: '同步文檔成功'
successMessage: '同步文檔成功',
},
delete: {
confirmTitle1: '是否批量刪除',
@ -129,31 +130,31 @@ export default {
successMessage: '批量刪除成功',
confirmTitle3: '是否刪除文檔:',
confirmMessage1: '此文檔下的',
confirmMessage2: '個分段都會被刪除,請謹慎操作。'
confirmMessage2: '個分段都會被刪除,請謹慎操作。',
},
form: {
source_url: {
label: '文檔地址',
placeholder: '請輸入文檔地址,一行一個,地址不正確文檔會導入失敗。',
requiredMessage: '請輸入文檔地址'
requiredMessage: '請輸入文檔地址',
},
selector: {
label: '選擇器',
placeholder: '默認為 body可輸入 .classname/#idname/tagname'
placeholder: '默認為 body可輸入 .classname/#idname/tagname',
},
hit_handling_method: {
label: '命中處理方式',
tooltip: '用戶提問時,命中文檔下的分段時按照設置的方式進行處理。'
tooltip: '用戶提問時,命中文檔下的分段時按照設置的方式進行處理。',
},
similarity: {
label: '相似度高于',
placeholder: '直接返回分段内容',
requiredMessage: '请输入相似度'
requiredMessage: '请输入相似度',
},
},
hitHandlingMethod: {
optimization: '模型優化',
directly_return: '直接回答'
directly_return: '直接回答',
},
generateQuestion: {
title: '生成問題',
@ -169,6 +170,6 @@ export default {
selectDocument: '選擇文檔',
tip1: '僅支持文檔和表格類型文檔會根據標題分段表格會轉為Markdown格式後再分段。',
tip2: '系統不存儲原始文檔,導入文檔前,建議規範文檔的分段標識。',
allCheck: '全選'
}
allCheck: '全選',
},
}

View File

@ -5,10 +5,10 @@ import { type Ref } from 'vue'
const useDocumentStore = defineStore('document', {
state: () => ({}),
actions: {
async asyncGetAllDocument(id: string, loading?: Ref<boolean>) {
async asyncGetKnowledgeDocument(id: string, loading?: Ref<boolean>) {
return new Promise((resolve, reject) => {
documentApi
.getAllDocument(id, loading)
.getDocumentList(id, loading)
.then((res) => {
resolve(res)
})

View File

@ -1,29 +1,22 @@
<template>
<el-dialog
:title="$t('views.chatLog.selectKnowledge')"
:title="`${$t('views.document.migrateDocument')}`"
v-model="dialogVisible"
width="600"
class="select-knowledge-dialog"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ '文档迁移到' }}</h4>
</template>
<el-form
ref="FormRef"
:model="form"
label-position="top"
require-asterisk-position="right"
>
<el-form ref="FormRef" :model="form" label-position="top" require-asterisk-position="right">
<el-form-item :label="$t('views.chatLog.selectKnowledge')" required>
<el-tree-select
v-model="form.selectKnowledge"
:props="defaultProps"
node-key="id"
:default-expanded-keys="['default']"
lazy
:load="loadTree"
:placeholder="$t('views.chatLog.selectKnowledgePlaceholder')"
:loading="loading"
>
<template #default="{ data }">
<div class="flex align-center">
@ -81,6 +74,9 @@ const loading = ref<boolean>(false)
const dialogVisible = ref<boolean>(false)
const knowledgeList = ref<any>([])
const documentList = ref<any>([])
const form = ref<any>({
selectKnowledge: '',
})
const defaultProps = {
children: 'children',
@ -91,9 +87,14 @@ const defaultProps = {
},
}
const form = ref<any>({
selectKnowledge: '',
})
const loadTree = (node: any, resolve: any) => {
console.log(node)
if (node.isLeaf) return resolve([])
const folder_id = node.level === 0 ? '' : node.data.id
knowledge.asyncGetFolderKnowledge(folder_id, loading).then((res: any) => {
resolve(res.data)
})
}
watch(dialogVisible, (bool) => {
if (!bool) {
@ -108,14 +109,6 @@ const open = (list: any) => {
dialogVisible.value = true
}
const loadTree = (node: any, resolve: any) => {
console.log(node)
if (node.isLeaf) return resolve([])
const folder_id = node.level === 0 ? '' : node.data.id
knowledge.asyncGetFolderKnowledge(folder_id, loading).then((res: any) => {
resolve(res.data)
})
}
const submitHandle = () => {
documentApi
.putMigrateMulDocument(id, form.value.selectKnowledge, documentList.value, loading)

View File

@ -7,7 +7,7 @@
@click.stop="editParagraph(data)"
>
<h2 class="mb-16">{{ data.title || '-' }}</h2>
<div v-show="show" class="mk-sticky">
<div v-show="show" class="mk-sticky" v-if="!disabled">
<el-card
class="paragraph-box-operation mt-8 mr-8"
shadow="always"
@ -79,7 +79,6 @@ import GenerateRelatedDialog from '@/components/generate-related-dialog/index.vu
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
import SelectDocumentDialog from '@/views/paragraph/component/SelectDocumentDialog.vue'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { elPaginationKey } from 'element-plus'
const { paragraph } = useStore()
@ -89,9 +88,10 @@ const {
} = route as any
const props = defineProps<{
data: any
disabled?: boolean
}>()
const emit = defineEmits(['changeState', 'deleteParagraph'])
const emit = defineEmits(['changeState', 'deleteParagraph', 'refresh', 'refreshMigrateParagraph'])
const loading = ref(false)
const changeStateloading = ref(false)
const show = ref(false)
@ -127,13 +127,6 @@ function openGenerateDialog(row: any) {
GenerateRelatedDialogRef.value.open([], 'paragraph', row.id)
}
}
function openSelectDocumentDialog(row?: any) {
// if (row) {
// multipleSelection.value = [row.id]
// }
// SelectDocumentDialogRef.value.open(multipleSelection.value)
}
function deleteParagraph(row: any) {
MsgConfirm(
`${t('views.paragraph.delete.confirmTitle')} ${row.title || '-'} ?`,
@ -151,17 +144,28 @@ function deleteParagraph(row: any) {
})
.catch(() => {})
}
const SelectDocumentDialogRef = ref()
const ParagraphDialogRef = ref()
const title = ref('')
function editParagraph(row: any) {
title.value = t('views.paragraph.paragraphDetail')
ParagraphDialogRef.value.open(row)
if (!props.disabled) {
title.value = t('views.paragraph.paragraphDetail')
ParagraphDialogRef.value.open(row)
}
}
function refresh() {}
const SelectDocumentDialogRef = ref()
function openSelectDocumentDialog(row?: any) {
SelectDocumentDialogRef.value.open([row.id])
}
function refreshMigrateParagraph() {}
function refresh(data?: any) {
emit('refresh', data)
}
function refreshMigrateParagraph() {
emit('refreshMigrateParagraph', props.data)
}
</script>
<style lang="scss" scoped>
.paragraph-box {

View File

@ -12,7 +12,7 @@
<el-col :span="18">
<el-scrollbar height="500" wrap-class="paragraph-scrollbar">
<div class="p-24" style="padding-bottom: 8px">
<div style="position: absolute; right: 20px; top: 20px; ">
<div style="position: absolute; right: 20px; top: 20px">
<el-button text @click="isEdit = true" v-if="problemId && !isEdit">
<el-icon><EditPen /></el-icon>
</el-button>
@ -22,9 +22,9 @@
</div>
</el-scrollbar>
<div class="text-right p-24 pt-0" v-if="problemId && isEdit">
<el-button @click.prevent="cancelEdit"> {{$t('common.cancel')}} </el-button>
<el-button @click.prevent="cancelEdit"> {{ $t('common.cancel') }} </el-button>
<el-button type="primary" :disabled="loading" @click="handleDebounceClick">
{{$t('common.save')}}
{{ $t('common.save') }}
</el-button>
</div>
</el-col>
@ -40,9 +40,9 @@
</el-row>
<template #footer v-if="!problemId">
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> {{$t('common.cancel')}} </el-button>
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
<el-button :disabled="loading" type="primary" @click="handleDebounceClick">
{{$t('common.submit')}}
{{ $t('common.submit') }}
</el-button>
</span>
</template>
@ -58,14 +58,14 @@ import paragraphApi from '@/api/knowledge/paragraph'
import useStore from '@/stores'
const props = defineProps({
title: String
title: String,
})
const { paragraph } = useStore()
const route = useRoute()
const {
params: { id, documentId }
params: { id, documentId },
} = route as any
const emit = defineEmits(['refresh'])
@ -122,7 +122,7 @@ const submitHandle = async () => {
documentId || document_id.value,
problemId.value,
paragraphFormRef.value?.form,
loading
loading,
)
.then((res: any) => {
isEdit.value = false
@ -133,7 +133,7 @@ const submitHandle = async () => {
ProblemRef.value.problemList.length > 0
? {
problem_list: ProblemRef.value.problemList,
...paragraphFormRef.value?.form
...paragraphFormRef.value?.form,
}
: paragraphFormRef.value?.form
paragraphApi.postParagraph(id, documentId, obj, loading).then((res) => {

View File

@ -5,6 +5,7 @@
width="500"
:close-on-click-modal="false"
:close-on-press-escape="false"
@click.stop
>
<el-form
ref="formRef"
@ -14,22 +15,37 @@
:rules="rules"
@submit.prevent
>
<el-form-item :label="$t('views.chatLog.selectKnowledge')" prop="dataset_id">
<el-select
v-model="form.dataset_id"
filterable
<el-form-item :label="$t('views.chatLog.selectKnowledge')" prop="knowledge_id">
<el-tree-select
v-model="form.knowledge_id"
:props="defaultProps"
node-key="id"
lazy
:load="loadTree"
:placeholder="$t('views.chatLog.selectKnowledgePlaceholder')"
@change="changeKnowledge"
:loading="optionLoading"
@change="changeDataset"
>
<el-option v-for="item in datasetList" :key="item.id" :label="item.name" :value="item.id">
<span class="flex align-center">
<KnowledgeIcon v-if="!item.dataset_id" :type="item.type" />
<template #default="{ data }">
<div class="flex align-center">
<KnowledgeIcon
class="mr-12"
:size="20"
v-if="data.resource_type !== 'folder'"
:type="data.type"
/>
<el-avatar v-else class="mr-12" shape="square" :size="20" style="background: none">
<img
src="@/assets/knowledge/icon_file-folder_colorful.svg"
style="width: 100%"
alt=""
/>
</el-avatar>
{{ item.name }}
</span>
</el-option>
</el-select>
{{ data.name }}
</div>
</template>
</el-tree-select>
</el-form-item>
<el-form-item :label="$t('views.chatLog.saveToDocument')" prop="document_id">
<el-select
@ -70,7 +86,7 @@ const { knowledge, document } = useStore()
const route = useRoute()
const {
params: { id, documentId },
params: { id, documentId }, // idknowledgeID
} = route as any
const emit = defineEmits(['refresh'])
@ -80,18 +96,19 @@ const dialogVisible = ref<boolean>(false)
const loading = ref(false)
const form = ref<any>({
dataset_id: '',
knowledge_id: '',
document_id: '',
})
const rules = reactive<FormRules>({
dataset_id: [
knowledge_id: [
{ required: true, message: t('views.chatLog.selectKnowledgePlaceholder'), trigger: 'change' },
],
document_id: [{ required: true, message: t('views.chatLog.documentPlaceholder'), trigger: 'change' }],
document_id: [
{ required: true, message: t('views.chatLog.documentPlaceholder'), trigger: 'change' },
],
})
const datasetList = ref<any[]>([])
const documentList = ref<any[]>([])
const optionLoading = ref(false)
const paragraphList = ref<string[]>([])
@ -99,36 +116,46 @@ const paragraphList = ref<string[]>([])
watch(dialogVisible, (bool) => {
if (!bool) {
form.value = {
dataset_id: '',
knowledge_id: '',
document_id: '',
}
datasetList.value = []
documentList.value = []
paragraphList.value = []
formRef.value?.clearValidate()
}
})
function changeDataset(id: string) {
const defaultProps = {
children: 'children',
label: 'name',
isLeaf: (data: any) => data.resource_type && data.resource_type !== 'folder',
disabled: (data: any, node: any) => {
return data.id === id
},
}
const loadTree = (node: any, resolve: any) => {
console.log(node)
if (node.isLeaf) return resolve([])
const folder_id = node.level === 0 ? '' : node.data.id
knowledge.asyncGetFolderKnowledge(folder_id, optionLoading).then((res: any) => {
resolve(res.data)
})
}
function changeKnowledge(id: string) {
form.value.document_id = ''
getDocument(id)
}
function getDocument(id: string) {
document.asyncGetAllDocument(id, loading).then((res: any) => {
document.asyncGetKnowledgeDocument(id, optionLoading).then((res: any) => {
documentList.value = res.data?.filter((v: any) => v.id !== documentId)
})
}
function getDataset() {
knowledge.asyncGetFolderKnowledge(loading).then((res: any) => {
datasetList.value = res.data
})
}
const open = (list: any) => {
paragraphList.value = list
getDataset()
formRef.value?.clearValidate()
dialogVisible.value = true
}
@ -136,13 +163,16 @@ const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
const obj = {
id_list: paragraphList.value,
}
paragraphApi
.putMigrateMulParagraph(
id,
documentId,
form.value.dataset_id,
form.value.knowledge_id,
form.value.document_id,
paragraphList.value,
obj,
loading,
)
.then(() => {

View File

@ -83,27 +83,34 @@
ghostClass="ghost"
>
<template v-for="(item, index) in paragraphDetail" :key="item.id">
<div :id="`m${item.id}`" style="display: flex; margin-bottom: 16px">
<div :id="`m${item.id}`" class="flex mb-16">
<!-- 批量操作 -->
<div class="paragraph-card flex" v-if="isBatch === true">
<div class="paragraph-card flex w-full" v-if="isBatch === true">
<el-checkbox :value="item.id" />
<ParagraphCard :data="item" class="mb-8 w-full" />
<ParagraphCard
:data="item"
class="mb-8 w-full"
@refresh="refresh"
@refreshMigrateParagraph="refreshMigrateParagraph"
:disabled="true"
/>
</div>
<!-- 非批量操作 -->
<div class="handle paragraph-card flex" :id="item.id" v-else>
<div class="handle paragraph-card flex w-full" :id="item.id" v-else>
<img
src="@/assets/sort.svg"
alt=""
height="15"
class="handle-img mr-8 mt-24 cursor"
/>
<ParagraphCard
:data="item"
class="mb-8 w-full"
@changeState="changeState"
@deleteParagraph="deleteParagraph"
/>
</div>
<ParagraphCard
:data="item"
class="mb-8 w-full"
@changeState="changeState"
@deleteParagraph="deleteParagraph"
/>
</div>
</template>
</VueDraggable>
@ -151,6 +158,7 @@ import { VueDraggable } from 'vue-draggable-plus'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import useStore from '@/stores'
import { t } from '@/locales'
import disable$ from 'dingtalk-jsapi/api/ui/pullToRefresh/disable'
const { paragraph } = useStore()
const route = useRoute()
const {
@ -192,7 +200,10 @@ function changeState(id: string) {
paragraphDetail.value[index].is_active = !paragraphDetail.value[index].is_active
}
function refreshMigrateParagraph() {
function refreshMigrateParagraph(data: any) {
if (data) {
multipleSelection.value = data
}
paragraphDetail.value = paragraphDetail.value.filter(
(v) => !multipleSelection.value.includes(v.id),
)